From 6e0207ca5ef458cd4a45c9da2a3dcee093647d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Mon, 24 Jan 2022 19:47:07 +0100 Subject: [PATCH 01/16] Fix spelling in app and main --- src/app/app.cpp | 6 +++--- src/app/app.h | 4 ++-- src/app/main.cpp | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 2d253aa3..51bdea01 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -593,7 +593,7 @@ bool CApplication::Create() h = atoi(hs.c_str()); } - // Why not just set m_deviceConfig.size to w,h? Because this way if the resolution is no longer supported (e.g. changimg monitor) defaults will be used instead + // Why not just set m_deviceConfig.size to w,h? Because this way if the resolution is no longer supported (e.g. changing monitor) defaults will be used instead for (auto it = modes.begin(); it != modes.end(); ++it) { if (it->x == w && it->y == h) @@ -1866,7 +1866,7 @@ void CApplication::SetLanguage(Language language) } else { - GetLogger()->Warn("Enviromnent locale ('%s') is not supported, setting default language\n", envLang); + GetLogger()->Warn("Environment locale ('%s') is not supported, setting default language\n", envLang); m_language = LANGUAGE_ENGLISH; } } @@ -1935,7 +1935,7 @@ void CApplication::SetLanguage(Language language) } catch (...) { - GetLogger()->Warn("Failed to update locale, possibly incorect system configuration. Will fallback to classic locale.\n"); + GetLogger()->Warn("Failed to update locale, possibly incorrect system configuration. Will fallback to classic locale.\n"); try { std::locale::global(std::locale::classic()); diff --git a/src/app/app.h b/src/app/app.h index 45e41464..234a13f7 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -218,7 +218,7 @@ public: //! Returns the relative time since last update [seconds] float GetRelTime() const; - //! Returns the exact realative time since last update [nanoseconds] + //! Returns the exact relative time since last update [nanoseconds] long long GetExactRelTime() const; //! Returns the exact relative time since last update disregarding speed setting [nanoseconds] @@ -411,7 +411,7 @@ protected: //! Application language Language m_language; - //! Screen resoultion overriden by commandline + //! Screen resolution overriden by commandline bool m_resolutionOverride; //! Headles mode diff --git a/src/app/main.cpp b/src/app/main.cpp index b20a6b2f..528c7f74 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -60,14 +60,14 @@ object-oriented language, CBOT, which can be used to program the robots availabl The original version of the game was developed by [Epsitec](http://www.epsitec.ch/) and released in 2001. Later, in 2005 another version named Ceebot was released. In March 2012, through attempts -by Polish Colobot fans, Epsitec agreeed to release the source code of the game on GPLv3 license. +by Polish Colobot fans, Epsitec agreed to release the source code of the game on GPLv3 license. The license was given specifically to our community, TerranovaTeam, part of International Colobot Community (ICC) (previously known as Polish Portal of Colobot (PPC); Polish: Polski Portal Colobota) with our website at http://colobot.info/. \section Intro Introduction -The source code released by Epitec was sparsely documented. This documentation, written from scratch, +The source code released by Epsitec was sparsely documented. This documentation, written from scratch, will aim to describe the various components of the code. Currently, the only documented classes are the ones written from scratch or the old ones rewritten @@ -92,7 +92,7 @@ The current layout is the following: - src/graphics/opengl - concrete implementation of CDevice class in OpenGL: CGLDevice - src/graphics/d3d - in (far) future - perhaps a newer implementation in DirectX (9? 10?) - src/math - mathematical structures and functions - - src/object - non-grphical game object logic, that is robots, buildings, etc. + - src/object - non-graphical game object logic, that is robots, buildings, etc. - src/level - main part of non-graphical game engine, that is loading levels etc. - src/level/parser - parser for loading/saving level files from format known as scene files - src/ui - 2D user interface (menu, buttons, check boxes, etc.) From 19ae57c9ad77240078dbe74222153463e6663be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Mon, 24 Jan 2022 19:51:00 +0100 Subject: [PATCH 02/16] Fix spelling of appearance --- src/CMakeLists.txt | 6 +- src/level/player_profile.cpp | 62 ++++---- src/level/player_profile.h | 18 +-- src/level/robotmain.cpp | 30 ++-- src/level/robotmain.h | 4 +- src/ui/mainui.cpp | 10 +- src/ui/mainui.h | 4 +- ...en_apperance.cpp => screen_appearance.cpp} | 136 +++++++++--------- ...screen_apperance.h => screen_appearance.h} | 8 +- src/ui/screen/screen_player_select.cpp | 2 +- 10 files changed, 140 insertions(+), 140 deletions(-) rename src/ui/screen/{screen_apperance.cpp => screen_appearance.cpp} (83%) rename src/ui/screen/{screen_apperance.h => screen_appearance.h} (86%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 105bce70..29856404 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -433,8 +433,8 @@ add_library(colobotbase STATIC ui/particles_generator.h ui/screen/screen.cpp ui/screen/screen.h - ui/screen/screen_apperance.cpp - ui/screen/screen_apperance.h + ui/screen/screen_appearance.cpp + ui/screen/screen_appearance.h ui/screen/screen_io.cpp ui/screen/screen_io.h ui/screen/screen_io_read.cpp @@ -521,7 +521,7 @@ if(PLATFORM_WINDOWS) # This should be treated as a temporary workaround if(USE_STATIC_RUNTIME) # Since we're using static runtime, assume every external library is static too find_package(Intl REQUIRED) - + find_library(BZ2_LIBRARY NAMES bz2) find_library(JPEG_LIBRARY NAMES jpeg) find_library(TIFF_LIBRARY NAMES tiff) diff --git a/src/level/player_profile.cpp b/src/level/player_profile.cpp index a88d052f..17a92567 100644 --- a/src/level/player_profile.cpp +++ b/src/level/player_profile.cpp @@ -33,7 +33,7 @@ #include "level/parser/parser.h" -void PlayerApperance::DefPerso() +void PlayerAppearance::DefPerso() { this->colorCombi.r = 206.0f/256.0f; this->colorCombi.g = 206.0f/256.0f; @@ -76,7 +76,7 @@ void PlayerApperance::DefPerso() this->colorBand.a = 0.0f; } -void PlayerApperance::DefHairColor() +void PlayerAppearance::DefHairColor() { if (this->face == 0) // normal ? { @@ -122,7 +122,7 @@ CPlayerProfile::CPlayerProfile(std::string playerName) m_levelInfoLoaded[static_cast(i)] = false; } - LoadApperance(); + LoadAppearance(); } CPlayerProfile::~CPlayerProfile() @@ -392,17 +392,17 @@ void CPlayerProfile::SaveFreeGameUnlock() file.close(); } -// APPERANCE +// APPEARANCE -PlayerApperance& CPlayerProfile::GetApperance() +PlayerAppearance& CPlayerProfile::GetAppearance() { - return m_apperance; + return m_appearance; } -void CPlayerProfile::LoadApperance() +void CPlayerProfile::LoadAppearance() { - m_apperance.face = 0; - m_apperance.DefPerso(); + m_appearance.face = 0; + m_appearance.DefPerso(); std::string filename = GetSaveFile("face.gam"); if (!CResourceManager::Exists(filename)) @@ -410,48 +410,48 @@ void CPlayerProfile::LoadApperance() try { - CLevelParser apperanceParser(filename); - apperanceParser.Load(); + CLevelParser appearanceParser(filename); + appearanceParser.Load(); CLevelParserLine* line; - line = apperanceParser.Get("Head"); - m_apperance.face = line->GetParam("face")->AsInt(); - m_apperance.glasses = line->GetParam("glasses")->AsInt(); - m_apperance.colorHair = line->GetParam("hair")->AsColor(); + line = appearanceParser.Get("Head"); + m_appearance.face = line->GetParam("face")->AsInt(); + m_appearance.glasses = line->GetParam("glasses")->AsInt(); + m_appearance.colorHair = line->GetParam("hair")->AsColor(); - line = apperanceParser.Get("Body"); - m_apperance.colorCombi = line->GetParam("combi")->AsColor(); - m_apperance.colorBand = line->GetParam("band")->AsColor(); + line = appearanceParser.Get("Body"); + m_appearance.colorCombi = line->GetParam("combi")->AsColor(); + m_appearance.colorBand = line->GetParam("band")->AsColor(); } catch (CLevelParserException& e) { - GetLogger()->Error("Unable to read personalized player apperance: %s\n", e.what()); + GetLogger()->Error("Unable to read personalized player appearance: %s\n", e.what()); } } -void CPlayerProfile::SaveApperance() +void CPlayerProfile::SaveAppearance() { try { - CLevelParser apperanceParser(GetSaveFile("face.gam")); + CLevelParser appearanceParser(GetSaveFile("face.gam")); CLevelParserLineUPtr line; line = MakeUnique("Head"); - line->AddParam("face", MakeUnique(m_apperance.face)); - line->AddParam("glasses", MakeUnique(m_apperance.glasses)); - line->AddParam("hair", MakeUnique(m_apperance.colorHair)); - apperanceParser.AddLine(std::move(line)); + line->AddParam("face", MakeUnique(m_appearance.face)); + line->AddParam("glasses", MakeUnique(m_appearance.glasses)); + line->AddParam("hair", MakeUnique(m_appearance.colorHair)); + appearanceParser.AddLine(std::move(line)); line = MakeUnique("Body"); - line->AddParam("combi", MakeUnique(m_apperance.colorCombi)); - line->AddParam("band", MakeUnique(m_apperance.colorBand)); - apperanceParser.AddLine(std::move(line)); + line->AddParam("combi", MakeUnique(m_appearance.colorCombi)); + line->AddParam("band", MakeUnique(m_appearance.colorBand)); + appearanceParser.AddLine(std::move(line)); - apperanceParser.Save(); + appearanceParser.Save(); } catch (CLevelParserException& e) { - GetLogger()->Error("Unable to write personalized player apperance: %s\n", e.what()); + GetLogger()->Error("Unable to write personalized player appearance: %s\n", e.what()); } } @@ -553,7 +553,7 @@ void CPlayerProfile::LoadScene(std::string dir) } else { - // Backwards combatibility + // Backwards compatibility chap = rank/100; rank = rank%100; } diff --git a/src/level/player_profile.h b/src/level/player_profile.h index 16ba0abd..d9ece2cf 100644 --- a/src/level/player_profile.h +++ b/src/level/player_profile.h @@ -33,7 +33,7 @@ struct LevelInfo bool bPassed = false; }; -struct PlayerApperance +struct PlayerAppearance { int face = 0; // face int glasses = 0; // glasses @@ -108,12 +108,12 @@ public: //! Saves unlocked DoneResearch for free game void SetFreeGameResearchUnlock(int freeResearch); - //! Returns a reference to PlayerApperance structure - PlayerApperance& GetApperance(); - //! Loads PlayerApperance structure - void LoadApperance(); - //! Saves PlayerApperance structure - void SaveApperance(); + //! Returns a reference to PlayerAppearance structure + PlayerAppearance& GetAppearance(); + //! Loads PlayerAppearance structure + void LoadAppearance(); + //! Saves PlayerAppearance structure + void SaveAppearance(); //! Returns true if player has at least one saved scene bool HasAnySavedScene(); @@ -157,6 +157,6 @@ protected: //! Researches unlocked for free game int m_freegameResearch; - //! Player apperance - PlayerApperance m_apperance; + //! Player appearance + PlayerAppearance m_appearance; }; diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 5f1b1599..0f48885a 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -317,7 +317,7 @@ std::string PhaseToString(Phase phase) if (phase == PHASE_WELCOME2) return "PHASE_WELCOME2"; if (phase == PHASE_WELCOME3) return "PHASE_WELCOME3"; if (phase == PHASE_PLAYER_SELECT) return "PHASE_PLAYER_SELECT"; - if (phase == PHASE_APPERANCE) return "PHASE_APPERANCE"; + if (phase == PHASE_APPEARANCE) return "PHASE_APPEARANCE"; if (phase == PHASE_MAIN_MENU) return "PHASE_MAIN_MENU"; if (phase == PHASE_LEVEL_LIST) return "PHASE_LEVEL_LIST"; if (phase == PHASE_MOD_LIST) return "PHASE_MOD_LIST"; @@ -352,14 +352,14 @@ bool IsPhaseWithWorld(Phase phase) if (phase == PHASE_SIMUL ) return true; if (phase == PHASE_WIN ) return true; if (phase == PHASE_LOST ) return true; - if (phase == PHASE_APPERANCE) return true; + if (phase == PHASE_APPEARANCE) return true; if (IsInSimulationConfigPhase(phase)) return true; return false; } bool IsMainMenuPhase(Phase phase) { - if (phase == PHASE_APPERANCE) return true; + if (phase == PHASE_APPEARANCE) return true; return !IsPhaseWithWorld(phase); } @@ -531,7 +531,7 @@ void CRobotMain::ChangePhase(Phase phase) float sx = (32.0f+2.0f)/640.0f; float sy = (32.0f+2.0f)/480.0f; - if (m_phase != PHASE_APPERANCE) + if (m_phase != PHASE_APPEARANCE) { m_engine->SetDrawWorld(true); m_engine->SetDrawFront(false); @@ -1162,7 +1162,7 @@ bool CRobotMain::ProcessEvent(Event &event) return false; } - if (m_phase == PHASE_APPERANCE) + if (m_phase == PHASE_APPEARANCE) EventObject(event); if (m_phase == PHASE_WIN || @@ -2080,7 +2080,7 @@ void CRobotMain::HiliteClear() //! Highlights the object with the mouse hovers over void CRobotMain::HiliteObject(Math::Point pos) { - if (m_fixScene && m_phase != PHASE_APPERANCE) return; + if (m_fixScene && m_phase != PHASE_APPEARANCE) return; if (m_movieLock) return; if (m_movie->IsExist()) return; if (m_app->GetMouseMode() == MOUSE_NONE) return; @@ -2146,7 +2146,7 @@ void CRobotMain::HiliteObject(Math::Point pos) //! Highlights the object with the mouse hovers over void CRobotMain::HiliteFrame(float rTime) { - if (m_fixScene && m_phase != PHASE_APPERANCE) return; + if (m_fixScene && m_phase != PHASE_APPEARANCE) return; if (m_movieLock) return; if (m_movie->IsExist()) return; @@ -2429,7 +2429,7 @@ bool CRobotMain::EventFrame(const Event &event) if (m_engine->GetFog()) m_camera->SetOverBaseColor(m_particle->GetFogColor(m_engine->GetEyePt())); } - if (m_phase == PHASE_APPERANCE || + if (m_phase == PHASE_APPEARANCE || m_phase == PHASE_WIN || m_phase == PHASE_LOST) { @@ -2702,7 +2702,7 @@ void CRobotMain::ScenePerso() } catch (const std::runtime_error& e) { - LevelLoadingError("An error occurred while trying to load apperance scene", e, PHASE_PLAYER_SELECT); + LevelLoadingError("An error occurred while trying to load appearance scene", e, PHASE_PLAYER_SELECT); } m_engine->SetDrawWorld(false); // does not draw anything on the interface @@ -3924,7 +3924,7 @@ void CRobotMain::ChangeColor() m_phase != PHASE_MOD_LIST && m_phase != PHASE_WIN && m_phase != PHASE_LOST && - m_phase != PHASE_APPERANCE ) return; + m_phase != PHASE_APPEARANCE ) return; // Player texture @@ -3939,11 +3939,11 @@ void CRobotMain::ChangeColor() colorRef1.r = 206.0f/256.0f; colorRef1.g = 206.0f/256.0f; colorRef1.b = 204.0f/256.0f; // ~white - colorNew1 = m_playerProfile->GetApperance().colorCombi; + colorNew1 = m_playerProfile->GetAppearance().colorCombi; colorRef2.r = 255.0f/256.0f; colorRef2.g = 132.0f/256.0f; colorRef2.b = 1.0f/256.0f; // orange - colorNew2 = m_playerProfile->GetApperance().colorBand; + colorNew2 = m_playerProfile->GetAppearance().colorBand; Math::Point exclu[6]; exclu[0] = Math::Point(192.0f/256.0f, 0.0f/256.0f); @@ -3985,7 +3985,7 @@ void CRobotMain::ChangeColor() colorRef1.b = 0.0f/256.0f; // yellow tolerance = 0.20f; } - colorNew1 = m_playerProfile->GetApperance().colorHair; + colorNew1 = m_playerProfile->GetAppearance().colorHair; colorRef2.r = 0.0f; colorRef2.g = 0.0f; colorRef2.b = 0.0f; @@ -5425,13 +5425,13 @@ MissionType CRobotMain::GetMissionType() //! Returns the representation to use for the player int CRobotMain::GetGamerFace() { - return m_playerProfile->GetApperance().face; + return m_playerProfile->GetAppearance().face; } //! Returns the representation to use for the player int CRobotMain::GetGamerGlasses() { - return m_playerProfile->GetApperance().glasses; + return m_playerProfile->GetAppearance().glasses; } //! Returns the mode with just the head diff --git a/src/level/robotmain.h b/src/level/robotmain.h index ceddcb78..bd72e942 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -52,7 +52,7 @@ enum Phase PHASE_WELCOME2, PHASE_WELCOME3, PHASE_PLAYER_SELECT, - PHASE_APPERANCE, + PHASE_APPEARANCE, PHASE_MAIN_MENU, PHASE_LEVEL_LIST, PHASE_MOD_LIST, @@ -198,7 +198,7 @@ public: Phase GetPhase(); //@} - //! Load the scene for apperance customization + //! Load the scene for appearance customization void ScenePerso(); void SetMovieLock(bool lock); diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp index d2a1fbe8..13e61893 100644 --- a/src/ui/mainui.cpp +++ b/src/ui/mainui.cpp @@ -43,7 +43,7 @@ #include "ui/controls/window.h" #include "ui/screen/screen.h" -#include "ui/screen/screen_apperance.h" +#include "ui/screen/screen_appearance.h" #include "ui/screen/screen_io_read.h" #include "ui/screen/screen_io_write.h" #include "ui/screen/screen_level_list.h" @@ -76,7 +76,7 @@ CMainUserInterface::CMainUserInterface() m_dialog = MakeUnique(); - m_screenAppearance = MakeUnique(); + m_screenAppearance = MakeUnique(); m_screenLevelList = MakeUnique(m_dialog.get()); m_screenIORead = MakeUnique(m_screenLevelList.get()); m_screenIOWrite = MakeUnique(m_screenLevelList.get()); @@ -173,7 +173,7 @@ void CMainUserInterface::ChangePhase(Phase phase) { m_currentScreen = m_screenPlayerSelect.get(); } - if (m_phase == PHASE_APPERANCE) + if (m_phase == PHASE_APPEARANCE) { m_currentScreen = m_screenAppearance.get(); } @@ -786,7 +786,7 @@ bool CMainUserInterface::GetPlusExplorer() bool CMainUserInterface::GetGamerOnlyHead() { - if (m_phase == PHASE_APPERANCE) + if (m_phase == PHASE_APPEARANCE) return m_screenAppearance->GetGamerOnlyHead(); return false; @@ -794,7 +794,7 @@ bool CMainUserInterface::GetGamerOnlyHead() float CMainUserInterface::GetPersoAngle() { - if (m_phase == PHASE_APPERANCE) + if (m_phase == PHASE_APPEARANCE) return m_screenAppearance->GetPersoAngle(); return 0.0f; diff --git a/src/ui/mainui.h b/src/ui/mainui.h index 6b57d975..73f592cc 100644 --- a/src/ui/mainui.h +++ b/src/ui/mainui.h @@ -44,7 +44,7 @@ class CInterface; class CMainDialog; class CScreen; -class CScreenApperance; +class CScreenAppearance; class CScreenIORead; class CScreenIOWrite; class CScreenLevelList; @@ -112,7 +112,7 @@ protected: std::unique_ptr m_dialog; CScreen* m_currentScreen; - std::unique_ptr m_screenAppearance; + std::unique_ptr m_screenAppearance; std::unique_ptr m_screenIORead; std::unique_ptr m_screenIOWrite; std::unique_ptr m_screenLevelList; diff --git a/src/ui/screen/screen_apperance.cpp b/src/ui/screen/screen_appearance.cpp similarity index 83% rename from src/ui/screen/screen_apperance.cpp rename to src/ui/screen/screen_appearance.cpp index 2df97bf6..3638c8fe 100644 --- a/src/ui/screen/screen_apperance.cpp +++ b/src/ui/screen/screen_appearance.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "ui/screen/screen_apperance.h" +#include "ui/screen/screen_appearance.h" #include "app/app.h" @@ -77,13 +77,13 @@ const int PERSO_COLOR[3*10*3] = 0, 0, 0, // }; -CScreenApperance::CScreenApperance() - : m_apperanceTab(0), - m_apperanceAngle(0.0f) +CScreenAppearance::CScreenAppearance() + : m_appearanceTab(0), + m_appearanceAngle(0.0f) { } -void CScreenApperance::CreateInterface() +void CScreenAppearance::CreateInterface() { CWindow* pw; CLabel* pl; @@ -305,17 +305,17 @@ void CScreenApperance::CreateInterface() pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PDEF); pb->SetState(STATE_SHADOW); - m_apperanceTab = 0; - m_apperanceAngle = -0.6f; - m_main->GetPlayerProfile()->LoadApperance(); + m_appearanceTab = 0; + m_appearanceAngle = -0.6f; + m_main->GetPlayerProfile()->LoadAppearance(); UpdatePerso(); m_main->ScenePerso(); CameraPerso(); } -bool CScreenApperance::EventProcess(const Event &event) +bool CScreenAppearance::EventProcess(const Event &event) { - PlayerApperance& apperance = m_main->GetPlayerProfile()->GetApperance(); + PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance(); switch( event.type ) { case EVENT_KEY_DOWN: @@ -338,13 +338,13 @@ bool CScreenApperance::EventProcess(const Event &event) break; case EVENT_INTERFACE_PHEAD: - m_apperanceTab = 0; + m_appearanceTab = 0; UpdatePerso(); m_main->ScenePerso(); CameraPerso(); break; case EVENT_INTERFACE_PBODY: - m_apperanceTab = 1; + m_appearanceTab = 1; UpdatePerso(); m_main->ScenePerso(); CameraPerso(); @@ -354,8 +354,8 @@ bool CScreenApperance::EventProcess(const Event &event) case EVENT_INTERFACE_PFACE2: case EVENT_INTERFACE_PFACE3: case EVENT_INTERFACE_PFACE4: - apperance.face = event.type-EVENT_INTERFACE_PFACE1; - apperance.DefHairColor(); + appearance.face = event.type-EVENT_INTERFACE_PFACE1; + appearance.DefHairColor(); UpdatePerso(); m_main->ScenePerso(); break; @@ -370,7 +370,7 @@ bool CScreenApperance::EventProcess(const Event &event) case EVENT_INTERFACE_PGLASS7: case EVENT_INTERFACE_PGLASS8: case EVENT_INTERFACE_PGLASS9: - apperance.glasses = event.type-EVENT_INTERFACE_PGLASS0; + appearance.glasses = event.type-EVENT_INTERFACE_PGLASS0; UpdatePerso(); m_main->ScenePerso(); break; @@ -417,25 +417,25 @@ bool CScreenApperance::EventProcess(const Event &event) break; case EVENT_INTERFACE_PDEF: - apperance.DefPerso(); + appearance.DefPerso(); UpdatePerso(); m_main->ScenePerso(); break; case EVENT_INTERFACE_PLROT: - m_apperanceAngle += 0.2f; + m_appearanceAngle += 0.2f; break; case EVENT_INTERFACE_PRROT: - m_apperanceAngle -= 0.2f; + m_appearanceAngle -= 0.2f; break; case EVENT_INTERFACE_POK: - m_main->GetPlayerProfile()->SaveApperance(); + m_main->GetPlayerProfile()->SaveAppearance(); m_main->ChangePhase(PHASE_MAIN_MENU); break; case EVENT_INTERFACE_PCANCEL: - m_main->GetPlayerProfile()->LoadApperance(); // reload apperance from file + m_main->GetPlayerProfile()->LoadAppearance(); // reload appearance from file m_main->ChangePhase(PHASE_PLAYER_SELECT); break; @@ -445,14 +445,14 @@ bool CScreenApperance::EventProcess(const Event &event) return false; } -bool CScreenApperance::GetGamerOnlyHead() +bool CScreenAppearance::GetGamerOnlyHead() { - return m_apperanceTab == 0; + return m_appearanceTab == 0; } -float CScreenApperance::GetPersoAngle() +float CScreenAppearance::GetPersoAngle() { - return m_apperanceAngle; + return m_appearanceAngle; } // Tests whether two colors are equal or nearly are. @@ -466,7 +466,7 @@ static bool EqColor(const Gfx::Color &c1, const Gfx::Color &c2) // Updates all the buttons for the character. -void CScreenApperance::UpdatePerso() +void CScreenAppearance::UpdatePerso() { CWindow* pw; CLabel* pl; @@ -477,7 +477,7 @@ void CScreenApperance::UpdatePerso() std::string name; int i; - PlayerApperance& apperance = m_main->GetPlayerProfile()->GetApperance(); + PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance(); pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; @@ -485,18 +485,18 @@ void CScreenApperance::UpdatePerso() pb = static_cast(pw->SearchControl(EVENT_INTERFACE_PHEAD)); if ( pb != nullptr ) { - pb->SetState(STATE_CHECK, m_apperanceTab==0); + pb->SetState(STATE_CHECK, m_appearanceTab==0); } pb = static_cast(pw->SearchControl(EVENT_INTERFACE_PBODY)); if ( pb != nullptr ) { - pb->SetState(STATE_CHECK, m_apperanceTab==1); + pb->SetState(STATE_CHECK, m_appearanceTab==1); } pl = static_cast(pw->SearchControl(EVENT_LABEL11)); if ( pl != nullptr ) { - if ( m_apperanceTab == 0 ) + if ( m_appearanceTab == 0 ) { pl->SetState(STATE_VISIBLE); GetResource(RES_TEXT, RT_PERSO_FACE, name); @@ -511,7 +511,7 @@ void CScreenApperance::UpdatePerso() pl = static_cast(pw->SearchControl(EVENT_LABEL12)); if ( pl != nullptr ) { - if ( m_apperanceTab == 0 ) + if ( m_appearanceTab == 0 ) { pl->SetState(STATE_VISIBLE); GetResource(RES_TEXT, RT_PERSO_GLASSES, name); @@ -526,7 +526,7 @@ void CScreenApperance::UpdatePerso() pl = static_cast(pw->SearchControl(EVENT_LABEL13)); if ( pl != nullptr ) { - if ( m_apperanceTab == 0 ) GetResource(RES_TEXT, RT_PERSO_HAIR, name); + if ( m_appearanceTab == 0 ) GetResource(RES_TEXT, RT_PERSO_HAIR, name); else GetResource(RES_TEXT, RT_PERSO_BAND, name); pl->SetName(name); } @@ -534,7 +534,7 @@ void CScreenApperance::UpdatePerso() pl = static_cast(pw->SearchControl(EVENT_LABEL14)); if ( pl != nullptr ) { - if ( m_apperanceTab == 0 ) + if ( m_appearanceTab == 0 ) { pl->ClearState(STATE_VISIBLE); } @@ -550,23 +550,23 @@ void CScreenApperance::UpdatePerso() { pb = static_cast(pw->SearchControl(static_cast(EVENT_INTERFACE_PFACE1+i))); if ( pb == nullptr ) break; - pb->SetState(STATE_VISIBLE, m_apperanceTab==0); - pb->SetState(STATE_CHECK, i==apperance.face); + pb->SetState(STATE_VISIBLE, m_appearanceTab==0); + pb->SetState(STATE_CHECK, i==appearance.face); } for ( i=0 ; i<10 ; i++ ) { pb = static_cast(pw->SearchControl(static_cast(EVENT_INTERFACE_PGLASS0+i))); if ( pb == nullptr ) break; - pb->SetState(STATE_VISIBLE, m_apperanceTab==0); - pb->SetState(STATE_CHECK, i==apperance.glasses); + pb->SetState(STATE_VISIBLE, m_appearanceTab==0); + pb->SetState(STATE_CHECK, i==appearance.glasses); } for ( i=0 ; i<3*3 ; i++ ) { pc = static_cast(pw->SearchControl(static_cast(EVENT_INTERFACE_PC0a+i))); if ( pc == nullptr ) break; - if ( m_apperanceTab == 0 ) + if ( m_appearanceTab == 0 ) { pc->ClearState(STATE_VISIBLE); } @@ -578,29 +578,29 @@ void CScreenApperance::UpdatePerso() color.b = PERSO_COLOR[3*10*1+3*i+2]/255.0f; color.a = 0.0f; pc->SetColor(color); - pc->SetState(STATE_CHECK, EqColor(color, apperance.colorCombi)); + pc->SetState(STATE_CHECK, EqColor(color, appearance.colorCombi)); } pc = static_cast(pw->SearchControl(static_cast(EVENT_INTERFACE_PC0b+i))); if ( pc == nullptr ) break; - color.r = PERSO_COLOR[3*10*2*m_apperanceTab+3*i+0]/255.0f; - color.g = PERSO_COLOR[3*10*2*m_apperanceTab+3*i+1]/255.0f; - color.b = PERSO_COLOR[3*10*2*m_apperanceTab+3*i+2]/255.0f; + color.r = PERSO_COLOR[3*10*2*m_appearanceTab+3*i+0]/255.0f; + color.g = PERSO_COLOR[3*10*2*m_appearanceTab+3*i+1]/255.0f; + color.b = PERSO_COLOR[3*10*2*m_appearanceTab+3*i+2]/255.0f; color.a = 0.0f; pc->SetColor(color); - pc->SetState(STATE_CHECK, EqColor(color, m_apperanceTab?apperance.colorBand:apperance.colorHair)); + pc->SetState(STATE_CHECK, EqColor(color, m_appearanceTab?appearance.colorBand:appearance.colorHair)); } for ( i=0 ; i<3 ; i++ ) { ps = static_cast(pw->SearchControl(static_cast(EVENT_INTERFACE_PCRa+i))); if ( ps == nullptr ) break; - ps->SetState(STATE_VISIBLE, m_apperanceTab==1); + ps->SetState(STATE_VISIBLE, m_appearanceTab==1); } - if ( m_apperanceTab == 1 ) + if ( m_appearanceTab == 1 ) { - color = apperance.colorCombi; + color = appearance.colorCombi; ps = static_cast(pw->SearchControl(EVENT_INTERFACE_PCRa)); if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f); ps = static_cast(pw->SearchControl(EVENT_INTERFACE_PCGa)); @@ -609,8 +609,8 @@ void CScreenApperance::UpdatePerso() if ( ps != nullptr ) ps->SetVisibleValue(color.b*255.0f); } - if ( m_apperanceTab == 0 ) color = apperance.colorHair; - else color = apperance.colorBand; + if ( m_appearanceTab == 0 ) color = appearance.colorHair; + else color = appearance.colorBand; ps = static_cast(pw->SearchControl(EVENT_INTERFACE_PCRb)); if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f); ps = static_cast(pw->SearchControl(EVENT_INTERFACE_PCGb)); @@ -621,9 +621,9 @@ void CScreenApperance::UpdatePerso() // Updates the camera for the character. -void CScreenApperance::CameraPerso() +void CScreenAppearance::CameraPerso() { - if ( m_apperanceTab == 0 ) + if ( m_appearanceTab == 0 ) { SetCamera(0.325f, -0.15f, 5.0f); } @@ -635,44 +635,44 @@ void CScreenApperance::CameraPerso() // Sets a fixed color. -void CScreenApperance::FixPerso(int rank, int index) +void CScreenAppearance::FixPerso(int rank, int index) { - PlayerApperance& apperance = m_main->GetPlayerProfile()->GetApperance(); - if ( m_apperanceTab == 0 ) + PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance(); + if ( m_appearanceTab == 0 ) { if ( index == 1 ) { - apperance.colorHair.r = PERSO_COLOR[3*10*0+rank*3+0]/255.0f; - apperance.colorHair.g = PERSO_COLOR[3*10*0+rank*3+1]/255.0f; - apperance.colorHair.b = PERSO_COLOR[3*10*0+rank*3+2]/255.0f; + appearance.colorHair.r = PERSO_COLOR[3*10*0+rank*3+0]/255.0f; + appearance.colorHair.g = PERSO_COLOR[3*10*0+rank*3+1]/255.0f; + appearance.colorHair.b = PERSO_COLOR[3*10*0+rank*3+2]/255.0f; } } - if ( m_apperanceTab == 1 ) + if ( m_appearanceTab == 1 ) { if ( index == 0 ) { - apperance.colorCombi.r = PERSO_COLOR[3*10*1+rank*3+0]/255.0f; - apperance.colorCombi.g = PERSO_COLOR[3*10*1+rank*3+1]/255.0f; - apperance.colorCombi.b = PERSO_COLOR[3*10*1+rank*3+2]/255.0f; + appearance.colorCombi.r = PERSO_COLOR[3*10*1+rank*3+0]/255.0f; + appearance.colorCombi.g = PERSO_COLOR[3*10*1+rank*3+1]/255.0f; + appearance.colorCombi.b = PERSO_COLOR[3*10*1+rank*3+2]/255.0f; } if ( index == 1 ) { - apperance.colorBand.r = PERSO_COLOR[3*10*2+rank*3+0]/255.0f; - apperance.colorBand.g = PERSO_COLOR[3*10*2+rank*3+1]/255.0f; - apperance.colorBand.b = PERSO_COLOR[3*10*2+rank*3+2]/255.0f; + appearance.colorBand.r = PERSO_COLOR[3*10*2+rank*3+0]/255.0f; + appearance.colorBand.g = PERSO_COLOR[3*10*2+rank*3+1]/255.0f; + appearance.colorBand.b = PERSO_COLOR[3*10*2+rank*3+2]/255.0f; } } } // Updates the color of the character. -void CScreenApperance::ColorPerso() +void CScreenAppearance::ColorPerso() { CWindow* pw; CSlider* ps; Gfx::Color color; - PlayerApperance& apperance = m_main->GetPlayerProfile()->GetApperance(); + PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance(); pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; @@ -685,7 +685,7 @@ void CScreenApperance::ColorPerso() if ( ps != nullptr ) color.g = ps->GetVisibleValue()/255.0f; ps = static_cast(pw->SearchControl(EVENT_INTERFACE_PCBa)); if ( ps != nullptr ) color.b = ps->GetVisibleValue()/255.0f; - if ( m_apperanceTab == 1 ) apperance.colorCombi = color; + if ( m_appearanceTab == 1 ) appearance.colorCombi = color; ps = static_cast(pw->SearchControl(EVENT_INTERFACE_PCRb)); if ( ps != nullptr ) color.r = ps->GetVisibleValue()/255.0f; @@ -693,11 +693,11 @@ void CScreenApperance::ColorPerso() if ( ps != nullptr ) color.g = ps->GetVisibleValue()/255.0f; ps = static_cast(pw->SearchControl(EVENT_INTERFACE_PCBb)); if ( ps != nullptr ) color.b = ps->GetVisibleValue()/255.0f; - if ( m_apperanceTab == 0 ) apperance.colorHair = color; - else apperance.colorBand = color; + if ( m_appearanceTab == 0 ) appearance.colorHair = color; + else appearance.colorBand = color; } -void CScreenApperance::SetCamera(float x, float y, float cameraDistance) +void CScreenAppearance::SetCamera(float x, float y, float cameraDistance) { Gfx::CCamera* camera = m_main->GetCamera(); Gfx::CEngine* engine = Gfx::CEngine::GetInstancePointer(); diff --git a/src/ui/screen/screen_apperance.h b/src/ui/screen/screen_appearance.h similarity index 86% rename from src/ui/screen/screen_apperance.h rename to src/ui/screen/screen_appearance.h index 32109361..3b1da435 100644 --- a/src/ui/screen/screen_apperance.h +++ b/src/ui/screen/screen_appearance.h @@ -24,10 +24,10 @@ namespace Ui { -class CScreenApperance : public CScreen +class CScreenAppearance : public CScreen { public: - CScreenApperance(); + CScreenAppearance(); void CreateInterface() override; bool EventProcess(const Event &event) override; @@ -44,8 +44,8 @@ protected: void SetCamera(float x, float y, float cameraDistance); protected: - int m_apperanceTab; // perso: tab selected - float m_apperanceAngle; // perso: angle of presentation + int m_appearanceTab; // perso: tab selected + float m_appearanceAngle; // perso: angle of presentation }; } // namespace Ui diff --git a/src/ui/screen/screen_player_select.cpp b/src/ui/screen/screen_player_select.cpp index 40472ff9..7727d240 100644 --- a/src/ui/screen/screen_player_select.cpp +++ b/src/ui/screen/screen_player_select.cpp @@ -183,7 +183,7 @@ bool CScreenPlayerSelect::EventProcess(const Event &event) case EVENT_INTERFACE_PERSO: NameSelect(); - m_main->ChangePhase(PHASE_APPERANCE); + m_main->ChangePhase(PHASE_APPEARANCE); break; case EVENT_INTERFACE_NDELETE: From 3400a0fab0660ad951377b86f8f7450b859573b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Wed, 2 Feb 2022 10:57:23 +0100 Subject: [PATCH 03/16] Add -loadgame argument to load game on start --- src/app/app.cpp | 28 +++++++++++++++++++++++++++- src/app/app.h | 4 ++++ src/level/robotmain.cpp | 11 +++++++++++ src/level/robotmain.h | 3 +++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 2d253aa3..bd37396b 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -246,6 +246,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) OPT_DEBUG, OPT_RUNSCENE, OPT_SCENETEST, + OPT_LOADGAME, OPT_LOGLEVEL, OPT_LANGDIR, OPT_DATADIR, @@ -264,6 +265,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) { "debug", required_argument, nullptr, OPT_DEBUG }, { "runscene", required_argument, nullptr, OPT_RUNSCENE }, { "scenetest", no_argument, nullptr, OPT_SCENETEST }, + { "loadgame", required_argument, nullptr, OPT_LOADGAME }, { "loglevel", required_argument, nullptr, OPT_LOGLEVEL }, { "langdir", required_argument, nullptr, OPT_LANGDIR }, { "datadir", required_argument, nullptr, OPT_DATADIR }, @@ -309,6 +311,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) GetLogger()->Message(" -debug modes enable debug modes (more info printed in logs; see code for reference of modes)\n"); GetLogger()->Message(" -runscene sceneNNN run given scene on start\n"); GetLogger()->Message(" -scenetest win every mission right after it's loaded\n"); + GetLogger()->Message(" -loadgame path load given game on start (path is /\n"); GetLogger()->Message(" -loglevel level set log level to level (one of: trace, debug, info, warn, error, none)\n"); GetLogger()->Message(" -langdir path set custom language directory path\n"); GetLogger()->Message(" environment variable: COLOBOT_LANG_DIR\n"); @@ -366,6 +369,22 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) m_sceneTest = true; break; } + case OPT_LOADGAME: + { + std::string playerAndPath = optarg; + size_t pos = playerAndPath.find_first_of("/"); + if (pos != std::string::npos && pos > 0 && pos+1 < playerAndPath.length()) // Split player name from path + { + m_loadGamePlayerName = playerAndPath.substr(0, pos); + m_loadGameDirName = playerAndPath.substr(pos+1, playerAndPath.length()-pos-1); + } + else + { + GetLogger()->Error("Invalid path: '%s'\n", optarg); + return PARSE_ARGS_FAIL; + } + break; + } case OPT_LOGLEVEL: { LogLevel logLevel; @@ -698,8 +717,15 @@ bool CApplication::Create() StartLoadingMusic(); - if (m_runSceneCategory == LevelCategory::Max) + if (!m_loadGameDirName.empty()) + { + m_controller->GetRobotMain()->SelectPlayer(m_loadGamePlayerName); + m_controller->GetRobotMain()->LoadGameFromDirName(m_loadGameDirName); + } + else if (m_runSceneCategory == LevelCategory::Max) + { m_controller->StartApp(); + } else { m_controller->GetRobotMain()->UpdateCustomLevelList(); // To load the userlevels diff --git a/src/app/app.h b/src/app/app.h index 45e41464..2efe34a1 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -405,6 +405,10 @@ protected: int m_runSceneRank; //@} + //! Game to load on startup + std::string m_loadGamePlayerName; + std::string m_loadGameDirName; + //! Scene test mode bool m_sceneTest; diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 68ecee4f..a5cc4fdb 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -5839,6 +5839,17 @@ void CRobotMain::QuickLoad() m_playerProfile->LoadScene(dir); } +void CRobotMain::LoadGameFromDirName(const std::string& gameDir) +{ + std::string dir = m_playerProfile->GetSaveFile(gameDir); + if(!CResourceManager::Exists(dir)) + { + GetLogger()->Error("Save slot not found\n"); + return; + } + m_playerProfile->LoadScene(dir); +} + void CRobotMain::SetExitAfterMission(bool exit) { m_exitAfterMission = exit; diff --git a/src/level/robotmain.h b/src/level/robotmain.h index ceddcb78..a7f0206b 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -368,6 +368,9 @@ public: //! Enable mode where completing mission closes the game void SetExitAfterMission(bool exit); + //! Load saved game (used by command line argument) + void LoadGameFromDirName(const std::string& gameDir); + //! Returns true if player can interact with things manually bool CanPlayerInteract(); From d9f307839693675bcba7ac80a2434b14febafad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Mon, 31 Jan 2022 23:25:00 +0100 Subject: [PATCH 04/16] Replace beam search with breadth-first search --- src/object/task/taskgoto.cpp | 274 ++++++++++++++++++++--------------- src/object/task/taskgoto.h | 13 +- 2 files changed, 168 insertions(+), 119 deletions(-) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index 6cdaed8c..cba8826e 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -49,9 +49,10 @@ const float FLY_DEF_HEIGHT = 50.0f; // default flying height // Settings that define goto() accuracy: const float BM_DIM_STEP = 5.0f; // Size of one pixel on the bitmap. Setting 5 means that 5x5 square (in game units) will be represented by 1 px on the bitmap. Decreasing this value will make a bigger bitmap, and may increase accuracy. TODO: Check how it actually impacts goto() accuracy const float BEAM_ACCURACY = 5.0f; // higher value = more accurate, but slower -const float SAFETY_MARGIN = 0.5f; // Smallest distance between two objects. Smaller = less "no route to destination", but higher probability of collisions between objects. +const float SAFETY_MARGIN = 1.5f; // Smallest distance between two objects. Smaller = less "no route to destination", but higher probability of collisions between objects. // Changing SAFETY_MARGIN (old value was 4.0f) seems to have fixed many issues with goto(). TODO: maybe we could make it even smaller? Did changing it introduce any new bugs? +const int NB_ITER = 2000; // Maximum number of iterations you have the right to make before temporarily interrupt in order not to lower the framerate. @@ -126,7 +127,7 @@ bool CTaskGoto::EventProcess(const Event &event) if (a || b) { Gfx::Color c = Gfx::Color(0.0f, 0.0f, 0.0f, 1.0f); - if (b) c = Gfx::Color(0.0f, 0.0f, 1.0f, 1.0f); + if (b) c = Gfx::Color(0.0f, 1.0f, 1.0f, 1.0f); debugImage->SetPixel(Math::IntPoint(x, y), c); } } @@ -1688,6 +1689,8 @@ void CTaskGoto::BeamInit() m_bmIter[i] = -1; } m_bmStep = 0; + m_bfsQueueBegin = 0; + m_bfsQueueEnd = 0; } // Calculates points and passes to go from start to goal. @@ -1701,138 +1704,166 @@ void CTaskGoto::BeamInit() Error CTaskGoto::BeamSearch(const Math::Vector &start, const Math::Vector &goal, float goalRadius) { - float step, len; - int nbIter; - m_bmStep ++; - len = Math::DistanceProjected(start, goal); - step = len/BEAM_ACCURACY; - if ( step < BM_DIM_STEP*2.1f ) step = BM_DIM_STEP*2.1f; - if ( step > 20.0f ) step = 20.0f; - nbIter = 200; // in order not to lower the framerate - m_bmIterCounter = 0; - return BeamExplore(start, start, goal, goalRadius, 165.0f*Math::PI/180.0f, 22, step, 0, nbIter); -} + // Relative postion and distance to neighbors. + static const int dXs[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; + static const int dYs[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; + // static const float dDist[8] = {M_SQRT2, 1.0f, M_SQRT2, 1.0f, 1.0f, M_SQRT2, 1.0f, M_SQRT2}; + static const int32_t dDist[8] = {7, 5, 7, 5, 5, 7, 5, 7}; -// prevPos: previous position -// curPos: current position -// goalPos: position that seeks to achieve -// angle: angle to the goal we explores -// nbDiv: number of subdivisions being done with angle -// step length of a step -// i number of recursions made -// nbIter maximum number of iterations you have the right to make before temporarily interrupt + const int startX = static_cast((start.x+1600.0f)/BM_DIM_STEP); + const int startY = static_cast((start.z+1600.0f)/BM_DIM_STEP); + const int goalX = static_cast((goal.x+1600.0f)/BM_DIM_STEP); + const int goalY = static_cast((goal.z+1600.0f)/BM_DIM_STEP); -Error CTaskGoto::BeamExplore(const Math::Vector &prevPos, const Math::Vector &curPos, - const Math::Vector &goalPos, float goalRadius, - float angle, int nbDiv, float step, - int i, int nbIter) -{ - Math::Vector newPos; - Error ret; - int iDiv, iClear, iLar; - - iLar = 0; - if ( i >= MAXPOINTS ) return ERR_GOTO_ITER; // too many recursions - - m_bmTotal = i; - - if ( m_bmIter[i] == -1 ) + if (m_bfsQueueEnd == 0) // New search { - m_bmIter[i] = 0; - - if ( i == 0 ) + if (startX == goalX && startY == goalY) { - m_bmPoints[i] = curPos; + m_bmPoints[0] = start; + m_bmPoints[1] = goal; + m_bmTotal = 1; + return ERR_OK; } - else + // Enqueue the goal node + if ( goalX >= 0 && goalX < m_bmSize && + goalY >= 0 && goalY < m_bmSize ) { - if ( !BitmapTestLine(prevPos, curPos, angle/nbDiv, true) ) return ERR_GOTO_IMPOSSIBLE; + const int indexInMap = goalY * m_bmSize + goalX; + m_bfsDistances[indexInMap] = 0.0; + m_bfsQueue[m_bfsQueueEnd++] = indexInMap; + BitmapSetDot(1, goalX, goalY); // Mark as enqueued + } - m_bmPoints[i] = curPos; - - if ( Math::DistanceProjected(curPos, goalPos)-goalRadius <= step ) + // Enqueue nodes around the goal + if (goalRadius > 0.0f) + { + const int minX = std::max(0, static_cast((goal.x-goalRadius+1600.0f)/BM_DIM_STEP)); + const int minY = std::max(0, static_cast((goal.z-goalRadius+1600.0f)/BM_DIM_STEP)); + const int maxX = std::min(m_bmSize-1, static_cast((goal.x+goalRadius+1600.0f)/BM_DIM_STEP)); + const int maxY = std::min(m_bmSize-1, static_cast((goal.z+goalRadius+1600.0f)/BM_DIM_STEP)); + for (int y = minY; y <= maxY; ++y) { - if ( goalRadius == 0.0f ) + for (int x = minX; x <= maxX; ++x) { - newPos = goalPos; - } - else - { - newPos = BeamPoint(curPos, goalPos, 0, Math::DistanceProjected(curPos, goalPos)-goalRadius); - } - if ( BitmapTestLine(curPos, newPos, angle/nbDiv, false) ) - { - m_bmPoints[i+1] = newPos; - m_bmTotal = i+1; - return ERR_OK; + float floatX = (x + 0.5f) * BM_DIM_STEP - 1600.0f; + float floatY = (y + 0.5f) * BM_DIM_STEP - 1600.0f; + if (std::hypot(floatX-goal.x, floatY-goal.z) <= goalRadius && + BitmapTestDotIsVisitable(x, y) && + !BitmapTestDot(1, x, y)) + { + const int indexInMap = y * m_bmSize + x; + m_bfsDistances[indexInMap] = 0.0; + m_bfsQueue[m_bfsQueueEnd++] = indexInMap; + BitmapSetDot(1, x, y); // Mark as enqueued + } } } } } - if ( iLar >= m_bmIter[i] ) + m_bmIterCounter = 0; + + while (m_bfsQueueBegin != m_bfsQueueEnd) { - newPos = BeamPoint(curPos, goalPos, 0, step); - ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); - if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; - m_bmIter[i] = iLar+1; - for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -1; + // Pop a node from the queue + const uint32_t indexInMap = m_bfsQueue[m_bfsQueueBegin++]; + const int x = indexInMap % m_bmSize; + const int y = indexInMap / m_bmSize; + const int32_t distance = m_bfsDistances[indexInMap]; + + if (x == startX && y == startY) + { + // We have reached the start. + // Follow decreasing distances to find the path. + m_bmPoints[0] = start; + int btX = x; + int btY = y; + for (m_bmTotal = 1; m_bmTotal < MAXPOINTS; ++m_bmTotal) + { + int bestX = -1; + int bestY = -1; + int32_t bestDistance = std::numeric_limits::max(); + for (int i = 0; i < 8; ++i) + { + const int nX = btX + dXs[i]; + const int nY = btY + dYs[i]; + if (!BitmapTestDot(1, nX, nY)) continue; + const int32_t nDistance = m_bfsDistances[nY * m_bmSize + nX]; + if (nDistance < bestDistance) + { + bestX = nX; + bestY = nY; + bestDistance = nDistance; + } + } + if (bestX == -1) + { + GetLogger()->Error("Failed to find node parent\n"); + return ERR_GOTO_ITER; + } + btX = bestX; + btY = bestY; + if (btX == goalX && btY == goalY) + { + m_bmPoints[m_bmTotal] = goal; + break; + } + m_bmPoints[m_bmTotal].x = (btX + 0.5f) * BM_DIM_STEP - 1600.f; + m_bmPoints[m_bmTotal].z = (btY + 0.5f) * BM_DIM_STEP - 1600.f; + + if (bestDistance == 0) + { + break; + } + } + // std::reverse(m_bmPoints, m_bmPoints + m_bmTotal); + + GetLogger()->Info("Found path to goal with %d nodes\n", m_bmTotal + 1); + return ERR_OK; + } + + // Expand the node + for (int i = 0; i < 8; ++i) + { + const int nX = x + dXs[i]; + const int nY = y + dYs[i]; + if (BitmapTestDotIsVisitable(nX, nY)) + { + if (BitmapTestDot(1, nX, nY)) + { + // We have seen this node before. + // Update distance without adding it to the queue. + const int neighborIndexInMap = nY * m_bmSize + nX; + m_bfsDistances[neighborIndexInMap] = std::min( + m_bfsDistances[neighborIndexInMap], + distance + dDist[i]); + } + else + { + // Enqueue this neighbor + const int neighborIndexInMap = nY * m_bmSize + nX; + m_bfsDistances[neighborIndexInMap] = distance + dDist[i]; + m_bfsQueue[m_bfsQueueEnd++] = neighborIndexInMap; + BitmapSetDot(1, nX, nY); // Mark as enqueued + if (m_bfsQueueEnd > m_bmSize * m_bmSize) + { + GetLogger()->Error("Queue is full\n"); + return ERR_GOTO_ITER; + } + } + } + } + + // Limit the number of iterations per frame. m_bmIterCounter ++; - if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; - } - iLar ++; - - for ( iDiv=1 ; iDiv<=nbDiv ; iDiv++ ) - { - if ( iLar >= m_bmIter[i] ) - { - newPos = BeamPoint(curPos, goalPos, angle*iDiv/nbDiv, step); - ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); - if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; - m_bmIter[i] = iLar+1; - for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -1; - m_bmIterCounter ++; - if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; - } - iLar ++; - - if ( iLar >= m_bmIter[i] ) - { - newPos = BeamPoint(curPos, goalPos, -angle*iDiv/nbDiv, step); - ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); - if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; - m_bmIter[i] = iLar+1; - for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -1; - m_bmIterCounter ++; - if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; - } - iLar ++; + if ( m_bmIterCounter >= NB_ITER ) return ERR_CONTINUE; } return ERR_GOTO_IMPOSSIBLE; } -// Is a right "start-goal". Calculates the point located at the distance "step" -// from the point "start" and an angle "angle" with the right. - -Math::Vector CTaskGoto::BeamPoint(const Math::Vector &startPoint, - const Math::Vector &goalPoint, - float angle, float step) -{ - Math::Vector resPoint; - float goalAngle; - - goalAngle = Math::RotateAngle(goalPoint.x-startPoint.x, goalPoint.z-startPoint.z); - - resPoint.x = startPoint.x + cosf(goalAngle+angle)*step; - resPoint.z = startPoint.z + sinf(goalAngle+angle)*step; - resPoint.y = 0.0f; - - return resPoint; -} - // Tests if a path along a straight line is possible. bool CTaskGoto::BitmapTestLine(const Math::Vector &start, const Math::Vector &goal, @@ -2094,10 +2125,11 @@ void CTaskGoto::BitmapTerrain(int minx, int miny, int maxx, int maxy) bool CTaskGoto::BitmapOpen() { - BitmapClose(); - m_bmSize = static_cast(3200.0f/BM_DIM_STEP); - m_bmArray = MakeUniqueArray(m_bmSize*m_bmSize/8*2); + if (m_bmArray.get() == nullptr) m_bmArray = MakeUniqueArray(m_bmSize*m_bmSize/8*2); + memset(m_bmArray.get(), 0, m_bmSize*m_bmSize/8*2); + if (m_bfsDistances.get() == nullptr) m_bfsDistances = MakeUniqueArray(m_bmSize*m_bmSize); + if (m_bfsQueue.get() == nullptr) m_bfsQueue = MakeUniqueArray(m_bmSize*m_bmSize); m_bmChanged = true; m_bmOffset = m_bmSize/2; @@ -2204,3 +2236,17 @@ bool CTaskGoto::BitmapTestDot(int rank, int x, int y) return m_bmArray[rank*m_bmLine*m_bmSize + m_bmLine*y + x/8] & (1<= m_bmSize || + y < 0 || y >= m_bmSize ) return false; + + if ( x < m_bmMinX || x > m_bmMaxX || + y < m_bmMinY || y > m_bmMaxY ) + { + BitmapTerrain(x-10,y-10, x+10,y+10); // remade a layer + } + + return !(m_bmArray[m_bmLine*y + x/8] & (1< m_bmArray; // bit table + std::unique_ptr m_bmArray; // Bit table + std::unique_ptr m_bfsDistances; // Distances to the goal for breadth-first search. + std::unique_ptr m_bfsQueue; // Nodes in the queue. Stored as indices. + int m_bfsQueueBegin = 0; // Front of the queue. This is the next node to be expanded. + int m_bfsQueueEnd = 0; // Back of the queue. This is where nodes are inserted. int m_bmMinX = 0, m_bmMinY = 0; int m_bmMaxX = 0, m_bmMaxY = 0; - int m_bmTotal = 0; // number of points in m_bmPoints + int m_bmTotal = 0; // index of final point in m_bmPoints int m_bmIndex = 0; // index in m_bmPoints Math::Vector m_bmPoints[MAXPOINTS+2]; signed char m_bmIter[MAXPOINTS+2] = {}; From e2d4dd6995a39f221229bfc56325ab11c8e37a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Wed, 2 Feb 2022 20:15:30 +0100 Subject: [PATCH 05/16] Use ray tracing algorithm in BitmapTestLine --- src/object/task/taskgoto.cpp | 91 ++++++++++++++++++++---------------- src/object/task/taskgoto.h | 2 +- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index cba8826e..ce891f5d 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -1636,7 +1636,7 @@ int CTaskGoto::BeamShortcut() for ( i=m_bmTotal ; i>=m_bmIndex+2 ; i-- ) // tries from the last { - if ( BitmapTestLine(m_bmPoints[m_bmIndex], m_bmPoints[i], 0.0f, false) ) + if ( BitmapTestLine(m_bmPoints[m_bmIndex], m_bmPoints[i]) ) { return i; // bingo, found } @@ -1866,62 +1866,73 @@ Error CTaskGoto::BeamSearch(const Math::Vector &start, const Math::Vector &goal, // Tests if a path along a straight line is possible. -bool CTaskGoto::BitmapTestLine(const Math::Vector &start, const Math::Vector &goal, - float stepAngle, bool bSecond) +bool CTaskGoto::BitmapTestLine(const Math::Vector &start, const Math::Vector &goal) { - Math::Vector pos, inc; - float dist, step; - float distNoB2; - int i, max, x, y; - if ( m_bmArray == nullptr ) return true; - dist = Math::DistanceProjected(start, goal); - if ( dist == 0.0f ) return true; - step = BM_DIM_STEP*0.5f; + const Math::Point startInGrid = Math::Point((start.x+1600.0f)/BM_DIM_STEP, (start.z+1600.0f)/BM_DIM_STEP); + const Math::Point goalInGrid = Math::Point((goal.x+1600.0f)/BM_DIM_STEP, (goal.z+1600.0f)/BM_DIM_STEP); - inc.x = (goal.x-start.x)*step/dist; - inc.z = (goal.z-start.z)*step/dist; + const int startXInt = static_cast(startInGrid.x); + const int startYInt = static_cast(startInGrid.y); + const int goalXInt = static_cast(goalInGrid.x); + const int goalYInt = static_cast(goalInGrid.y); - pos = start; - - if ( bSecond ) + if (startXInt == goalXInt && startYInt == goalYInt) { - x = static_cast((pos.x+1600.0f)/BM_DIM_STEP); - y = static_cast((pos.z+1600.0f)/BM_DIM_STEP); - BitmapSetDot(1, x, y); // puts the flag as the starting point + return true; } - max = static_cast(dist/step); - if ( max == 0 ) max = 1; - distNoB2 = BM_DIM_STEP*sqrtf(2.0f)/sinf(stepAngle); - for ( i=0 ; i 0.0f ? 1 : -1; + const int stepY = dirInGrid.y > 0.0f ? 1 : -1; + + // At what t does the ray enter the next cell? + float tMaxX = + dirInGrid.x > 0.0 ? (std::floor(startInGrid.x) - startInGrid.x + 1) / dirInGrid.x : + dirInGrid.x < 0.0 ? (std::floor(startInGrid.x) - startInGrid.x) / dirInGrid.x : + std::numeric_limits::infinity(); + float tMaxY = + dirInGrid.y > 0.0 ? (std::floor(startInGrid.y) - startInGrid.y + 1) / dirInGrid.y : + dirInGrid.y < 0.0 ? (std::floor(startInGrid.y) - startInGrid.y) / dirInGrid.y : + std::numeric_limits::infinity(); + + // How much t is needed to step from one column/row to another? + // stepX = dir.x * t + // stepX / dir.x = t + const float tDeltaX = static_cast(stepX) / dirInGrid.x; + const float tDeltaY = static_cast(stepY) / dirInGrid.y; + + // Traverse the grid + const int numIntersections = + std::abs(goalXInt - startXInt) + + std::abs(goalYInt - startYInt); + int x = startXInt; + int y = startYInt; + + for ( int i = 0; i < numIntersections; ++i ) { - if ( i == max-1 ) + if ( tMaxX < tMaxY ) { - pos = goal; // tests the point of arrival + tMaxX += tDeltaX; + x += stepX; } else { - pos.x += inc.x; - pos.z += inc.z; + tMaxY += tDeltaY; + y += stepY; } - - x = static_cast((pos.x+1600.0f)/BM_DIM_STEP); - y = static_cast((pos.z+1600.0f)/BM_DIM_STEP); - - if ( bSecond ) + if ( BitmapTestDot(0, x, y) ) { - if ( i > 2 && BitmapTestDot(1, x, y) ) return false; - - if ( step*(i+1) > distNoB2 && i < max-2 ) - { - BitmapSetDot(1, x, y); - } + return false; } - - if ( BitmapTestDot(0, x, y) ) return false; } + return true; } diff --git a/src/object/task/taskgoto.h b/src/object/task/taskgoto.h index 4d18135d..118a372a 100644 --- a/src/object/task/taskgoto.h +++ b/src/object/task/taskgoto.h @@ -104,7 +104,7 @@ protected: void BeamInit(); Error BeamSearch(const Math::Vector &start, const Math::Vector &goal, float goalRadius); - bool BitmapTestLine(const Math::Vector &start, const Math::Vector &goal, float stepAngle, bool bSecond); + bool BitmapTestLine(const Math::Vector &start, const Math::Vector &goal); void BitmapObject(); void BitmapTerrain(const Math::Vector &min, const Math::Vector &max); void BitmapTerrain(int minx, int miny, int maxx, int maxy); From a7b294025e62b6e267c0ae44965fe362551f575a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Tue, 1 Feb 2022 21:32:13 +0100 Subject: [PATCH 06/16] Rename BeamSearch to PathFindingSearch etc --- src/object/task/taskgoto.cpp | 24 ++++++++++++------------ src/object/task/taskgoto.h | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index ce891f5d..3e9a06f5 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -222,7 +222,7 @@ bool CTaskGoto::EventProcess(const Event &event) if ( m_bmCargoObject->GetType() == OBJECT_BASE ) dist = 12.0f; } - ret = BeamSearch(pos, goal, dist); + ret = PathFindingSearch(pos, goal, dist); if ( ret == ERR_OK ) { if ( m_physics->GetLand() ) m_phase = TGP_BEAMWCOLD; @@ -346,7 +346,7 @@ bool CTaskGoto::EventProcess(const Event &event) { m_physics->SetMotorSpeedX(0.0f); // stops the advance m_physics->SetMotorSpeedZ(0.0f); // stops the rotation - BeamStart(); // we start all + PathFindingStart(); // we start all return true; } @@ -790,7 +790,7 @@ Error CTaskGoto::Start(Math::Vector goal, float altitude, } } - BeamStart(); + PathFindingStart(); if ( m_bmCargoObject == nullptr ) { @@ -826,7 +826,7 @@ Error CTaskGoto::IsEnded() { m_physics->SetMotorSpeedX(0.0f); // stops the advance m_physics->SetMotorSpeedZ(0.0f); // stops the rotation - BeamInit(); + PathFindingInit(); m_phase = TGP_BEAMSEARCH; // will seek the path } return ERR_CONTINUE; @@ -888,7 +888,7 @@ Error CTaskGoto::IsEnded() m_physics->SetMotorSpeedX(0.0f); // stops the advance m_physics->SetMotorSpeedZ(0.0f); // stops the rotation - m_bmIndex = BeamShortcut(); + m_bmIndex = PathFindingShortcut(); if ( m_bmIndex > m_bmTotal ) { @@ -1630,7 +1630,7 @@ void CTaskGoto::ComputeFlyingRepulse(float &dir) // Among all of the following, seek if there is one allowing to go directly to the crow flies. // If yes, skip all the unnecessary intermediate points. -int CTaskGoto::BeamShortcut() +int CTaskGoto::PathFindingShortcut() { int i; @@ -1647,7 +1647,7 @@ int CTaskGoto::BeamShortcut() // That's the big start. -void CTaskGoto::BeamStart() +void CTaskGoto::PathFindingStart() { Math::Vector min, max; @@ -1673,14 +1673,14 @@ void CTaskGoto::BeamStart() { m_physics->SetMotorSpeedX(0.0f); // stops the advance m_physics->SetMotorSpeedZ(0.0f); // stops the rotation - BeamInit(); + PathFindingInit(); m_phase = TGP_BEAMSEARCH; // will seek the path } } -// Initialization before the first BeamSearch. +// Initialization before the first PathFindingSearch. -void CTaskGoto::BeamInit() +void CTaskGoto::PathFindingInit() { int i; @@ -1701,8 +1701,8 @@ void CTaskGoto::BeamInit() // ERR_CONTINUE if not done yet // goalRadius: distance at which we must approach the goal -Error CTaskGoto::BeamSearch(const Math::Vector &start, const Math::Vector &goal, - float goalRadius) +Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector &goal, + float goalRadius) { m_bmStep ++; diff --git a/src/object/task/taskgoto.h b/src/object/task/taskgoto.h index 118a372a..26bc52e3 100644 --- a/src/object/task/taskgoto.h +++ b/src/object/task/taskgoto.h @@ -99,10 +99,10 @@ protected: void ComputeRepulse(Math::Point &dir); void ComputeFlyingRepulse(float &dir); - int BeamShortcut(); - void BeamStart(); - void BeamInit(); - Error BeamSearch(const Math::Vector &start, const Math::Vector &goal, float goalRadius); + int PathFindingShortcut(); + void PathFindingStart(); + void PathFindingInit(); + Error PathFindingSearch(const Math::Vector &start, const Math::Vector &goal, float goalRadius); bool BitmapTestLine(const Math::Vector &start, const Math::Vector &goal); void BitmapObject(); From 14e7cd0e520594bb18d412e73f6cfb9310468f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Tue, 8 Feb 2022 14:17:11 +0100 Subject: [PATCH 07/16] Rename loadgame to loadsave --- src/app/app.cpp | 18 +++++++++--------- src/app/app.h | 4 ++-- src/level/robotmain.cpp | 2 +- src/level/robotmain.h | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index bd37396b..bd83cffd 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -246,7 +246,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) OPT_DEBUG, OPT_RUNSCENE, OPT_SCENETEST, - OPT_LOADGAME, + OPT_LOADSAVE, OPT_LOGLEVEL, OPT_LANGDIR, OPT_DATADIR, @@ -265,7 +265,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) { "debug", required_argument, nullptr, OPT_DEBUG }, { "runscene", required_argument, nullptr, OPT_RUNSCENE }, { "scenetest", no_argument, nullptr, OPT_SCENETEST }, - { "loadgame", required_argument, nullptr, OPT_LOADGAME }, + { "loadsave", required_argument, nullptr, OPT_LOADSAVE }, { "loglevel", required_argument, nullptr, OPT_LOGLEVEL }, { "langdir", required_argument, nullptr, OPT_LANGDIR }, { "datadir", required_argument, nullptr, OPT_DATADIR }, @@ -311,7 +311,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) GetLogger()->Message(" -debug modes enable debug modes (more info printed in logs; see code for reference of modes)\n"); GetLogger()->Message(" -runscene sceneNNN run given scene on start\n"); GetLogger()->Message(" -scenetest win every mission right after it's loaded\n"); - GetLogger()->Message(" -loadgame path load given game on start (path is /\n"); + GetLogger()->Message(" -loadsave path load given saved game on start (path is /\n"); GetLogger()->Message(" -loglevel level set log level to level (one of: trace, debug, info, warn, error, none)\n"); GetLogger()->Message(" -langdir path set custom language directory path\n"); GetLogger()->Message(" environment variable: COLOBOT_LANG_DIR\n"); @@ -369,14 +369,14 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) m_sceneTest = true; break; } - case OPT_LOADGAME: + case OPT_LOADSAVE: { std::string playerAndPath = optarg; size_t pos = playerAndPath.find_first_of("/"); if (pos != std::string::npos && pos > 0 && pos+1 < playerAndPath.length()) // Split player name from path { - m_loadGamePlayerName = playerAndPath.substr(0, pos); - m_loadGameDirName = playerAndPath.substr(pos+1, playerAndPath.length()-pos-1); + m_loadSavePlayerName = playerAndPath.substr(0, pos); + m_loadSaveDirName = playerAndPath.substr(pos+1, playerAndPath.length()-pos-1); } else { @@ -717,10 +717,10 @@ bool CApplication::Create() StartLoadingMusic(); - if (!m_loadGameDirName.empty()) + if (!m_loadSaveDirName.empty()) { - m_controller->GetRobotMain()->SelectPlayer(m_loadGamePlayerName); - m_controller->GetRobotMain()->LoadGameFromDirName(m_loadGameDirName); + m_controller->GetRobotMain()->SelectPlayer(m_loadSavePlayerName); + m_controller->GetRobotMain()->LoadSaveFromDirName(m_loadSaveDirName); } else if (m_runSceneCategory == LevelCategory::Max) { diff --git a/src/app/app.h b/src/app/app.h index 2efe34a1..6868ea65 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -406,8 +406,8 @@ protected: //@} //! Game to load on startup - std::string m_loadGamePlayerName; - std::string m_loadGameDirName; + std::string m_loadSavePlayerName; + std::string m_loadSaveDirName; //! Scene test mode bool m_sceneTest; diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index a5cc4fdb..b829c80e 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -5839,7 +5839,7 @@ void CRobotMain::QuickLoad() m_playerProfile->LoadScene(dir); } -void CRobotMain::LoadGameFromDirName(const std::string& gameDir) +void CRobotMain::LoadSaveFromDirName(const std::string& gameDir) { std::string dir = m_playerProfile->GetSaveFile(gameDir); if(!CResourceManager::Exists(dir)) diff --git a/src/level/robotmain.h b/src/level/robotmain.h index a7f0206b..a90da679 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -369,7 +369,7 @@ public: void SetExitAfterMission(bool exit); //! Load saved game (used by command line argument) - void LoadGameFromDirName(const std::string& gameDir); + void LoadSaveFromDirName(const std::string& gameDir); //! Returns true if player can interact with things manually bool CanPlayerInteract(); From ee390bc27082d22ab5f66cc9cdc42b8b2ee66af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Tue, 8 Feb 2022 20:02:56 +0100 Subject: [PATCH 08/16] LoadFinishedLevels if needed in LoadScene --- src/level/player_profile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/level/player_profile.cpp b/src/level/player_profile.cpp index a88d052f..028d3e55 100644 --- a/src/level/player_profile.cpp +++ b/src/level/player_profile.cpp @@ -525,7 +525,7 @@ void CPlayerProfile::LoadScene(std::string dir) CLevelParserLine* line = levelParser.Get("Mission"); cat = GetLevelCategoryFromDir(line->GetParam("base")->AsString()); - if (dir == "../../crashsave") + if(!m_levelInfoLoaded[cat]) LoadFinishedLevels(cat); rank = line->GetParam("rank")->AsInt(); From 2b8d580355f0b6745c5f148f7894e28e45bb2161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Tue, 8 Feb 2022 21:12:33 +0100 Subject: [PATCH 09/16] goto with Dial's algo (Dijkstra's + bucket sort) --- src/object/task/taskgoto.cpp | 95 ++++++++++++++++++++++++------------ src/object/task/taskgoto.h | 11 +++-- 2 files changed, 73 insertions(+), 33 deletions(-) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index 3e9a06f5..bb4203a1 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -1689,8 +1689,15 @@ void CTaskGoto::PathFindingInit() m_bmIter[i] = -1; } m_bmStep = 0; - m_bfsQueueBegin = 0; - m_bfsQueueEnd = 0; + for (auto& bucket : m_bfsQueue) + { + bucket.clear(); + } + m_bfsQueueMin = 0; + m_bfsQueueCountPushed = 0; + m_bfsQueueCountPopped = 0; + m_bfsQueueCountRepeated = 0; + m_bfsQueueCountSkipped = 0; } // Calculates points and passes to go from start to goal. @@ -1709,7 +1716,7 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector // Relative postion and distance to neighbors. static const int dXs[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; static const int dYs[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; - // static const float dDist[8] = {M_SQRT2, 1.0f, M_SQRT2, 1.0f, 1.0f, M_SQRT2, 1.0f, M_SQRT2}; + // These are the costs of the edges. They must be less than the number of buckets in the queue. static const int32_t dDist[8] = {7, 5, 7, 5, 5, 7, 5, 7}; const int startX = static_cast((start.x+1600.0f)/BM_DIM_STEP); @@ -1717,7 +1724,7 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector const int goalX = static_cast((goal.x+1600.0f)/BM_DIM_STEP); const int goalY = static_cast((goal.z+1600.0f)/BM_DIM_STEP); - if (m_bfsQueueEnd == 0) // New search + if (m_bfsQueueCountPushed == 0) // New search { if (startX == goalX && startY == goalY) { @@ -1731,8 +1738,9 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector goalY >= 0 && goalY < m_bmSize ) { const int indexInMap = goalY * m_bmSize + goalX; - m_bfsDistances[indexInMap] = 0.0; - m_bfsQueue[m_bfsQueueEnd++] = indexInMap; + m_bfsDistances[indexInMap] = 0; + m_bfsQueue[0].push_back(indexInMap); + m_bfsQueueCountPushed += 1; BitmapSetDot(1, goalX, goalY); // Mark as enqueued } @@ -1754,8 +1762,9 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector !BitmapTestDot(1, x, y)) { const int indexInMap = y * m_bmSize + x; - m_bfsDistances[indexInMap] = 0.0; - m_bfsQueue[m_bfsQueueEnd++] = indexInMap; + m_bfsDistances[indexInMap] = 0; + m_bfsQueue[0].push_back(indexInMap); + m_bfsQueueCountPushed += 1; BitmapSetDot(1, x, y); // Mark as enqueued } } @@ -1765,13 +1774,30 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector m_bmIterCounter = 0; - while (m_bfsQueueBegin != m_bfsQueueEnd) + while (m_bfsQueueCountPushed != m_bfsQueueCountPopped) { // Pop a node from the queue - const uint32_t indexInMap = m_bfsQueue[m_bfsQueueBegin++]; + while (m_bfsQueue[m_bfsQueueMin % 8].empty()) + { + m_bfsQueueMin += 1; + } + auto& bucket = m_bfsQueue[m_bfsQueueMin % 8]; + const uint32_t indexInMap = bucket.back(); + bucket.pop_back(); + m_bfsQueueCountPopped += 1; + + const int32_t distance = m_bfsDistances[indexInMap]; + + if (distance != m_bfsQueueMin) + { + m_bfsQueueCountSkipped += 1; + GetLogger()->Debug("Skipping node with mismatched distance, distance: %d, m_bfsQueueMin: %d\n", + distance, m_bfsQueueMin); + continue; + } + const int x = indexInMap % m_bmSize; const int y = indexInMap / m_bmSize; - const int32_t distance = m_bfsDistances[indexInMap]; if (x == startX && y == startY) { @@ -1800,7 +1826,7 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector } if (bestX == -1) { - GetLogger()->Error("Failed to find node parent\n"); + GetLogger()->Debug("Failed to find node parent\n"); return ERR_GOTO_ITER; } btX = bestX; @@ -1820,7 +1846,15 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector } // std::reverse(m_bmPoints, m_bmPoints + m_bmTotal); - GetLogger()->Info("Found path to goal with %d nodes\n", m_bmTotal + 1); + GetLogger()->Debug("Found path to goal with %d nodes\n", m_bmTotal + 1); + GetLogger()->Debug("m_bmStep: %d\n", m_bmStep); + GetLogger()->Debug("m_bfsQueueMin: %d mod 8 = %d\n", m_bfsQueueMin, m_bfsQueueMin % 8); + GetLogger()->Debug("m_bfsQueueCountPushed: %d\n", m_bfsQueueCountPushed); + GetLogger()->Debug("m_bfsQueueCountPopped: %d\n", m_bfsQueueCountPopped); + GetLogger()->Debug("m_bfsQueueCountRepeated: %d\n", m_bfsQueueCountRepeated); + GetLogger()->Debug("m_bfsQueueCountSkipped: %d\n", m_bfsQueueCountSkipped); + GetLogger()->Debug("m_bfsQueue sizes:\n 0: %lu\n 1: %lu\n 2: %lu\n 3: %lu\n 4: %lu\n 5: %lu\n 6: %lu\n 7: %lu\n", + m_bfsQueue[0].size(), m_bfsQueue[1].size(), m_bfsQueue[2].size(), m_bfsQueue[3].size(), m_bfsQueue[4].size(), m_bfsQueue[5].size(), m_bfsQueue[6].size(), m_bfsQueue[7].size()); return ERR_OK; } @@ -1831,28 +1865,26 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector const int nY = y + dYs[i]; if (BitmapTestDotIsVisitable(nX, nY)) { + const int neighborIndexInMap = nY * m_bmSize + nX; + const int32_t newDistance = distance + dDist[i]; if (BitmapTestDot(1, nX, nY)) { // We have seen this node before. - // Update distance without adding it to the queue. - const int neighborIndexInMap = nY * m_bmSize + nX; - m_bfsDistances[neighborIndexInMap] = std::min( - m_bfsDistances[neighborIndexInMap], - distance + dDist[i]); - } - else - { - // Enqueue this neighbor - const int neighborIndexInMap = nY * m_bmSize + nX; - m_bfsDistances[neighborIndexInMap] = distance + dDist[i]; - m_bfsQueue[m_bfsQueueEnd++] = neighborIndexInMap; - BitmapSetDot(1, nX, nY); // Mark as enqueued - if (m_bfsQueueEnd > m_bmSize * m_bmSize) + // Only enqueue previously seen nodes if this is a shorter path. + if (newDistance < m_bfsDistances[neighborIndexInMap]) { - GetLogger()->Error("Queue is full\n"); - return ERR_GOTO_ITER; + m_bfsQueueCountRepeated += 1; + } + else + { + continue; } } + // Enqueue this neighbor + m_bfsDistances[neighborIndexInMap] = newDistance; + m_bfsQueue[newDistance % 8].push_back(neighborIndexInMap); + m_bfsQueueCountPushed += 1; + BitmapSetDot(1, nX, nY); // Mark as enqueued } } @@ -2140,7 +2172,10 @@ bool CTaskGoto::BitmapOpen() if (m_bmArray.get() == nullptr) m_bmArray = MakeUniqueArray(m_bmSize*m_bmSize/8*2); memset(m_bmArray.get(), 0, m_bmSize*m_bmSize/8*2); if (m_bfsDistances.get() == nullptr) m_bfsDistances = MakeUniqueArray(m_bmSize*m_bmSize); - if (m_bfsQueue.get() == nullptr) m_bfsQueue = MakeUniqueArray(m_bmSize*m_bmSize); + for (auto& bucket : m_bfsQueue) + { + bucket.reserve(256); + } m_bmChanged = true; m_bmOffset = m_bmSize/2; diff --git a/src/object/task/taskgoto.h b/src/object/task/taskgoto.h index 26bc52e3..a95b73e4 100644 --- a/src/object/task/taskgoto.h +++ b/src/object/task/taskgoto.h @@ -23,7 +23,9 @@ #include "math/vector.h" +#include #include +#include namespace Math { @@ -142,9 +144,12 @@ protected: int m_bmLine = 0; // increment line m_bmSize/8 std::unique_ptr m_bmArray; // Bit table std::unique_ptr m_bfsDistances; // Distances to the goal for breadth-first search. - std::unique_ptr m_bfsQueue; // Nodes in the queue. Stored as indices. - int m_bfsQueueBegin = 0; // Front of the queue. This is the next node to be expanded. - int m_bfsQueueEnd = 0; // Back of the queue. This is where nodes are inserted. + std::array, 8> m_bfsQueue; // Priority queue with indices to nodes. Nodes are sorted into buckets. + int m_bfsQueueMin = 0; // Front of the queue. This value mod 8 is the index to the bucket with the next node to be expanded. + int m_bfsQueueCountPushed = 0; // Number of nodes inserted into the queue. + int m_bfsQueueCountPopped = 0; // Number of nodes extacted from the queue. + int m_bfsQueueCountRepeated = 0; // Number of nodes re-inserted into the queue. + int m_bfsQueueCountSkipped = 0; // Number of nodes skipped because of unexpected distance (likely re-added). int m_bmMinX = 0, m_bmMinY = 0; int m_bmMaxX = 0, m_bmMaxY = 0; int m_bmTotal = 0; // index of final point in m_bmPoints From e38835cfd467e4e43be35b88e254b97db2e1759a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Sat, 12 Feb 2022 00:07:52 +0100 Subject: [PATCH 10/16] goto with A-star (with bucket queue) --- src/object/task/taskgoto.cpp | 71 ++++++++++++++++++++++++++---------- src/object/task/taskgoto.h | 4 +- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index bb4203a1..038c03fa 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -1700,6 +1700,25 @@ void CTaskGoto::PathFindingInit() m_bfsQueueCountSkipped = 0; } +static int HeuristicDistance(int nX, int nY, int startX, int startY) +{ + // 8-way connectivity yields a shortest path that + // consists of a diagonal and a non-diagonal part. + // ...+ + // : | + // :..| + // : /: + // :/ : + // +..: + const int distX = std::abs(nX - startX); + const int distY = std::abs(nY - startY); + const int smaller = std::min(distX, distY); + const int bigger = std::max(distX, distY); + // diagonal number of steps: smaller + // non-diagonal number of steps: bigger - smaller + return smaller * (7 - 5) + bigger * 5; +} + // Calculates points and passes to go from start to goal. // Returns: // ERR_OK if it's good @@ -1738,11 +1757,17 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector goalY >= 0 && goalY < m_bmSize ) { const int indexInMap = goalY * m_bmSize + goalX; + const int totalDistance = HeuristicDistance(goalX, goalY, startX, startY); + m_bfsQueueMin = totalDistance; m_bfsDistances[indexInMap] = 0; - m_bfsQueue[0].push_back(indexInMap); + m_bfsQueue[totalDistance % NUMQUEUEBUCKETS].push_back(indexInMap); m_bfsQueueCountPushed += 1; BitmapSetDot(1, goalX, goalY); // Mark as enqueued } + else + { + m_bfsQueueMin = std::numeric_limits::max(); + } // Enqueue nodes around the goal if (goalRadius > 0.0f) @@ -1762,8 +1787,10 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector !BitmapTestDot(1, x, y)) { const int indexInMap = y * m_bmSize + x; + const int totalDistance = HeuristicDistance(x, y, startX, startY); + m_bfsQueueMin = std::min(m_bfsQueueMin, totalDistance); m_bfsDistances[indexInMap] = 0; - m_bfsQueue[0].push_back(indexInMap); + m_bfsQueue[totalDistance % NUMQUEUEBUCKETS].push_back(indexInMap); m_bfsQueueCountPushed += 1; BitmapSetDot(1, x, y); // Mark as enqueued } @@ -1777,27 +1804,27 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector while (m_bfsQueueCountPushed != m_bfsQueueCountPopped) { // Pop a node from the queue - while (m_bfsQueue[m_bfsQueueMin % 8].empty()) + while (m_bfsQueue[m_bfsQueueMin % NUMQUEUEBUCKETS].empty()) { m_bfsQueueMin += 1; } - auto& bucket = m_bfsQueue[m_bfsQueueMin % 8]; + auto& bucket = m_bfsQueue[m_bfsQueueMin % NUMQUEUEBUCKETS]; const uint32_t indexInMap = bucket.back(); bucket.pop_back(); m_bfsQueueCountPopped += 1; - const int32_t distance = m_bfsDistances[indexInMap]; - - if (distance != m_bfsQueueMin) - { - m_bfsQueueCountSkipped += 1; - GetLogger()->Debug("Skipping node with mismatched distance, distance: %d, m_bfsQueueMin: %d\n", - distance, m_bfsQueueMin); - continue; - } - const int x = indexInMap % m_bmSize; const int y = indexInMap / m_bmSize; + const int32_t distance = m_bfsDistances[indexInMap]; + const int totalDistance = distance + HeuristicDistance(x, y, startX, startY); + + if (totalDistance != m_bfsQueueMin) + { + m_bfsQueueCountSkipped += 1; + GetLogger()->Debug("Skipping node with mismatched distance, distance: %d, totalDistance: %d, m_bfsQueueMin: %d\n", + distance, totalDistance, m_bfsQueueMin); + continue; + } if (x == startX && y == startY) { @@ -1844,17 +1871,19 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector break; } } - // std::reverse(m_bmPoints, m_bmPoints + m_bmTotal); - GetLogger()->Debug("Found path to goal with %d nodes\n", m_bmTotal + 1); + GetLogger()->Debug("Found path to goal with %d nodes and %d cost\n", m_bmTotal + 1, totalDistance); GetLogger()->Debug("m_bmStep: %d\n", m_bmStep); - GetLogger()->Debug("m_bfsQueueMin: %d mod 8 = %d\n", m_bfsQueueMin, m_bfsQueueMin % 8); + GetLogger()->Debug("m_bfsQueueMin: %d mod %d = %d\n", m_bfsQueueMin, NUMQUEUEBUCKETS, m_bfsQueueMin % NUMQUEUEBUCKETS); GetLogger()->Debug("m_bfsQueueCountPushed: %d\n", m_bfsQueueCountPushed); GetLogger()->Debug("m_bfsQueueCountPopped: %d\n", m_bfsQueueCountPopped); GetLogger()->Debug("m_bfsQueueCountRepeated: %d\n", m_bfsQueueCountRepeated); GetLogger()->Debug("m_bfsQueueCountSkipped: %d\n", m_bfsQueueCountSkipped); - GetLogger()->Debug("m_bfsQueue sizes:\n 0: %lu\n 1: %lu\n 2: %lu\n 3: %lu\n 4: %lu\n 5: %lu\n 6: %lu\n 7: %lu\n", - m_bfsQueue[0].size(), m_bfsQueue[1].size(), m_bfsQueue[2].size(), m_bfsQueue[3].size(), m_bfsQueue[4].size(), m_bfsQueue[5].size(), m_bfsQueue[6].size(), m_bfsQueue[7].size()); + GetLogger()->Debug("m_bfsQueue sizes:\n"); + for (size_t i = 0; i < m_bfsQueue.size(); ++i) + { + if (!m_bfsQueue[i].empty()) GetLogger()->Debug(" %lu: %lu\n", i, m_bfsQueue[i].size()); + } return ERR_OK; } @@ -1880,9 +1909,11 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector continue; } } + // Enqueue this neighbor + const int32_t newTotalDistance = newDistance + HeuristicDistance(nX, nY, startX, startY); m_bfsDistances[neighborIndexInMap] = newDistance; - m_bfsQueue[newDistance % 8].push_back(neighborIndexInMap); + m_bfsQueue[newTotalDistance % NUMQUEUEBUCKETS].push_back(neighborIndexInMap); m_bfsQueueCountPushed += 1; BitmapSetDot(1, nX, nY); // Mark as enqueued } diff --git a/src/object/task/taskgoto.h b/src/object/task/taskgoto.h index a95b73e4..f5131fa5 100644 --- a/src/object/task/taskgoto.h +++ b/src/object/task/taskgoto.h @@ -36,7 +36,7 @@ struct Point; class CObject; const int MAXPOINTS = 50000; - +const int NUMQUEUEBUCKETS = 32; enum TaskGotoGoal { @@ -144,7 +144,7 @@ protected: int m_bmLine = 0; // increment line m_bmSize/8 std::unique_ptr m_bmArray; // Bit table std::unique_ptr m_bfsDistances; // Distances to the goal for breadth-first search. - std::array, 8> m_bfsQueue; // Priority queue with indices to nodes. Nodes are sorted into buckets. + std::array, NUMQUEUEBUCKETS> m_bfsQueue; // Priority queue with indices to nodes. Nodes are sorted into buckets. int m_bfsQueueMin = 0; // Front of the queue. This value mod 8 is the index to the bucket with the next node to be expanded. int m_bfsQueueCountPushed = 0; // Number of nodes inserted into the queue. int m_bfsQueueCountPopped = 0; // Number of nodes extacted from the queue. From 2d794d1a32f7d8b8922296d31460c8e66fd75925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Sat, 12 Feb 2022 14:27:23 +0100 Subject: [PATCH 11/16] goto: Handle oversized costs with a special bucket This is a bit redundant because the current usage of goto does not trigger it. It can be triggered by: * increasing goalRadius * decreasing NUMQUEUEBUCKETS * decreasing BM_DIM_STEP * increasing edge costs --- src/object/task/taskgoto.cpp | 57 ++++++++++++++++++++++++++++++++++-- src/object/task/taskgoto.h | 2 +- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index 038c03fa..4a23c0e0 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -1807,6 +1807,34 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector while (m_bfsQueue[m_bfsQueueMin % NUMQUEUEBUCKETS].empty()) { m_bfsQueueMin += 1; + if (m_bfsQueueMin % NUMQUEUEBUCKETS == 0 && !m_bfsQueue[NUMQUEUEBUCKETS].empty()) + { + // Process nodes with oversized costs. + const size_t countBefore = m_bfsQueue[NUMQUEUEBUCKETS].size(); + for (size_t i = 0; i < m_bfsQueue[NUMQUEUEBUCKETS].size();) + { + const uint32_t indexInMap = m_bfsQueue[NUMQUEUEBUCKETS][i]; + const int x = indexInMap % m_bmSize; + const int y = indexInMap / m_bmSize; + const int32_t distance = m_bfsDistances[indexInMap]; + const int totalDistance = distance + HeuristicDistance(x, y, startX, startY); + if (totalDistance < m_bfsQueueMin + NUMQUEUEBUCKETS) + { + // Move node to a regular bucket. + m_bfsQueue[totalDistance % NUMQUEUEBUCKETS].push_back(indexInMap); + m_bfsQueue[NUMQUEUEBUCKETS][i] = m_bfsQueue[NUMQUEUEBUCKETS].back(); + m_bfsQueue[NUMQUEUEBUCKETS].pop_back(); + } + else + { + // Look at next node. + i += 1; + } + } + const size_t countAfter = m_bfsQueue[NUMQUEUEBUCKETS].size(); + GetLogger()->Debug("Redistributed %lu of %lu nodes from the bucket with oversized costs.\n", + countBefore - countAfter, countBefore); + } } auto& bucket = m_bfsQueue[m_bfsQueueMin % NUMQUEUEBUCKETS]; const uint32_t indexInMap = bucket.back(); @@ -1820,9 +1848,32 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector if (totalDistance != m_bfsQueueMin) { - m_bfsQueueCountSkipped += 1; - GetLogger()->Debug("Skipping node with mismatched distance, distance: %d, totalDistance: %d, m_bfsQueueMin: %d\n", - distance, totalDistance, m_bfsQueueMin); + if (totalDistance < m_bfsQueueMin) + { + // This node has been updated to a lower cost and has allready been processed. + m_bfsQueueCountSkipped += 1; + // GetLogger()->Debug("Skipping node with smaller distance, distance: %d, totalDistance: %d, m_bfsQueueMin: %d\n", + // distance, totalDistance, m_bfsQueueMin); + } + else + { + if (totalDistance < m_bfsQueueMin + NUMQUEUEBUCKETS) + { + // Move node to a regular bucket. + m_bfsQueue[totalDistance % NUMQUEUEBUCKETS].push_back(indexInMap); + m_bfsQueueCountPushed += 1; + GetLogger()->Debug("Moving node with bigger distance into regular bucket, distance: %d, totalDistance: %d, m_bfsQueueMin: %d\n", + distance, totalDistance, m_bfsQueueMin); + } + else + { + // Move node to the bucket with oversized costs. + m_bfsQueue[NUMQUEUEBUCKETS].push_back(indexInMap); + m_bfsQueueCountPushed += 1; + GetLogger()->Debug("Moving node with bigger distance into bucket with oversized costs, distance: %d, totalDistance: %d, m_bfsQueueMin: %d\n", + distance, totalDistance, m_bfsQueueMin); + } + } continue; } diff --git a/src/object/task/taskgoto.h b/src/object/task/taskgoto.h index f5131fa5..e502c7c8 100644 --- a/src/object/task/taskgoto.h +++ b/src/object/task/taskgoto.h @@ -144,7 +144,7 @@ protected: int m_bmLine = 0; // increment line m_bmSize/8 std::unique_ptr m_bmArray; // Bit table std::unique_ptr m_bfsDistances; // Distances to the goal for breadth-first search. - std::array, NUMQUEUEBUCKETS> m_bfsQueue; // Priority queue with indices to nodes. Nodes are sorted into buckets. + std::array, NUMQUEUEBUCKETS + 1> m_bfsQueue; // Priority queue with indices to nodes. Nodes are sorted into buckets. The last bucket contains oversized costs. int m_bfsQueueMin = 0; // Front of the queue. This value mod 8 is the index to the bucket with the next node to be expanded. int m_bfsQueueCountPushed = 0; // Number of nodes inserted into the queue. int m_bfsQueueCountPopped = 0; // Number of nodes extacted from the queue. From 3478ee322b9a3b03018562efe7a67edc3a39e45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Tue, 15 Feb 2022 01:02:35 +0100 Subject: [PATCH 12/16] goto: Find a more exact end of path wrt goalRadius The last segment of the path is shortened to avoid going too close and risk bumping into the object that it was meant to approach. The same position could also be found by finding the roots of a second order polynomial analytically but this solution is simple and sufficient. --- src/object/task/taskgoto.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index 4a23c0e0..210fe30f 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -1912,18 +1912,42 @@ Error CTaskGoto::PathFindingSearch(const Math::Vector &start, const Math::Vector if (btX == goalX && btY == goalY) { m_bmPoints[m_bmTotal] = goal; - break; } - m_bmPoints[m_bmTotal].x = (btX + 0.5f) * BM_DIM_STEP - 1600.f; - m_bmPoints[m_bmTotal].z = (btY + 0.5f) * BM_DIM_STEP - 1600.f; + else + { + m_bmPoints[m_bmTotal].x = (btX + 0.5f) * BM_DIM_STEP - 1600.f; + m_bmPoints[m_bmTotal].z = (btY + 0.5f) * BM_DIM_STEP - 1600.f; + } if (bestDistance == 0) { + if (goalRadius > 0.0f) + { + // Find a more exact position by repeatedly bisecting the interval. + const float r2 = goalRadius * goalRadius; + Math::Vector inside = m_bmPoints[m_bmTotal] - goal; + Math::Vector outside = m_bmPoints[m_bmTotal-1] - goal; + Math::Vector mid = (inside + outside) * 0.5f; + for (int i = 0; i < 10; ++i) + { + if (mid.x*mid.x + mid.z*mid.z < r2) + { + inside = mid; + } + else + { + outside = mid; + } + mid = (inside + outside) * 0.5f; + } + m_bmPoints[m_bmTotal] = mid + goal; + } break; } } - GetLogger()->Debug("Found path to goal with %d nodes and %d cost\n", m_bmTotal + 1, totalDistance); + const float distanceToGoal = DistanceProjected(m_bmPoints[m_bmTotal], goal); + GetLogger()->Debug("Found path to goal with %d nodes and %d cost. Final distance to goal: %f\n", m_bmTotal + 1, totalDistance, distanceToGoal); GetLogger()->Debug("m_bmStep: %d\n", m_bmStep); GetLogger()->Debug("m_bfsQueueMin: %d mod %d = %d\n", m_bfsQueueMin, NUMQUEUEBUCKETS, m_bfsQueueMin % NUMQUEUEBUCKETS); GetLogger()->Debug("m_bfsQueueCountPushed: %d\n", m_bfsQueueCountPushed); From 8af600692e97285de4ccc8f1a01395c3b564568a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Thu, 17 Feb 2022 23:40:07 +0100 Subject: [PATCH 13/16] goto: Reduce NB_ITER back to 200 --- src/object/task/taskgoto.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index 210fe30f..2f1b48fe 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -52,7 +52,7 @@ const float BEAM_ACCURACY = 5.0f; // higher value = more accurate, but slow const float SAFETY_MARGIN = 1.5f; // Smallest distance between two objects. Smaller = less "no route to destination", but higher probability of collisions between objects. // Changing SAFETY_MARGIN (old value was 4.0f) seems to have fixed many issues with goto(). TODO: maybe we could make it even smaller? Did changing it introduce any new bugs? -const int NB_ITER = 2000; // Maximum number of iterations you have the right to make before temporarily interrupt in order not to lower the framerate. +const int NB_ITER = 200; // Maximum number of iterations you have the right to make before temporarily interrupt in order not to lower the framerate. From 0829cd84feabcbb88c1b640a17cc0f632d68b48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Br=C3=B6nneg=C3=A5rd?= <1162652+rasmusgo@users.noreply.github.com> Date: Thu, 17 Feb 2022 23:58:32 +0100 Subject: [PATCH 14/16] goto: Remove unused BEAM_ACCURACY --- src/object/task/taskgoto.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index 2f1b48fe..3f01ea7a 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -48,7 +48,6 @@ const float FLY_DEF_HEIGHT = 50.0f; // default flying height // Settings that define goto() accuracy: const float BM_DIM_STEP = 5.0f; // Size of one pixel on the bitmap. Setting 5 means that 5x5 square (in game units) will be represented by 1 px on the bitmap. Decreasing this value will make a bigger bitmap, and may increase accuracy. TODO: Check how it actually impacts goto() accuracy -const float BEAM_ACCURACY = 5.0f; // higher value = more accurate, but slower const float SAFETY_MARGIN = 1.5f; // Smallest distance between two objects. Smaller = less "no route to destination", but higher probability of collisions between objects. // Changing SAFETY_MARGIN (old value was 4.0f) seems to have fixed many issues with goto(). TODO: maybe we could make it even smaller? Did changing it introduce any new bugs? From 73db861cc6e76e1e76797999e154031ed67dab2e Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Tue, 1 Mar 2022 17:35:57 +0100 Subject: [PATCH 15/16] Update data submodule --- data | 2 +- lib/googletest | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data b/data index 5d658744..9e27e420 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 5d658744641064e56cf489453dbf726964b2b98d +Subproject commit 9e27e420d8b3c0619f148fa2c7b254b51338c5d1 diff --git a/lib/googletest b/lib/googletest index e2239ee6..97a46757 160000 --- a/lib/googletest +++ b/lib/googletest @@ -1 +1 @@ -Subproject commit e2239ee6043f73722e7aa812a459f54a28552929 +Subproject commit 97a467571a0f615a4d96e79e4399c43221ca1232 From 8fb0f76f6f172caf241813a64e48e0e033a7d962 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Fri, 8 Apr 2022 19:22:39 -0700 Subject: [PATCH 16/16] Use https URLs for git submodules --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 0b5b4004..45f67340 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule "data"] path = data - url = git://github.com/colobot/colobot-data.git + url = https://github.com/colobot/colobot-data.git branch = . update = rebase [submodule "lib/googletest"] path = lib/googletest - url = git://github.com/google/googletest.git + url = https://github.com/google/googletest.git ignore = all