Merge branch 'dev' into dev-graphics-overhaul

# Conflicts:
#	src/level/player_profile.cpp
#	src/object/task/taskgoto.cpp
#	src/object/task/taskgoto.h
#	src/ui/mainui.cpp
dev
Tomasz Kapuściński 2022-04-23 11:48:09 +02:00
commit daa9419557
17 changed files with 581 additions and 332 deletions

4
.gitmodules vendored
View File

@ -1,11 +1,11 @@
[submodule "data"] [submodule "data"]
path = data path = data
url = git://github.com/colobot/colobot-data.git url = https://github.com/colobot/colobot-data.git
branch = . branch = .
update = rebase update = rebase
[submodule "lib/googletest"] [submodule "lib/googletest"]
path = lib/googletest path = lib/googletest
url = git://github.com/google/googletest.git url = https://github.com/google/googletest.git
ignore = all ignore = all
[submodule "lib/json"] [submodule "lib/json"]
path = lib/json path = lib/json

@ -1 +1 @@
Subproject commit e2239ee6043f73722e7aa812a459f54a28552929 Subproject commit 97a467571a0f615a4d96e79e4399c43221ca1232

View File

@ -435,8 +435,8 @@ add_library(colobotbase STATIC
ui/particles_generator.h ui/particles_generator.h
ui/screen/screen.cpp ui/screen/screen.cpp
ui/screen/screen.h ui/screen/screen.h
ui/screen/screen_apperance.cpp ui/screen/screen_appearance.cpp
ui/screen/screen_apperance.h ui/screen/screen_appearance.h
ui/screen/screen_io.cpp ui/screen/screen_io.cpp
ui/screen/screen_io.h ui/screen/screen_io.h
ui/screen/screen_io_read.cpp ui/screen/screen_io_read.cpp

View File

@ -244,6 +244,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
OPT_DEBUG, OPT_DEBUG,
OPT_RUNSCENE, OPT_RUNSCENE,
OPT_SCENETEST, OPT_SCENETEST,
OPT_LOADSAVE,
OPT_LOGLEVEL, OPT_LOGLEVEL,
OPT_LANGDIR, OPT_LANGDIR,
OPT_DATADIR, OPT_DATADIR,
@ -262,6 +263,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
{ "debug", required_argument, nullptr, OPT_DEBUG }, { "debug", required_argument, nullptr, OPT_DEBUG },
{ "runscene", required_argument, nullptr, OPT_RUNSCENE }, { "runscene", required_argument, nullptr, OPT_RUNSCENE },
{ "scenetest", no_argument, nullptr, OPT_SCENETEST }, { "scenetest", no_argument, nullptr, OPT_SCENETEST },
{ "loadsave", required_argument, nullptr, OPT_LOADSAVE },
{ "loglevel", required_argument, nullptr, OPT_LOGLEVEL }, { "loglevel", required_argument, nullptr, OPT_LOGLEVEL },
{ "langdir", required_argument, nullptr, OPT_LANGDIR }, { "langdir", required_argument, nullptr, OPT_LANGDIR },
{ "datadir", required_argument, nullptr, OPT_DATADIR }, { "datadir", required_argument, nullptr, OPT_DATADIR },
@ -307,6 +309,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(" -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(" -runscene sceneNNN run given scene on start\n");
GetLogger()->Message(" -scenetest win every mission right after it's loaded\n"); GetLogger()->Message(" -scenetest win every mission right after it's loaded\n");
GetLogger()->Message(" -loadsave path load given saved game on start (path is <player>/<save>\n");
GetLogger()->Message(" -loglevel level set log level to level (one of: trace, debug, info, warn, error, none)\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(" -langdir path set custom language directory path\n");
GetLogger()->Message(" environment variable: COLOBOT_LANG_DIR\n"); GetLogger()->Message(" environment variable: COLOBOT_LANG_DIR\n");
@ -364,6 +367,22 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
m_sceneTest = true; m_sceneTest = true;
break; break;
} }
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_loadSavePlayerName = playerAndPath.substr(0, pos);
m_loadSaveDirName = playerAndPath.substr(pos+1, playerAndPath.length()-pos-1);
}
else
{
GetLogger()->Error("Invalid path: '%s'\n", optarg);
return PARSE_ARGS_FAIL;
}
break;
}
case OPT_LOGLEVEL: case OPT_LOGLEVEL:
{ {
LogLevel logLevel; LogLevel logLevel;
@ -590,7 +609,7 @@ bool CApplication::Create()
h = atoi(hs.c_str()); 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) for (auto it = modes.begin(); it != modes.end(); ++it)
{ {
if (it->x == w && it->y == h) if (it->x == w && it->y == h)
@ -695,8 +714,15 @@ bool CApplication::Create()
StartLoadingMusic(); StartLoadingMusic();
if (m_runSceneCategory == LevelCategory::Max) if (!m_loadSaveDirName.empty())
{
m_controller->GetRobotMain()->SelectPlayer(m_loadSavePlayerName);
m_controller->GetRobotMain()->LoadSaveFromDirName(m_loadSaveDirName);
}
else if (m_runSceneCategory == LevelCategory::Max)
{
m_controller->StartApp(); m_controller->StartApp();
}
else else
{ {
m_controller->GetRobotMain()->UpdateCustomLevelList(); // To load the userlevels m_controller->GetRobotMain()->UpdateCustomLevelList(); // To load the userlevels
@ -1865,7 +1891,7 @@ void CApplication::SetLanguage(Language language)
} }
else 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; m_language = LANGUAGE_ENGLISH;
} }
} }
@ -1934,7 +1960,7 @@ void CApplication::SetLanguage(Language language)
} }
catch (...) 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 try
{ {
std::locale::global(std::locale::classic()); std::locale::global(std::locale::classic());

View File

@ -219,7 +219,7 @@ public:
//! Returns the relative time since last update [seconds] //! Returns the relative time since last update [seconds]
float GetRelTime() const; 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; long long GetExactRelTime() const;
//! Returns the exact relative time since last update disregarding speed setting [nanoseconds] //! Returns the exact relative time since last update disregarding speed setting [nanoseconds]
@ -406,13 +406,17 @@ protected:
int m_runSceneRank; int m_runSceneRank;
//@} //@}
//! Game to load on startup
std::string m_loadSavePlayerName;
std::string m_loadSaveDirName;
//! Scene test mode //! Scene test mode
bool m_sceneTest; bool m_sceneTest;
//! Application language //! Application language
Language m_language; Language m_language;
//! Screen resoultion overriden by commandline //! Screen resolution overriden by commandline
bool m_resolutionOverride; bool m_resolutionOverride;
//! Headles mode //! Headles mode

View File

@ -59,14 +59,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. 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 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, <b>TerranovaTeam</b>, The license was given specifically to our community, <b>TerranovaTeam</b>,
part of <b>International Colobot Community (ICC)</b> (previously known as <i>Polish Portal of Colobot (PPC)</i>; part of <b>International Colobot Community (ICC)</b> (previously known as <i>Polish Portal of Colobot (PPC)</i>;
Polish: <i>Polski Portal Colobota</i>) with our website at http://colobot.info/. Polish: <i>Polski Portal Colobota</i>) with our website at http://colobot.info/.
\section Intro Introduction \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. 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 Currently, the only documented classes are the ones written from scratch or the old ones rewritten
@ -91,7 +91,7 @@ The current layout is the following:
- src/graphics/opengl - concrete implementation of CDevice class in OpenGL: CGLDevice - 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/graphics/d3d - in (far) future - perhaps a newer implementation in DirectX (9? 10?)
- src/math - mathematical structures and functions - 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 - 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 <i>scene files</i> - src/level/parser - parser for loading/saving level files from format known as <i>scene files</i>
- src/ui - 2D user interface (menu, buttons, check boxes, etc.) - src/ui - 2D user interface (menu, buttons, check boxes, etc.)

View File

@ -32,7 +32,7 @@
#include "level/parser/parser.h" #include "level/parser/parser.h"
void PlayerApperance::DefPerso() void PlayerAppearance::DefPerso()
{ {
this->colorCombi.r = 206.0f/256.0f; this->colorCombi.r = 206.0f/256.0f;
this->colorCombi.g = 206.0f/256.0f; this->colorCombi.g = 206.0f/256.0f;
@ -75,7 +75,7 @@ void PlayerApperance::DefPerso()
this->colorBand.a = 0.0f; this->colorBand.a = 0.0f;
} }
void PlayerApperance::DefHairColor() void PlayerAppearance::DefHairColor()
{ {
if (this->face == 0) // normal ? if (this->face == 0) // normal ?
{ {
@ -121,7 +121,7 @@ CPlayerProfile::CPlayerProfile(std::string playerName)
m_levelInfoLoaded[static_cast<LevelCategory>(i)] = false; m_levelInfoLoaded[static_cast<LevelCategory>(i)] = false;
} }
LoadApperance(); LoadAppearance();
} }
CPlayerProfile::~CPlayerProfile() CPlayerProfile::~CPlayerProfile()
@ -391,17 +391,17 @@ void CPlayerProfile::SaveFreeGameUnlock()
file.close(); 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_appearance.face = 0;
m_apperance.DefPerso(); m_appearance.DefPerso();
std::string filename = GetSaveFile("face.gam"); std::string filename = GetSaveFile("face.gam");
if (!CResourceManager::Exists(filename)) if (!CResourceManager::Exists(filename))
@ -409,48 +409,48 @@ void CPlayerProfile::LoadApperance()
try try
{ {
CLevelParser apperanceParser(filename); CLevelParser appearanceParser(filename);
apperanceParser.Load(); appearanceParser.Load();
CLevelParserLine* line; CLevelParserLine* line;
line = apperanceParser.Get("Head"); line = appearanceParser.Get("Head");
m_apperance.face = line->GetParam("face")->AsInt(); m_appearance.face = line->GetParam("face")->AsInt();
m_apperance.glasses = line->GetParam("glasses")->AsInt(); m_appearance.glasses = line->GetParam("glasses")->AsInt();
m_apperance.colorHair = line->GetParam("hair")->AsColor(); m_appearance.colorHair = line->GetParam("hair")->AsColor();
line = apperanceParser.Get("Body"); line = appearanceParser.Get("Body");
m_apperance.colorCombi = line->GetParam("combi")->AsColor(); m_appearance.colorCombi = line->GetParam("combi")->AsColor();
m_apperance.colorBand = line->GetParam("band")->AsColor(); m_appearance.colorBand = line->GetParam("band")->AsColor();
} }
catch (CLevelParserException& e) 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 try
{ {
CLevelParser apperanceParser(GetSaveFile("face.gam")); CLevelParser appearanceParser(GetSaveFile("face.gam"));
CLevelParserLineUPtr line; CLevelParserLineUPtr line;
line = std::make_unique<CLevelParserLine>("Head"); line = std::make_unique<CLevelParserLine>("Head");
line->AddParam("face", std::make_unique<CLevelParserParam>(m_apperance.face)); line->AddParam("face", std::make_unique<CLevelParserParam>(m_appearance.face));
line->AddParam("glasses", std::make_unique<CLevelParserParam>(m_apperance.glasses)); line->AddParam("glasses", std::make_unique<CLevelParserParam>(m_appearance.glasses));
line->AddParam("hair", std::make_unique<CLevelParserParam>(m_apperance.colorHair)); line->AddParam("hair", std::make_unique<CLevelParserParam>(m_appearance.colorHair));
apperanceParser.AddLine(std::move(line)); appearanceParser.AddLine(std::move(line));
line = std::make_unique<CLevelParserLine>("Body"); line = std::make_unique<CLevelParserLine>("Body");
line->AddParam("combi", std::make_unique<CLevelParserParam>(m_apperance.colorCombi)); line->AddParam("combi", std::make_unique<CLevelParserParam>(m_appearance.colorCombi));
line->AddParam("band", std::make_unique<CLevelParserParam>(m_apperance.colorBand)); line->AddParam("band", std::make_unique<CLevelParserParam>(m_appearance.colorBand));
apperanceParser.AddLine(std::move(line)); appearanceParser.AddLine(std::move(line));
apperanceParser.Save(); appearanceParser.Save();
} }
catch (CLevelParserException& e) 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());
} }
} }
@ -524,7 +524,7 @@ void CPlayerProfile::LoadScene(std::string dir)
CLevelParserLine* line = levelParser.Get("Mission"); CLevelParserLine* line = levelParser.Get("Mission");
cat = GetLevelCategoryFromDir(line->GetParam("base")->AsString()); cat = GetLevelCategoryFromDir(line->GetParam("base")->AsString());
if (dir == "../../crashsave") if(!m_levelInfoLoaded[cat])
LoadFinishedLevels(cat); LoadFinishedLevels(cat);
rank = line->GetParam("rank")->AsInt(); rank = line->GetParam("rank")->AsInt();
@ -552,7 +552,7 @@ void CPlayerProfile::LoadScene(std::string dir)
} }
else else
{ {
// Backwards combatibility // Backwards compatibility
chap = rank/100; chap = rank/100;
rank = rank%100; rank = rank%100;
} }

View File

@ -33,7 +33,7 @@ struct LevelInfo
bool bPassed = false; bool bPassed = false;
}; };
struct PlayerApperance struct PlayerAppearance
{ {
int face = 0; // face int face = 0; // face
int glasses = 0; // glasses int glasses = 0; // glasses
@ -108,12 +108,12 @@ public:
//! Saves unlocked DoneResearch for free game //! Saves unlocked DoneResearch for free game
void SetFreeGameResearchUnlock(int freeResearch); void SetFreeGameResearchUnlock(int freeResearch);
//! Returns a reference to PlayerApperance structure //! Returns a reference to PlayerAppearance structure
PlayerApperance& GetApperance(); PlayerAppearance& GetAppearance();
//! Loads PlayerApperance structure //! Loads PlayerAppearance structure
void LoadApperance(); void LoadAppearance();
//! Saves PlayerApperance structure //! Saves PlayerAppearance structure
void SaveApperance(); void SaveAppearance();
//! Returns true if player has at least one saved scene //! Returns true if player has at least one saved scene
bool HasAnySavedScene(); bool HasAnySavedScene();
@ -157,6 +157,6 @@ protected:
//! Researches unlocked for free game //! Researches unlocked for free game
int m_freegameResearch; int m_freegameResearch;
//! Player apperance //! Player appearance
PlayerApperance m_apperance; PlayerAppearance m_appearance;
}; };

View File

@ -320,7 +320,7 @@ std::string PhaseToString(Phase phase)
if (phase == PHASE_WELCOME2) return "PHASE_WELCOME2"; if (phase == PHASE_WELCOME2) return "PHASE_WELCOME2";
if (phase == PHASE_WELCOME3) return "PHASE_WELCOME3"; if (phase == PHASE_WELCOME3) return "PHASE_WELCOME3";
if (phase == PHASE_PLAYER_SELECT) return "PHASE_PLAYER_SELECT"; 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_MAIN_MENU) return "PHASE_MAIN_MENU";
if (phase == PHASE_LEVEL_LIST) return "PHASE_LEVEL_LIST"; if (phase == PHASE_LEVEL_LIST) return "PHASE_LEVEL_LIST";
if (phase == PHASE_MOD_LIST) return "PHASE_MOD_LIST"; if (phase == PHASE_MOD_LIST) return "PHASE_MOD_LIST";
@ -355,14 +355,14 @@ bool IsPhaseWithWorld(Phase phase)
if (phase == PHASE_SIMUL ) return true; if (phase == PHASE_SIMUL ) return true;
if (phase == PHASE_WIN ) return true; if (phase == PHASE_WIN ) return true;
if (phase == PHASE_LOST ) 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; if (IsInSimulationConfigPhase(phase)) return true;
return false; return false;
} }
bool IsMainMenuPhase(Phase phase) bool IsMainMenuPhase(Phase phase)
{ {
if (phase == PHASE_APPERANCE) return true; if (phase == PHASE_APPEARANCE) return true;
return !IsPhaseWithWorld(phase); return !IsPhaseWithWorld(phase);
} }
@ -534,7 +534,7 @@ void CRobotMain::ChangePhase(Phase phase)
float sx = (32.0f+2.0f)/640.0f; float sx = (32.0f+2.0f)/640.0f;
float sy = (32.0f+2.0f)/480.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->SetDrawWorld(true);
m_engine->SetDrawFront(false); m_engine->SetDrawFront(false);
@ -1165,7 +1165,7 @@ bool CRobotMain::ProcessEvent(Event &event)
return false; return false;
} }
if (m_phase == PHASE_APPERANCE) if (m_phase == PHASE_APPEARANCE)
EventObject(event); EventObject(event);
if (m_phase == PHASE_WIN || if (m_phase == PHASE_WIN ||
@ -2100,7 +2100,7 @@ void CRobotMain::HiliteClear()
//! Highlights the object with the mouse hovers over //! Highlights the object with the mouse hovers over
void CRobotMain::HiliteObject(const glm::vec2& pos) void CRobotMain::HiliteObject(const glm::vec2& pos)
{ {
if (m_fixScene && m_phase != PHASE_APPERANCE) return; if (m_fixScene && m_phase != PHASE_APPEARANCE) return;
if (m_movieLock) return; if (m_movieLock) return;
if (m_movie->IsExist()) return; if (m_movie->IsExist()) return;
if (m_app->GetMouseMode() == MOUSE_NONE) return; if (m_app->GetMouseMode() == MOUSE_NONE) return;
@ -2166,7 +2166,7 @@ void CRobotMain::HiliteObject(const glm::vec2& pos)
//! Highlights the object with the mouse hovers over //! Highlights the object with the mouse hovers over
void CRobotMain::HiliteFrame(float rTime) 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_movieLock) return;
if (m_movie->IsExist()) return; if (m_movie->IsExist()) return;
@ -2451,7 +2451,7 @@ bool CRobotMain::EventFrame(const Event &event)
if (m_engine->GetFog()) if (m_engine->GetFog())
m_camera->SetOverBaseColor(m_particle->GetFogColor(m_engine->GetEyePt())); 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_WIN ||
m_phase == PHASE_LOST) m_phase == PHASE_LOST)
{ {
@ -2724,7 +2724,7 @@ void CRobotMain::ScenePerso()
} }
catch (const std::runtime_error& e) 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 m_engine->SetDrawWorld(false); // does not draw anything on the interface
@ -3946,7 +3946,7 @@ void CRobotMain::ChangeColor()
m_phase != PHASE_MOD_LIST && m_phase != PHASE_MOD_LIST &&
m_phase != PHASE_WIN && m_phase != PHASE_WIN &&
m_phase != PHASE_LOST && m_phase != PHASE_LOST &&
m_phase != PHASE_APPERANCE ) return; m_phase != PHASE_APPEARANCE ) return;
// Player texture // Player texture
@ -3961,11 +3961,11 @@ void CRobotMain::ChangeColor()
colorRef1.r = 206.0f/256.0f; colorRef1.r = 206.0f/256.0f;
colorRef1.g = 206.0f/256.0f; colorRef1.g = 206.0f/256.0f;
colorRef1.b = 204.0f/256.0f; // ~white colorRef1.b = 204.0f/256.0f; // ~white
colorNew1 = m_playerProfile->GetApperance().colorCombi; colorNew1 = m_playerProfile->GetAppearance().colorCombi;
colorRef2.r = 255.0f/256.0f; colorRef2.r = 255.0f/256.0f;
colorRef2.g = 132.0f/256.0f; colorRef2.g = 132.0f/256.0f;
colorRef2.b = 1.0f/256.0f; // orange colorRef2.b = 1.0f/256.0f; // orange
colorNew2 = m_playerProfile->GetApperance().colorBand; colorNew2 = m_playerProfile->GetAppearance().colorBand;
glm::vec2 exclu[6]; glm::vec2 exclu[6];
exclu[0] = { 192.0f / 256.0f, 0.0f / 256.0f }; exclu[0] = { 192.0f / 256.0f, 0.0f / 256.0f };
@ -4007,7 +4007,7 @@ void CRobotMain::ChangeColor()
colorRef1.b = 0.0f/256.0f; // yellow colorRef1.b = 0.0f/256.0f; // yellow
tolerance = 0.20f; tolerance = 0.20f;
} }
colorNew1 = m_playerProfile->GetApperance().colorHair; colorNew1 = m_playerProfile->GetAppearance().colorHair;
colorRef2.r = 0.0f; colorRef2.r = 0.0f;
colorRef2.g = 0.0f; colorRef2.g = 0.0f;
colorRef2.b = 0.0f; colorRef2.b = 0.0f;
@ -5492,13 +5492,13 @@ MissionType CRobotMain::GetMissionType()
//! Returns the representation to use for the player //! Returns the representation to use for the player
int CRobotMain::GetGamerFace() int CRobotMain::GetGamerFace()
{ {
return m_playerProfile->GetApperance().face; return m_playerProfile->GetAppearance().face;
} }
//! Returns the representation to use for the player //! Returns the representation to use for the player
int CRobotMain::GetGamerGlasses() int CRobotMain::GetGamerGlasses()
{ {
return m_playerProfile->GetApperance().glasses; return m_playerProfile->GetAppearance().glasses;
} }
//! Returns the mode with just the head //! Returns the mode with just the head
@ -5878,6 +5878,17 @@ void CRobotMain::QuickLoad()
m_playerProfile->LoadScene(dir); m_playerProfile->LoadScene(dir);
} }
void CRobotMain::LoadSaveFromDirName(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) void CRobotMain::SetExitAfterMission(bool exit)
{ {
m_exitAfterMission = exit; m_exitAfterMission = exit;

View File

@ -56,7 +56,7 @@ enum Phase
PHASE_WELCOME2, PHASE_WELCOME2,
PHASE_WELCOME3, PHASE_WELCOME3,
PHASE_PLAYER_SELECT, PHASE_PLAYER_SELECT,
PHASE_APPERANCE, PHASE_APPEARANCE,
PHASE_MAIN_MENU, PHASE_MAIN_MENU,
PHASE_LEVEL_LIST, PHASE_LEVEL_LIST,
PHASE_MOD_LIST, PHASE_MOD_LIST,
@ -203,7 +203,7 @@ public:
Phase GetPhase(); Phase GetPhase();
//@} //@}
//! Load the scene for apperance customization //! Load the scene for appearance customization
void ScenePerso(); void ScenePerso();
void SetMovieLock(bool lock); void SetMovieLock(bool lock);
@ -373,6 +373,9 @@ public:
//! Enable mode where completing mission closes the game //! Enable mode where completing mission closes the game
void SetExitAfterMission(bool exit); void SetExitAfterMission(bool exit);
//! Load saved game (used by command line argument)
void LoadSaveFromDirName(const std::string& gameDir);
//! Returns true if player can interact with things manually //! Returns true if player can interact with things manually
bool CanPlayerInteract(); bool CanPlayerInteract();

View File

@ -48,10 +48,10 @@ const float FLY_DEF_HEIGHT = 50.0f; // default flying height
// Settings that define goto() accuracy: // 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 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.
const float SAFETY_MARGIN = 0.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? // 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 = 200; // Maximum number of iterations you have the right to make before temporarily interrupt in order not to lower the framerate.
@ -130,7 +130,7 @@ bool CTaskGoto::EventProcess(const Event &event)
if (a || b) if (a || b)
{ {
Gfx::Color c = Gfx::Color(0.0f, 0.0f, 0.0f, 1.0f); 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({ x, y }, c); debugImage->SetPixel({ x, y }, c);
} }
} }
@ -225,7 +225,7 @@ bool CTaskGoto::EventProcess(const Event &event)
if ( m_bmCargoObject->GetType() == OBJECT_BASE ) dist = 12.0f; if ( m_bmCargoObject->GetType() == OBJECT_BASE ) dist = 12.0f;
} }
ret = BeamSearch(pos, goal, dist); ret = PathFindingSearch(pos, goal, dist);
if ( ret == ERR_OK ) if ( ret == ERR_OK )
{ {
if ( m_physics->GetLand() ) m_phase = TGP_BEAMWCOLD; if ( m_physics->GetLand() ) m_phase = TGP_BEAMWCOLD;
@ -349,7 +349,7 @@ bool CTaskGoto::EventProcess(const Event &event)
{ {
m_physics->SetMotorSpeedX(0.0f); // stops the advance m_physics->SetMotorSpeedX(0.0f); // stops the advance
m_physics->SetMotorSpeedZ(0.0f); // stops the rotation m_physics->SetMotorSpeedZ(0.0f); // stops the rotation
BeamStart(); // we start all PathFindingStart(); // we start all
return true; return true;
} }
@ -793,7 +793,7 @@ Error CTaskGoto::Start(glm::vec3 goal, float altitude,
} }
} }
BeamStart(); PathFindingStart();
if ( m_bmCargoObject == nullptr ) if ( m_bmCargoObject == nullptr )
{ {
@ -829,7 +829,7 @@ Error CTaskGoto::IsEnded()
{ {
m_physics->SetMotorSpeedX(0.0f); // stops the advance m_physics->SetMotorSpeedX(0.0f); // stops the advance
m_physics->SetMotorSpeedZ(0.0f); // stops the rotation m_physics->SetMotorSpeedZ(0.0f); // stops the rotation
BeamInit(); PathFindingInit();
m_phase = TGP_BEAMSEARCH; // will seek the path m_phase = TGP_BEAMSEARCH; // will seek the path
} }
return ERR_CONTINUE; return ERR_CONTINUE;
@ -891,7 +891,7 @@ Error CTaskGoto::IsEnded()
m_physics->SetMotorSpeedX(0.0f); // stops the advance m_physics->SetMotorSpeedX(0.0f); // stops the advance
m_physics->SetMotorSpeedZ(0.0f); // stops the rotation m_physics->SetMotorSpeedZ(0.0f); // stops the rotation
m_bmIndex = BeamShortcut(); m_bmIndex = PathFindingShortcut();
if ( m_bmIndex > m_bmTotal ) if ( m_bmIndex > m_bmTotal )
{ {
@ -1631,13 +1631,13 @@ void CTaskGoto::ComputeFlyingRepulse(float &dir)
// Among all of the following, seek if there is one allowing to go directly to the crow flies. // 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. // If yes, skip all the unnecessary intermediate points.
int CTaskGoto::BeamShortcut() int CTaskGoto::PathFindingShortcut()
{ {
int i; int i;
for ( i=m_bmTotal ; i>=m_bmIndex+2 ; i-- ) // tries from the last 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 return i; // bingo, found
} }
@ -1648,7 +1648,7 @@ int CTaskGoto::BeamShortcut()
// That's the big start. // That's the big start.
void CTaskGoto::BeamStart() void CTaskGoto::PathFindingStart()
{ {
glm::vec3 min, max; glm::vec3 min, max;
@ -1674,14 +1674,14 @@ void CTaskGoto::BeamStart()
{ {
m_physics->SetMotorSpeedX(0.0f); // stops the advance m_physics->SetMotorSpeedX(0.0f); // stops the advance
m_physics->SetMotorSpeedZ(0.0f); // stops the rotation m_physics->SetMotorSpeedZ(0.0f); // stops the rotation
BeamInit(); PathFindingInit();
m_phase = TGP_BEAMSEARCH; // will seek the path 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; int i;
@ -1690,6 +1690,34 @@ void CTaskGoto::BeamInit()
m_bmIter[i] = -1; m_bmIter[i] = -1;
} }
m_bmStep = 0; m_bmStep = 0;
for (auto& bucket : m_bfsQueue)
{
bucket.clear();
}
m_bfsQueueMin = 0;
m_bfsQueueCountPushed = 0;
m_bfsQueueCountPopped = 0;
m_bfsQueueCountRepeated = 0;
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. // Calculates points and passes to go from start to goal.
@ -1700,199 +1728,350 @@ void CTaskGoto::BeamInit()
// ERR_CONTINUE if not done yet // ERR_CONTINUE if not done yet
// goalRadius: distance at which we must approach the goal // goalRadius: distance at which we must approach the goal
Error CTaskGoto::BeamSearch(const glm::vec3 &start, const glm::vec3 &goal, Error CTaskGoto::PathFindingSearch(const glm::vec3 &start, const glm::vec3 &goal,
float goalRadius) float goalRadius)
{ {
float step, len;
int nbIter;
m_bmStep ++; m_bmStep ++;
len = Math::DistanceProjected(start, goal); // Relative postion and distance to neighbors.
step = len/BEAM_ACCURACY; static const int dXs[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
if ( step < BM_DIM_STEP*2.1f ) step = BM_DIM_STEP*2.1f; static const int dYs[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
if ( step > 20.0f ) step = 20.0f; // These are the costs of the edges. They must be less than the number of buckets in the queue.
nbIter = 200; // in order not to lower the framerate static const int32_t dDist[8] = {7, 5, 7, 5, 5, 7, 5, 7};
m_bmIterCounter = 0;
return BeamExplore(start, start, goal, goalRadius, 165.0f*Math::PI/180.0f, 22, step, 0, nbIter);
}
// prevPos: previous position const int startX = static_cast<int>((start.x+1600.0f)/BM_DIM_STEP);
// curPos: current position const int startY = static_cast<int>((start.z+1600.0f)/BM_DIM_STEP);
// goalPos: position that seeks to achieve const int goalX = static_cast<int>((goal.x+1600.0f)/BM_DIM_STEP);
// angle: angle to the goal we explores const int goalY = static_cast<int>((goal.z+1600.0f)/BM_DIM_STEP);
// 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
Error CTaskGoto::BeamExplore(const glm::vec3 &prevPos, const glm::vec3 &curPos, if (m_bfsQueueCountPushed == 0) // New search
const glm::vec3 &goalPos, float goalRadius,
float angle, int nbDiv, float step,
int i, int nbIter)
{
glm::vec3 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 )
{ {
m_bmIter[i] = 0; if (startX == goalX && startY == goalY)
if ( i == 0 )
{ {
m_bmPoints[i] = curPos; m_bmPoints[0] = start;
m_bmPoints[1] = goal;
m_bmTotal = 1;
return ERR_OK;
}
// Enqueue the goal node
if ( goalX >= 0 && goalX < m_bmSize &&
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[totalDistance % NUMQUEUEBUCKETS].push_back(indexInMap);
m_bfsQueueCountPushed += 1;
BitmapSetDot(1, goalX, goalY); // Mark as enqueued
} }
else else
{ {
if ( !BitmapTestLine(prevPos, curPos, angle/nbDiv, true) ) return ERR_GOTO_IMPOSSIBLE; m_bfsQueueMin = std::numeric_limits<int>::max();
}
m_bmPoints[i] = curPos; // Enqueue nodes around the goal
if (goalRadius > 0.0f)
if ( Math::DistanceProjected(curPos, goalPos)-goalRadius <= step ) {
const int minX = std::max(0, static_cast<int>((goal.x-goalRadius+1600.0f)/BM_DIM_STEP));
const int minY = std::max(0, static_cast<int>((goal.z-goalRadius+1600.0f)/BM_DIM_STEP));
const int maxX = std::min(m_bmSize-1, static_cast<int>((goal.x+goalRadius+1600.0f)/BM_DIM_STEP));
const int maxY = std::min(m_bmSize-1, static_cast<int>((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; float floatX = (x + 0.5f) * BM_DIM_STEP - 1600.0f;
} float floatY = (y + 0.5f) * BM_DIM_STEP - 1600.0f;
else if (std::hypot(floatX-goal.x, floatY-goal.z) <= goalRadius &&
{ BitmapTestDotIsVisitable(x, y) &&
newPos = BeamPoint(curPos, goalPos, 0, Math::DistanceProjected(curPos, goalPos)-goalRadius); !BitmapTestDot(1, x, y))
} {
if ( BitmapTestLine(curPos, newPos, angle/nbDiv, false) ) const int indexInMap = y * m_bmSize + x;
{ const int totalDistance = HeuristicDistance(x, y, startX, startY);
m_bmPoints[i+1] = newPos; m_bfsQueueMin = std::min(m_bfsQueueMin, totalDistance);
m_bmTotal = i+1; m_bfsDistances[indexInMap] = 0;
return ERR_OK; m_bfsQueue[totalDistance % NUMQUEUEBUCKETS].push_back(indexInMap);
m_bfsQueueCountPushed += 1;
BitmapSetDot(1, x, y); // Mark as enqueued
}
} }
} }
} }
} }
if ( iLar >= m_bmIter[i] ) m_bmIterCounter = 0;
while (m_bfsQueueCountPushed != m_bfsQueueCountPopped)
{ {
newPos = BeamPoint(curPos, goalPos, 0, step); // Pop a node from the queue
ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); while (m_bfsQueue[m_bfsQueueMin % NUMQUEUEBUCKETS].empty())
if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; {
m_bmIter[i] = iLar+1; m_bfsQueueMin += 1;
for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -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();
bucket.pop_back();
m_bfsQueueCountPopped += 1;
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)
{
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;
}
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<int32_t>::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()->Debug("Failed to find node parent\n");
return ERR_GOTO_ITER;
}
btX = bestX;
btY = bestY;
if (btX == goalX && btY == goalY)
{
m_bmPoints[m_bmTotal] = goal;
}
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;
glm::vec3 inside = m_bmPoints[m_bmTotal] - goal;
glm::vec3 outside = m_bmPoints[m_bmTotal-1] - goal;
glm::vec3 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;
}
}
const float distanceToGoal = Math::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);
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");
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;
}
// 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))
{
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.
// Only enqueue previously seen nodes if this is a shorter path.
if (newDistance < m_bfsDistances[neighborIndexInMap])
{
m_bfsQueueCountRepeated += 1;
}
else
{
continue;
}
}
// Enqueue this neighbor
const int32_t newTotalDistance = newDistance + HeuristicDistance(nX, nY, startX, startY);
m_bfsDistances[neighborIndexInMap] = newDistance;
m_bfsQueue[newTotalDistance % NUMQUEUEBUCKETS].push_back(neighborIndexInMap);
m_bfsQueueCountPushed += 1;
BitmapSetDot(1, nX, nY); // Mark as enqueued
}
}
// Limit the number of iterations per frame.
m_bmIterCounter ++; m_bmIterCounter ++;
if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; if ( m_bmIterCounter >= NB_ITER ) 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 ++;
} }
return ERR_GOTO_IMPOSSIBLE; 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.
glm::vec3 CTaskGoto::BeamPoint(const glm::vec3 &startPoint,
const glm::vec3 &goalPoint,
float angle, float step)
{
glm::vec3 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. // Tests if a path along a straight line is possible.
bool CTaskGoto::BitmapTestLine(const glm::vec3 &start, const glm::vec3 &goal, bool CTaskGoto::BitmapTestLine(const glm::vec3 &start, const glm::vec3 &goal)
float stepAngle, bool bSecond)
{ {
glm::vec3 pos, inc;
float dist, step;
float distNoB2;
int i, max, x, y;
if ( m_bmArray == nullptr ) return true; if ( m_bmArray == nullptr ) return true;
dist = Math::DistanceProjected(start, goal); const glm::vec2 startInGrid = glm::vec2((start.x+1600.0f)/BM_DIM_STEP, (start.z+1600.0f)/BM_DIM_STEP);
if ( dist == 0.0f ) return true; const glm::vec2 goalInGrid = glm::vec2((goal.x+1600.0f)/BM_DIM_STEP, (goal.z+1600.0f)/BM_DIM_STEP);
step = BM_DIM_STEP*0.5f;
inc.x = (goal.x-start.x)*step/dist; const int startXInt = static_cast<int>(startInGrid.x);
inc.z = (goal.z-start.z)*step/dist; const int startYInt = static_cast<int>(startInGrid.y);
const int goalXInt = static_cast<int>(goalInGrid.x);
const int goalYInt = static_cast<int>(goalInGrid.y);
pos = start; if (startXInt == goalXInt && startYInt == goalYInt)
if ( bSecond )
{ {
x = static_cast<int>((pos.x+1600.0f)/BM_DIM_STEP); return true;
y = static_cast<int>((pos.z+1600.0f)/BM_DIM_STEP);
BitmapSetDot(1, x, y); // puts the flag as the starting point
} }
max = static_cast<int>(dist/step); // Grid traversal based on
if ( max == 0 ) max = 1; // Amanatides, John, and Andrew Woo. "A fast voxel traversal algorithm for ray tracing." Eurographics. Vol. 87. No. 3. 1987.
distNoB2 = BM_DIM_STEP*sqrtf(2.0f)/sinf(stepAngle); // http://www.cse.yorku.ca/~amana/research/grid.pdf
for ( i=0 ; i<max ; i++ )
glm::vec2 dirInGrid = goalInGrid - startInGrid;
dirInGrid /= std::hypot(dirInGrid.x, dirInGrid.y);
const int stepX = dirInGrid.x > 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<float>::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<float>::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<float>(stepX) / dirInGrid.x;
const float tDeltaY = static_cast<float>(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 else
{ {
pos.x += inc.x; tMaxY += tDeltaY;
pos.z += inc.z; y += stepY;
} }
if ( BitmapTestDot(0, x, y) )
x = static_cast<int>((pos.x+1600.0f)/BM_DIM_STEP);
y = static_cast<int>((pos.z+1600.0f)/BM_DIM_STEP);
if ( bSecond )
{ {
if ( i > 2 && BitmapTestDot(1, x, y) ) return false; return false;
if ( step*(i+1) > distNoB2 && i < max-2 )
{
BitmapSetDot(1, x, y);
}
} }
if ( BitmapTestDot(0, x, y) ) return false;
} }
return true; return true;
} }
@ -2096,10 +2275,14 @@ void CTaskGoto::BitmapTerrain(int minx, int miny, int maxx, int maxy)
bool CTaskGoto::BitmapOpen() bool CTaskGoto::BitmapOpen()
{ {
BitmapClose();
m_bmSize = static_cast<int>(3200.0f/BM_DIM_STEP); m_bmSize = static_cast<int>(3200.0f/BM_DIM_STEP);
m_bmArray = std::make_unique<unsigned char[]>(m_bmSize * m_bmSize / 8 * 2); if (m_bmArray.get() == nullptr) m_bmArray = std::make_unique<unsigned char[]>(m_bmSize * m_bmSize / 8 * 2);
memset(m_bmArray.get(), 0, m_bmSize*m_bmSize/8*2);
if (m_bfsDistances.get() == nullptr) m_bfsDistances = std::make_unique<int32_t[]>(m_bmSize * m_bmSize);
for (auto& bucket : m_bfsQueue)
{
bucket.reserve(256);
}
m_bmChanged = true; m_bmChanged = true;
m_bmOffset = m_bmSize/2; m_bmOffset = m_bmSize/2;
@ -2206,3 +2389,17 @@ bool CTaskGoto::BitmapTestDot(int rank, int x, int y)
return m_bmArray[rank*m_bmLine*m_bmSize + m_bmLine*y + x/8] & (1<<x%8); return m_bmArray[rank*m_bmLine*m_bmSize + m_bmLine*y + x/8] & (1<<x%8);
} }
bool CTaskGoto::BitmapTestDotIsVisitable(int x, int y)
{
if ( x < 0 || x >= 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<<x%8));
}

View File

@ -23,13 +23,15 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <array>
#include <memory> #include <memory>
#include <vector>
class CObject; class CObject;
const int MAXPOINTS = 500; const int MAXPOINTS = 50000;
const int NUMQUEUEBUCKETS = 32;
enum TaskGotoGoal enum TaskGotoGoal
{ {
@ -94,14 +96,12 @@ protected:
void ComputeRepulse(glm::vec2& dir); void ComputeRepulse(glm::vec2& dir);
void ComputeFlyingRepulse(float &dir); void ComputeFlyingRepulse(float &dir);
int BeamShortcut(); int PathFindingShortcut();
void BeamStart(); void PathFindingStart();
void BeamInit(); void PathFindingInit();
Error BeamSearch(const glm::vec3 &start, const glm::vec3 &goal, float goalRadius); Error PathFindingSearch(const glm::vec3 &start, const glm::vec3 &goal, float goalRadius);
Error BeamExplore(const glm::vec3 &prevPos, const glm::vec3 &curPos, const glm::vec3 &goalPos, float goalRadius, float angle, int nbDiv, float step, int i, int nbIter);
glm::vec3 BeamPoint(const glm::vec3 &startPoint, const glm::vec3 &goalPoint, float angle, float step);
bool BitmapTestLine(const glm::vec3 &start, const glm::vec3 &goal, float stepAngle, bool bSecond); bool BitmapTestLine(const glm::vec3 &start, const glm::vec3 &goal);
void BitmapObject(); void BitmapObject();
void BitmapTerrain(const glm::vec3 &min, const glm::vec3 &max); void BitmapTerrain(const glm::vec3 &min, const glm::vec3 &max);
void BitmapTerrain(int minx, int miny, int maxx, int maxy); void BitmapTerrain(int minx, int miny, int maxx, int maxy);
@ -112,10 +112,11 @@ protected:
void BitmapSetDot(int rank, int x, int y); void BitmapSetDot(int rank, int x, int y);
void BitmapClearDot(int rank, int x, int y); void BitmapClearDot(int rank, int x, int y);
bool BitmapTestDot(int rank, int x, int y); bool BitmapTestDot(int rank, int x, int y);
bool BitmapTestDotIsVisitable(int x, int y);
protected: protected:
glm::vec3 m_goal = { 0, 0, 0 }; glm::vec3 m_goal = { 0, 0, 0 };
glm::vec3 m_goalObject = { 0, 0, 0 }; glm::vec3 m_goalObject = { 0, 0, 0 };
float m_angle = 0.0f; float m_angle = 0.0f;
float m_altitude = 0.0f; float m_altitude = 0.0f;
TaskGotoCrash m_crashMode = TGC_DEFAULT; TaskGotoCrash m_crashMode = TGC_DEFAULT;
@ -136,10 +137,17 @@ protected:
int m_bmSize = 0; // width or height of the table int m_bmSize = 0; // width or height of the table
int m_bmOffset = 0; // m_bmSize/2 int m_bmOffset = 0; // m_bmSize/2
int m_bmLine = 0; // increment line m_bmSize/8 int m_bmLine = 0; // increment line m_bmSize/8
std::unique_ptr<unsigned char[]> m_bmArray; // bit table std::unique_ptr<unsigned char[]> m_bmArray; // Bit table
std::unique_ptr<int32_t[]> m_bfsDistances; // Distances to the goal for breadth-first search.
std::array<std::vector<uint32_t>, 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.
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_bmMinX = 0, m_bmMinY = 0;
int m_bmMaxX = 0, m_bmMaxY = 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 int m_bmIndex = 0; // index in m_bmPoints
glm::vec3 m_bmPoints[MAXPOINTS+2]; glm::vec3 m_bmPoints[MAXPOINTS+2];
signed char m_bmIter[MAXPOINTS+2] = {}; signed char m_bmIter[MAXPOINTS+2] = {};

View File

@ -44,7 +44,7 @@
#include "ui/controls/window.h" #include "ui/controls/window.h"
#include "ui/screen/screen.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_read.h"
#include "ui/screen/screen_io_write.h" #include "ui/screen/screen_io_write.h"
#include "ui/screen/screen_level_list.h" #include "ui/screen/screen_level_list.h"
@ -81,7 +81,7 @@ CMainUserInterface::CMainUserInterface()
m_dialog = std::make_unique<CMainDialog>(); m_dialog = std::make_unique<CMainDialog>();
m_screenAppearance = std::make_unique<CScreenApperance>(); m_screenAppearance = std::make_unique<CScreenAppearance>();
m_screenLevelList = std::make_unique<CScreenLevelList>(m_dialog.get()); m_screenLevelList = std::make_unique<CScreenLevelList>(m_dialog.get());
m_screenIORead = std::make_unique<CScreenIORead>(m_screenLevelList.get()); m_screenIORead = std::make_unique<CScreenIORead>(m_screenLevelList.get());
m_screenIOWrite = std::make_unique<CScreenIOWrite>(m_screenLevelList.get()); m_screenIOWrite = std::make_unique<CScreenIOWrite>(m_screenLevelList.get());
@ -178,7 +178,7 @@ void CMainUserInterface::ChangePhase(Phase phase)
{ {
m_currentScreen = m_screenPlayerSelect.get(); m_currentScreen = m_screenPlayerSelect.get();
} }
if (m_phase == PHASE_APPERANCE) if (m_phase == PHASE_APPEARANCE)
{ {
m_currentScreen = m_screenAppearance.get(); m_currentScreen = m_screenAppearance.get();
} }
@ -791,7 +791,7 @@ bool CMainUserInterface::GetPlusExplorer()
bool CMainUserInterface::GetGamerOnlyHead() bool CMainUserInterface::GetGamerOnlyHead()
{ {
if (m_phase == PHASE_APPERANCE) if (m_phase == PHASE_APPEARANCE)
return m_screenAppearance->GetGamerOnlyHead(); return m_screenAppearance->GetGamerOnlyHead();
return false; return false;
@ -799,7 +799,7 @@ bool CMainUserInterface::GetGamerOnlyHead()
float CMainUserInterface::GetPersoAngle() float CMainUserInterface::GetPersoAngle()
{ {
if (m_phase == PHASE_APPERANCE) if (m_phase == PHASE_APPEARANCE)
return m_screenAppearance->GetPersoAngle(); return m_screenAppearance->GetPersoAngle();
return 0.0f; return 0.0f;

View File

@ -46,7 +46,7 @@ class CInterface;
class CMainDialog; class CMainDialog;
class CScreen; class CScreen;
class CScreenApperance; class CScreenAppearance;
class CScreenIORead; class CScreenIORead;
class CScreenIOWrite; class CScreenIOWrite;
class CScreenLevelList; class CScreenLevelList;
@ -114,7 +114,7 @@ protected:
std::unique_ptr<CMainDialog> m_dialog; std::unique_ptr<CMainDialog> m_dialog;
CScreen* m_currentScreen; CScreen* m_currentScreen;
std::unique_ptr<CScreenApperance> m_screenAppearance; std::unique_ptr<CScreenAppearance> m_screenAppearance;
std::unique_ptr<CScreenIORead> m_screenIORead; std::unique_ptr<CScreenIORead> m_screenIORead;
std::unique_ptr<CScreenIOWrite> m_screenIOWrite; std::unique_ptr<CScreenIOWrite> m_screenIOWrite;
std::unique_ptr<CScreenLevelList> m_screenLevelList; std::unique_ptr<CScreenLevelList> m_screenLevelList;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see http://gnu.org/licenses * 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" #include "app/app.h"
@ -78,13 +78,13 @@ const int PERSO_COLOR[3*10*3] =
0, 0, 0, // 0, 0, 0, //
}; };
CScreenApperance::CScreenApperance() CScreenAppearance::CScreenAppearance()
: m_apperanceTab(0), : m_appearanceTab(0),
m_apperanceAngle(0.0f) m_appearanceAngle(0.0f)
{ {
} }
void CScreenApperance::CreateInterface() void CScreenAppearance::CreateInterface()
{ {
CWindow* pw; CWindow* pw;
CLabel* pl; CLabel* pl;
@ -306,17 +306,17 @@ void CScreenApperance::CreateInterface()
pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PDEF); pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PDEF);
pb->SetState(STATE_SHADOW); pb->SetState(STATE_SHADOW);
m_apperanceTab = 0; m_appearanceTab = 0;
m_apperanceAngle = -0.6f; m_appearanceAngle = -0.6f;
m_main->GetPlayerProfile()->LoadApperance(); m_main->GetPlayerProfile()->LoadAppearance();
UpdatePerso(); UpdatePerso();
m_main->ScenePerso(); m_main->ScenePerso();
CameraPerso(); 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 ) switch( event.type )
{ {
case EVENT_KEY_DOWN: case EVENT_KEY_DOWN:
@ -339,13 +339,13 @@ bool CScreenApperance::EventProcess(const Event &event)
break; break;
case EVENT_INTERFACE_PHEAD: case EVENT_INTERFACE_PHEAD:
m_apperanceTab = 0; m_appearanceTab = 0;
UpdatePerso(); UpdatePerso();
m_main->ScenePerso(); m_main->ScenePerso();
CameraPerso(); CameraPerso();
break; break;
case EVENT_INTERFACE_PBODY: case EVENT_INTERFACE_PBODY:
m_apperanceTab = 1; m_appearanceTab = 1;
UpdatePerso(); UpdatePerso();
m_main->ScenePerso(); m_main->ScenePerso();
CameraPerso(); CameraPerso();
@ -355,8 +355,8 @@ bool CScreenApperance::EventProcess(const Event &event)
case EVENT_INTERFACE_PFACE2: case EVENT_INTERFACE_PFACE2:
case EVENT_INTERFACE_PFACE3: case EVENT_INTERFACE_PFACE3:
case EVENT_INTERFACE_PFACE4: case EVENT_INTERFACE_PFACE4:
apperance.face = event.type-EVENT_INTERFACE_PFACE1; appearance.face = event.type-EVENT_INTERFACE_PFACE1;
apperance.DefHairColor(); appearance.DefHairColor();
UpdatePerso(); UpdatePerso();
m_main->ScenePerso(); m_main->ScenePerso();
break; break;
@ -371,7 +371,7 @@ bool CScreenApperance::EventProcess(const Event &event)
case EVENT_INTERFACE_PGLASS7: case EVENT_INTERFACE_PGLASS7:
case EVENT_INTERFACE_PGLASS8: case EVENT_INTERFACE_PGLASS8:
case EVENT_INTERFACE_PGLASS9: case EVENT_INTERFACE_PGLASS9:
apperance.glasses = event.type-EVENT_INTERFACE_PGLASS0; appearance.glasses = event.type-EVENT_INTERFACE_PGLASS0;
UpdatePerso(); UpdatePerso();
m_main->ScenePerso(); m_main->ScenePerso();
break; break;
@ -418,25 +418,25 @@ bool CScreenApperance::EventProcess(const Event &event)
break; break;
case EVENT_INTERFACE_PDEF: case EVENT_INTERFACE_PDEF:
apperance.DefPerso(); appearance.DefPerso();
UpdatePerso(); UpdatePerso();
m_main->ScenePerso(); m_main->ScenePerso();
break; break;
case EVENT_INTERFACE_PLROT: case EVENT_INTERFACE_PLROT:
m_apperanceAngle += 0.2f; m_appearanceAngle += 0.2f;
break; break;
case EVENT_INTERFACE_PRROT: case EVENT_INTERFACE_PRROT:
m_apperanceAngle -= 0.2f; m_appearanceAngle -= 0.2f;
break; break;
case EVENT_INTERFACE_POK: case EVENT_INTERFACE_POK:
m_main->GetPlayerProfile()->SaveApperance(); m_main->GetPlayerProfile()->SaveAppearance();
m_main->ChangePhase(PHASE_MAIN_MENU); m_main->ChangePhase(PHASE_MAIN_MENU);
break; break;
case EVENT_INTERFACE_PCANCEL: 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); m_main->ChangePhase(PHASE_PLAYER_SELECT);
break; break;
@ -446,14 +446,14 @@ bool CScreenApperance::EventProcess(const Event &event)
return false; 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. // Tests whether two colors are equal or nearly are.
@ -467,7 +467,7 @@ static bool EqColor(const Gfx::Color &c1, const Gfx::Color &c2)
// Updates all the buttons for the character. // Updates all the buttons for the character.
void CScreenApperance::UpdatePerso() void CScreenAppearance::UpdatePerso()
{ {
CWindow* pw; CWindow* pw;
CLabel* pl; CLabel* pl;
@ -478,7 +478,7 @@ void CScreenApperance::UpdatePerso()
std::string name; std::string name;
int i; int i;
PlayerApperance& apperance = m_main->GetPlayerProfile()->GetApperance(); PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance();
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5)); pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return; if ( pw == nullptr ) return;
@ -486,18 +486,18 @@ void CScreenApperance::UpdatePerso()
pb = static_cast<CButton*>(pw->SearchControl(EVENT_INTERFACE_PHEAD)); pb = static_cast<CButton*>(pw->SearchControl(EVENT_INTERFACE_PHEAD));
if ( pb != nullptr ) if ( pb != nullptr )
{ {
pb->SetState(STATE_CHECK, m_apperanceTab==0); pb->SetState(STATE_CHECK, m_appearanceTab==0);
} }
pb = static_cast<CButton*>(pw->SearchControl(EVENT_INTERFACE_PBODY)); pb = static_cast<CButton*>(pw->SearchControl(EVENT_INTERFACE_PBODY));
if ( pb != nullptr ) if ( pb != nullptr )
{ {
pb->SetState(STATE_CHECK, m_apperanceTab==1); pb->SetState(STATE_CHECK, m_appearanceTab==1);
} }
pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL11)); pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL11));
if ( pl != nullptr ) if ( pl != nullptr )
{ {
if ( m_apperanceTab == 0 ) if ( m_appearanceTab == 0 )
{ {
pl->SetState(STATE_VISIBLE); pl->SetState(STATE_VISIBLE);
GetResource(RES_TEXT, RT_PERSO_FACE, name); GetResource(RES_TEXT, RT_PERSO_FACE, name);
@ -512,7 +512,7 @@ void CScreenApperance::UpdatePerso()
pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL12)); pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL12));
if ( pl != nullptr ) if ( pl != nullptr )
{ {
if ( m_apperanceTab == 0 ) if ( m_appearanceTab == 0 )
{ {
pl->SetState(STATE_VISIBLE); pl->SetState(STATE_VISIBLE);
GetResource(RES_TEXT, RT_PERSO_GLASSES, name); GetResource(RES_TEXT, RT_PERSO_GLASSES, name);
@ -527,7 +527,7 @@ void CScreenApperance::UpdatePerso()
pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL13)); pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL13));
if ( pl != nullptr ) 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); else GetResource(RES_TEXT, RT_PERSO_BAND, name);
pl->SetName(name); pl->SetName(name);
} }
@ -535,7 +535,7 @@ void CScreenApperance::UpdatePerso()
pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL14)); pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL14));
if ( pl != nullptr ) if ( pl != nullptr )
{ {
if ( m_apperanceTab == 0 ) if ( m_appearanceTab == 0 )
{ {
pl->ClearState(STATE_VISIBLE); pl->ClearState(STATE_VISIBLE);
} }
@ -551,23 +551,23 @@ void CScreenApperance::UpdatePerso()
{ {
pb = static_cast<CButton*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PFACE1+i))); pb = static_cast<CButton*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PFACE1+i)));
if ( pb == nullptr ) break; if ( pb == nullptr ) break;
pb->SetState(STATE_VISIBLE, m_apperanceTab==0); pb->SetState(STATE_VISIBLE, m_appearanceTab==0);
pb->SetState(STATE_CHECK, i==apperance.face); pb->SetState(STATE_CHECK, i==appearance.face);
} }
for ( i=0 ; i<10 ; i++ ) for ( i=0 ; i<10 ; i++ )
{ {
pb = static_cast<CButton*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PGLASS0+i))); pb = static_cast<CButton*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PGLASS0+i)));
if ( pb == nullptr ) break; if ( pb == nullptr ) break;
pb->SetState(STATE_VISIBLE, m_apperanceTab==0); pb->SetState(STATE_VISIBLE, m_appearanceTab==0);
pb->SetState(STATE_CHECK, i==apperance.glasses); pb->SetState(STATE_CHECK, i==appearance.glasses);
} }
for ( i=0 ; i<3*3 ; i++ ) for ( i=0 ; i<3*3 ; i++ )
{ {
pc = static_cast<CColor*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PC0a+i))); pc = static_cast<CColor*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PC0a+i)));
if ( pc == nullptr ) break; if ( pc == nullptr ) break;
if ( m_apperanceTab == 0 ) if ( m_appearanceTab == 0 )
{ {
pc->ClearState(STATE_VISIBLE); pc->ClearState(STATE_VISIBLE);
} }
@ -579,29 +579,29 @@ void CScreenApperance::UpdatePerso()
color.b = PERSO_COLOR[3*10*1+3*i+2]/255.0f; color.b = PERSO_COLOR[3*10*1+3*i+2]/255.0f;
color.a = 0.0f; color.a = 0.0f;
pc->SetColor(color); pc->SetColor(color);
pc->SetState(STATE_CHECK, EqColor(color, apperance.colorCombi)); pc->SetState(STATE_CHECK, EqColor(color, appearance.colorCombi));
} }
pc = static_cast<CColor*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PC0b+i))); pc = static_cast<CColor*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PC0b+i)));
if ( pc == nullptr ) break; if ( pc == nullptr ) break;
color.r = PERSO_COLOR[3*10*2*m_apperanceTab+3*i+0]/255.0f; color.r = PERSO_COLOR[3*10*2*m_appearanceTab+3*i+0]/255.0f;
color.g = PERSO_COLOR[3*10*2*m_apperanceTab+3*i+1]/255.0f; color.g = PERSO_COLOR[3*10*2*m_appearanceTab+3*i+1]/255.0f;
color.b = PERSO_COLOR[3*10*2*m_apperanceTab+3*i+2]/255.0f; color.b = PERSO_COLOR[3*10*2*m_appearanceTab+3*i+2]/255.0f;
color.a = 0.0f; color.a = 0.0f;
pc->SetColor(color); 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++ ) for ( i=0 ; i<3 ; i++ )
{ {
ps = static_cast<CSlider*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PCRa+i))); ps = static_cast<CSlider*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PCRa+i)));
if ( ps == nullptr ) break; 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<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRa)); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRa));
if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f); if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f);
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCGa)); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCGa));
@ -610,8 +610,8 @@ void CScreenApperance::UpdatePerso()
if ( ps != nullptr ) ps->SetVisibleValue(color.b*255.0f); if ( ps != nullptr ) ps->SetVisibleValue(color.b*255.0f);
} }
if ( m_apperanceTab == 0 ) color = apperance.colorHair; if ( m_appearanceTab == 0 ) color = appearance.colorHair;
else color = apperance.colorBand; else color = appearance.colorBand;
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRb)); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRb));
if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f); if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f);
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCGb)); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCGb));
@ -622,9 +622,9 @@ void CScreenApperance::UpdatePerso()
// Updates the camera for the character. // 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); SetCamera(0.325f, -0.15f, 5.0f);
} }
@ -636,44 +636,44 @@ void CScreenApperance::CameraPerso()
// Sets a fixed color. // Sets a fixed color.
void CScreenApperance::FixPerso(int rank, int index) void CScreenAppearance::FixPerso(int rank, int index)
{ {
PlayerApperance& apperance = m_main->GetPlayerProfile()->GetApperance(); PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance();
if ( m_apperanceTab == 0 ) if ( m_appearanceTab == 0 )
{ {
if ( index == 1 ) if ( index == 1 )
{ {
apperance.colorHair.r = PERSO_COLOR[3*10*0+rank*3+0]/255.0f; appearance.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; appearance.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.b = PERSO_COLOR[3*10*0+rank*3+2]/255.0f;
} }
} }
if ( m_apperanceTab == 1 ) if ( m_appearanceTab == 1 )
{ {
if ( index == 0 ) if ( index == 0 )
{ {
apperance.colorCombi.r = PERSO_COLOR[3*10*1+rank*3+0]/255.0f; appearance.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; appearance.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.b = PERSO_COLOR[3*10*1+rank*3+2]/255.0f;
} }
if ( index == 1 ) if ( index == 1 )
{ {
apperance.colorBand.r = PERSO_COLOR[3*10*2+rank*3+0]/255.0f; appearance.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; appearance.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.b = PERSO_COLOR[3*10*2+rank*3+2]/255.0f;
} }
} }
} }
// Updates the color of the character. // Updates the color of the character.
void CScreenApperance::ColorPerso() void CScreenAppearance::ColorPerso()
{ {
CWindow* pw; CWindow* pw;
CSlider* ps; CSlider* ps;
Gfx::Color color; Gfx::Color color;
PlayerApperance& apperance = m_main->GetPlayerProfile()->GetApperance(); PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance();
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5)); pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return; if ( pw == nullptr ) return;
@ -686,7 +686,7 @@ void CScreenApperance::ColorPerso()
if ( ps != nullptr ) color.g = ps->GetVisibleValue()/255.0f; if ( ps != nullptr ) color.g = ps->GetVisibleValue()/255.0f;
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCBa)); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCBa));
if ( ps != nullptr ) color.b = ps->GetVisibleValue()/255.0f; 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<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRb)); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRb));
if ( ps != nullptr ) color.r = ps->GetVisibleValue()/255.0f; if ( ps != nullptr ) color.r = ps->GetVisibleValue()/255.0f;
@ -694,11 +694,11 @@ void CScreenApperance::ColorPerso()
if ( ps != nullptr ) color.g = ps->GetVisibleValue()/255.0f; if ( ps != nullptr ) color.g = ps->GetVisibleValue()/255.0f;
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCBb)); ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCBb));
if ( ps != nullptr ) color.b = ps->GetVisibleValue()/255.0f; if ( ps != nullptr ) color.b = ps->GetVisibleValue()/255.0f;
if ( m_apperanceTab == 0 ) apperance.colorHair = color; if ( m_appearanceTab == 0 ) appearance.colorHair = color;
else apperance.colorBand = 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::CCamera* camera = m_main->GetCamera();
Gfx::CEngine* engine = Gfx::CEngine::GetInstancePointer(); Gfx::CEngine* engine = Gfx::CEngine::GetInstancePointer();

View File

@ -24,10 +24,10 @@
namespace Ui namespace Ui
{ {
class CScreenApperance : public CScreen class CScreenAppearance : public CScreen
{ {
public: public:
CScreenApperance(); CScreenAppearance();
void CreateInterface() override; void CreateInterface() override;
bool EventProcess(const Event &event) override; bool EventProcess(const Event &event) override;
@ -44,8 +44,8 @@ protected:
void SetCamera(float x, float y, float cameraDistance); void SetCamera(float x, float y, float cameraDistance);
protected: protected:
int m_apperanceTab; // perso: tab selected int m_appearanceTab; // perso: tab selected
float m_apperanceAngle; // perso: angle of presentation float m_appearanceAngle; // perso: angle of presentation
}; };
} // namespace Ui } // namespace Ui

View File

@ -183,7 +183,7 @@ bool CScreenPlayerSelect::EventProcess(const Event &event)
case EVENT_INTERFACE_PERSO: case EVENT_INTERFACE_PERSO:
NameSelect(); NameSelect();
m_main->ChangePhase(PHASE_APPERANCE); m_main->ChangePhase(PHASE_APPEARANCE);
break; break;
case EVENT_INTERFACE_NDELETE: case EVENT_INTERFACE_NDELETE: