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"]
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
[submodule "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/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
@ -525,7 +525,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)

View File

@ -244,6 +244,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
OPT_DEBUG,
OPT_RUNSCENE,
OPT_SCENETEST,
OPT_LOADSAVE,
OPT_LOGLEVEL,
OPT_LANGDIR,
OPT_DATADIR,
@ -262,6 +263,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 },
{ "loadsave", required_argument, nullptr, OPT_LOADSAVE },
{ "loglevel", required_argument, nullptr, OPT_LOGLEVEL },
{ "langdir", required_argument, nullptr, OPT_LANGDIR },
{ "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(" -runscene sceneNNN run given scene on start\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(" -langdir path set custom language directory path\n");
GetLogger()->Message(" environment variable: COLOBOT_LANG_DIR\n");
@ -364,6 +367,22 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
m_sceneTest = true;
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:
{
LogLevel logLevel;
@ -590,7 +609,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)
@ -695,8 +714,15 @@ bool CApplication::Create()
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();
}
else
{
m_controller->GetRobotMain()->UpdateCustomLevelList(); // To load the userlevels
@ -1865,7 +1891,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;
}
}
@ -1934,7 +1960,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());

View File

@ -219,7 +219,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]
@ -406,13 +406,17 @@ protected:
int m_runSceneRank;
//@}
//! Game to load on startup
std::string m_loadSavePlayerName;
std::string m_loadSaveDirName;
//! Scene test mode
bool m_sceneTest;
//! Application language
Language m_language;
//! Screen resoultion overriden by commandline
//! Screen resolution overriden by commandline
bool m_resolutionOverride;
//! 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.
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>,
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/.
\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
@ -91,7 +91,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 <i>scene files</i>
- src/ui - 2D user interface (menu, buttons, check boxes, etc.)

View File

@ -32,7 +32,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;
@ -75,7 +75,7 @@ void PlayerApperance::DefPerso()
this->colorBand.a = 0.0f;
}
void PlayerApperance::DefHairColor()
void PlayerAppearance::DefHairColor()
{
if (this->face == 0) // normal ?
{
@ -121,7 +121,7 @@ CPlayerProfile::CPlayerProfile(std::string playerName)
m_levelInfoLoaded[static_cast<LevelCategory>(i)] = false;
}
LoadApperance();
LoadAppearance();
}
CPlayerProfile::~CPlayerProfile()
@ -391,17 +391,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))
@ -409,48 +409,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 = std::make_unique<CLevelParserLine>("Head");
line->AddParam("face", std::make_unique<CLevelParserParam>(m_apperance.face));
line->AddParam("glasses", std::make_unique<CLevelParserParam>(m_apperance.glasses));
line->AddParam("hair", std::make_unique<CLevelParserParam>(m_apperance.colorHair));
apperanceParser.AddLine(std::move(line));
line->AddParam("face", std::make_unique<CLevelParserParam>(m_appearance.face));
line->AddParam("glasses", std::make_unique<CLevelParserParam>(m_appearance.glasses));
line->AddParam("hair", std::make_unique<CLevelParserParam>(m_appearance.colorHair));
appearanceParser.AddLine(std::move(line));
line = std::make_unique<CLevelParserLine>("Body");
line->AddParam("combi", std::make_unique<CLevelParserParam>(m_apperance.colorCombi));
line->AddParam("band", std::make_unique<CLevelParserParam>(m_apperance.colorBand));
apperanceParser.AddLine(std::move(line));
line->AddParam("combi", std::make_unique<CLevelParserParam>(m_appearance.colorCombi));
line->AddParam("band", std::make_unique<CLevelParserParam>(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());
}
}
@ -524,7 +524,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();
@ -552,7 +552,7 @@ void CPlayerProfile::LoadScene(std::string dir)
}
else
{
// Backwards combatibility
// Backwards compatibility
chap = rank/100;
rank = rank%100;
}

View File

@ -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;
};

View File

@ -320,7 +320,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";
@ -355,14 +355,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);
}
@ -534,7 +534,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);
@ -1165,7 +1165,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 ||
@ -2100,7 +2100,7 @@ void CRobotMain::HiliteClear()
//! Highlights the object with the mouse hovers over
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_movie->IsExist()) 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
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;
@ -2451,7 +2451,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)
{
@ -2724,7 +2724,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
@ -3946,7 +3946,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
@ -3961,11 +3961,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;
glm::vec2 exclu[6];
exclu[0] = { 192.0f / 256.0f, 0.0f / 256.0f };
@ -4007,7 +4007,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;
@ -5492,13 +5492,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
@ -5878,6 +5878,17 @@ void CRobotMain::QuickLoad()
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)
{
m_exitAfterMission = exit;

View File

@ -56,7 +56,7 @@ enum Phase
PHASE_WELCOME2,
PHASE_WELCOME3,
PHASE_PLAYER_SELECT,
PHASE_APPERANCE,
PHASE_APPEARANCE,
PHASE_MAIN_MENU,
PHASE_LEVEL_LIST,
PHASE_MOD_LIST,
@ -203,7 +203,7 @@ public:
Phase GetPhase();
//@}
//! Load the scene for apperance customization
//! Load the scene for appearance customization
void ScenePerso();
void SetMovieLock(bool lock);
@ -373,6 +373,9 @@ public:
//! Enable mode where completing mission closes the game
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
bool CanPlayerInteract();

View File

@ -48,10 +48,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 = 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)
{
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);
}
}
@ -225,7 +225,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;
@ -349,7 +349,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;
}
@ -793,7 +793,7 @@ Error CTaskGoto::Start(glm::vec3 goal, float altitude,
}
}
BeamStart();
PathFindingStart();
if ( m_bmCargoObject == nullptr )
{
@ -829,7 +829,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;
@ -891,7 +891,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 )
{
@ -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.
// If yes, skip all the unnecessary intermediate points.
int CTaskGoto::BeamShortcut()
int CTaskGoto::PathFindingShortcut()
{
int i;
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
}
@ -1648,7 +1648,7 @@ int CTaskGoto::BeamShortcut()
// That's the big start.
void CTaskGoto::BeamStart()
void CTaskGoto::PathFindingStart()
{
glm::vec3 min, max;
@ -1674,14 +1674,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;
@ -1690,6 +1690,34 @@ void CTaskGoto::BeamInit()
m_bmIter[i] = -1;
}
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.
@ -1700,199 +1728,350 @@ void CTaskGoto::BeamInit()
// ERR_CONTINUE if not done yet
// goalRadius: distance at which we must approach the goal
Error CTaskGoto::BeamSearch(const glm::vec3 &start, const glm::vec3 &goal,
float goalRadius)
Error CTaskGoto::PathFindingSearch(const glm::vec3 &start, const glm::vec3 &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};
// 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};
// 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<int>((start.x+1600.0f)/BM_DIM_STEP);
const int startY = static_cast<int>((start.z+1600.0f)/BM_DIM_STEP);
const int goalX = static_cast<int>((goal.x+1600.0f)/BM_DIM_STEP);
const int goalY = static_cast<int>((goal.z+1600.0f)/BM_DIM_STEP);
Error CTaskGoto::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 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_bfsQueueCountPushed == 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;
}
// 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
{
if ( !BitmapTestLine(prevPos, curPos, angle/nbDiv, true) ) return ERR_GOTO_IMPOSSIBLE;
m_bfsQueueMin = std::numeric_limits<int>::max();
}
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<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;
}
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;
const int totalDistance = HeuristicDistance(x, y, startX, startY);
m_bfsQueueMin = std::min(m_bfsQueueMin, totalDistance);
m_bfsDistances[indexInMap] = 0;
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);
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
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();
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 ++;
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.
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.
bool CTaskGoto::BitmapTestLine(const glm::vec3 &start, const glm::vec3 &goal,
float stepAngle, bool bSecond)
bool CTaskGoto::BitmapTestLine(const glm::vec3 &start, const glm::vec3 &goal)
{
glm::vec3 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 glm::vec2 startInGrid = glm::vec2((start.x+1600.0f)/BM_DIM_STEP, (start.z+1600.0f)/BM_DIM_STEP);
const glm::vec2 goalInGrid = glm::vec2((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<int>(startInGrid.x);
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 ( bSecond )
if (startXInt == goalXInt && startYInt == goalYInt)
{
x = static_cast<int>((pos.x+1600.0f)/BM_DIM_STEP);
y = static_cast<int>((pos.z+1600.0f)/BM_DIM_STEP);
BitmapSetDot(1, x, y); // puts the flag as the starting point
return true;
}
max = static_cast<int>(dist/step);
if ( max == 0 ) max = 1;
distNoB2 = BM_DIM_STEP*sqrtf(2.0f)/sinf(stepAngle);
for ( i=0 ; i<max ; i++ )
// Grid traversal based on
// Amanatides, John, and Andrew Woo. "A fast voxel traversal algorithm for ray tracing." Eurographics. Vol. 87. No. 3. 1987.
// http://www.cse.yorku.ca/~amana/research/grid.pdf
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
{
pos.x += inc.x;
pos.z += inc.z;
tMaxY += tDeltaY;
y += stepY;
}
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 ( 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;
}
@ -2096,10 +2275,14 @@ void CTaskGoto::BitmapTerrain(int minx, int miny, int maxx, int maxy)
bool CTaskGoto::BitmapOpen()
{
BitmapClose();
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_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);
}
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 <array>
#include <memory>
#include <vector>
class CObject;
const int MAXPOINTS = 500;
const int MAXPOINTS = 50000;
const int NUMQUEUEBUCKETS = 32;
enum TaskGotoGoal
{
@ -94,14 +96,12 @@ protected:
void ComputeRepulse(glm::vec2& dir);
void ComputeFlyingRepulse(float &dir);
int BeamShortcut();
void BeamStart();
void BeamInit();
Error BeamSearch(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);
int PathFindingShortcut();
void PathFindingStart();
void PathFindingInit();
Error PathFindingSearch(const glm::vec3 &start, const glm::vec3 &goal, float goalRadius);
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 BitmapTerrain(const glm::vec3 &min, const glm::vec3 &max);
void BitmapTerrain(int minx, int miny, int maxx, int maxy);
@ -112,10 +112,11 @@ protected:
void BitmapSetDot(int rank, int x, int y);
void BitmapClearDot(int rank, int x, int y);
bool BitmapTestDot(int rank, int x, int y);
bool BitmapTestDotIsVisitable(int x, int y);
protected:
glm::vec3 m_goal = { 0, 0, 0 };
glm::vec3 m_goalObject = { 0, 0, 0 };
glm::vec3 m_goal = { 0, 0, 0 };
glm::vec3 m_goalObject = { 0, 0, 0 };
float m_angle = 0.0f;
float m_altitude = 0.0f;
TaskGotoCrash m_crashMode = TGC_DEFAULT;
@ -136,10 +137,17 @@ protected:
int m_bmSize = 0; // width or height of the table
int m_bmOffset = 0; // m_bmSize/2
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_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
glm::vec3 m_bmPoints[MAXPOINTS+2];
signed char m_bmIter[MAXPOINTS+2] = {};

View File

@ -44,7 +44,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"
@ -81,7 +81,7 @@ CMainUserInterface::CMainUserInterface()
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_screenIORead = std::make_unique<CScreenIORead>(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();
}
if (m_phase == PHASE_APPERANCE)
if (m_phase == PHASE_APPEARANCE)
{
m_currentScreen = m_screenAppearance.get();
}
@ -791,7 +791,7 @@ bool CMainUserInterface::GetPlusExplorer()
bool CMainUserInterface::GetGamerOnlyHead()
{
if (m_phase == PHASE_APPERANCE)
if (m_phase == PHASE_APPEARANCE)
return m_screenAppearance->GetGamerOnlyHead();
return false;
@ -799,7 +799,7 @@ bool CMainUserInterface::GetGamerOnlyHead()
float CMainUserInterface::GetPersoAngle()
{
if (m_phase == PHASE_APPERANCE)
if (m_phase == PHASE_APPEARANCE)
return m_screenAppearance->GetPersoAngle();
return 0.0f;

View File

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

View File

@ -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"
@ -78,13 +78,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;
@ -306,17 +306,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:
@ -339,13 +339,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();
@ -355,8 +355,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;
@ -371,7 +371,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;
@ -418,25 +418,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;
@ -446,14 +446,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.
@ -467,7 +467,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;
@ -478,7 +478,7 @@ void CScreenApperance::UpdatePerso()
std::string name;
int i;
PlayerApperance& apperance = m_main->GetPlayerProfile()->GetApperance();
PlayerAppearance& appearance = m_main->GetPlayerProfile()->GetAppearance();
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return;
@ -486,18 +486,18 @@ void CScreenApperance::UpdatePerso()
pb = static_cast<CButton*>(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<CButton*>(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<CLabel*>(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);
@ -512,7 +512,7 @@ void CScreenApperance::UpdatePerso()
pl = static_cast<CLabel*>(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);
@ -527,7 +527,7 @@ void CScreenApperance::UpdatePerso()
pl = static_cast<CLabel*>(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);
}
@ -535,7 +535,7 @@ void CScreenApperance::UpdatePerso()
pl = static_cast<CLabel*>(pw->SearchControl(EVENT_LABEL14));
if ( pl != nullptr )
{
if ( m_apperanceTab == 0 )
if ( m_appearanceTab == 0 )
{
pl->ClearState(STATE_VISIBLE);
}
@ -551,23 +551,23 @@ void CScreenApperance::UpdatePerso()
{
pb = static_cast<CButton*>(pw->SearchControl(static_cast<EventType>(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<CButton*>(pw->SearchControl(static_cast<EventType>(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<CColor*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_PC0a+i)));
if ( pc == nullptr ) break;
if ( m_apperanceTab == 0 )
if ( m_appearanceTab == 0 )
{
pc->ClearState(STATE_VISIBLE);
}
@ -579,29 +579,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<CColor*>(pw->SearchControl(static_cast<EventType>(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<CSlider*>(pw->SearchControl(static_cast<EventType>(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<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRa));
if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f);
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 ( m_apperanceTab == 0 ) color = apperance.colorHair;
else color = apperance.colorBand;
if ( m_appearanceTab == 0 ) color = appearance.colorHair;
else color = appearance.colorBand;
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRb));
if ( ps != nullptr ) ps->SetVisibleValue(color.r*255.0f);
ps = static_cast<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCGb));
@ -622,9 +622,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);
}
@ -636,44 +636,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<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return;
@ -686,7 +686,7 @@ void CScreenApperance::ColorPerso()
if ( ps != nullptr ) color.g = ps->GetVisibleValue()/255.0f;
ps = static_cast<CSlider*>(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<CSlider*>(pw->SearchControl(EVENT_INTERFACE_PCRb));
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;
ps = static_cast<CSlider*>(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();

View File

@ -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

View File

@ -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: