LevelController improvements; minor EndMissionTake cleanup
* using LevelController script in the level doesn't forcefully disable EndMissionTake and AudioChange anymore * cleaned up some code related to processing EndMissionTake commandsdev-time-step
parent
c6c01c332f
commit
4c8da2c503
|
@ -1236,19 +1236,22 @@ void CRobotMain::ExecuteCmd(const std::string& cmd)
|
||||||
|
|
||||||
if (cmd == "controller")
|
if (cmd == "controller")
|
||||||
{
|
{
|
||||||
if (m_controller != nullptr)
|
if (m_controller == nullptr)
|
||||||
{
|
{
|
||||||
// Don't use SelectObject because it checks if the object is selectable
|
GetLogger()->Error("No LevelController on the map to select\n");
|
||||||
if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT)
|
return;
|
||||||
StopDisplayVisit();
|
|
||||||
|
|
||||||
CObject* prev = DeselectAll();
|
|
||||||
if (prev != nullptr && prev != m_controller)
|
|
||||||
PushToSelectionHistory(prev);
|
|
||||||
|
|
||||||
SelectOneObject(m_controller, true);
|
|
||||||
m_short->UpdateShortcuts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't use SelectObject because it checks if the object is selectable
|
||||||
|
if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT)
|
||||||
|
StopDisplayVisit();
|
||||||
|
|
||||||
|
CObject* prev = DeselectAll();
|
||||||
|
if (prev != nullptr && prev != m_controller)
|
||||||
|
PushToSelectionHistory(prev);
|
||||||
|
|
||||||
|
SelectOneObject(m_controller, true);
|
||||||
|
m_short->UpdateShortcuts();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2831,6 +2834,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
|
||||||
m_endingLostRank = 0;
|
m_endingLostRank = 0;
|
||||||
m_audioChange.clear();
|
m_audioChange.clear();
|
||||||
m_endTake.clear();
|
m_endTake.clear();
|
||||||
|
m_endTakeImmediat = false;
|
||||||
m_endTakeResearch = 0;
|
m_endTakeResearch = 0;
|
||||||
m_endTakeWinDelay = 2.0f;
|
m_endTakeWinDelay = 2.0f;
|
||||||
m_endTakeLostDelay = 2.0f;
|
m_endTakeLostDelay = 2.0f;
|
||||||
|
@ -2870,6 +2874,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
|
||||||
m_teamNames.clear();
|
m_teamNames.clear();
|
||||||
|
|
||||||
m_missionResult = ERR_MISSION_NOTERM;
|
m_missionResult = ERR_MISSION_NOTERM;
|
||||||
|
m_missionResultFromScript = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//NOTE: Reset timer always, even when only resetting object positions
|
//NOTE: Reset timer always, even when only resetting object positions
|
||||||
|
@ -2999,7 +3004,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line->GetCommand() == "AudioChange" && !resetObject && m_controller == nullptr)
|
if (line->GetCommand() == "AudioChange" && !resetObject)
|
||||||
{
|
{
|
||||||
auto audioChange = MakeUnique<CAudioChangeCondition>();
|
auto audioChange = MakeUnique<CAudioChangeCondition>();
|
||||||
audioChange->Read(line.get());
|
audioChange->Read(line.get());
|
||||||
|
@ -3009,7 +3014,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line->GetCommand() == "Audio" && !resetObject && m_controller == nullptr)
|
if (line->GetCommand() == "Audio" && !resetObject)
|
||||||
{
|
{
|
||||||
if (line->GetParam("track")->IsDefined())
|
if (line->GetParam("track")->IsDefined())
|
||||||
{
|
{
|
||||||
|
@ -3410,6 +3415,11 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
|
||||||
|
|
||||||
if (line->GetCommand() == "LevelController" && m_sceneReadPath.empty())
|
if (line->GetCommand() == "LevelController" && m_sceneReadPath.empty())
|
||||||
{
|
{
|
||||||
|
if (m_controller != nullptr)
|
||||||
|
{
|
||||||
|
throw CLevelParserException("There can be only one LevelController in the level");
|
||||||
|
}
|
||||||
|
|
||||||
m_controller = m_objMan->CreateObject(Math::Vector(0.0f, 0.0f, 0.0f), 0.0f, OBJECT_CONTROLLER);
|
m_controller = m_objMan->CreateObject(Math::Vector(0.0f, 0.0f, 0.0f), 0.0f, OBJECT_CONTROLLER);
|
||||||
assert(m_controller->Implements(ObjectInterfaceType::Programmable));
|
assert(m_controller->Implements(ObjectInterfaceType::Programmable));
|
||||||
assert(m_controller->Implements(ObjectInterfaceType::ProgramStorage));
|
assert(m_controller->Implements(ObjectInterfaceType::ProgramStorage));
|
||||||
|
@ -3625,26 +3635,28 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line->GetCommand() == "EndMissionTake" && !resetObject && m_controller == nullptr)
|
if (line->GetCommand() == "EndMissionTake" && !resetObject)
|
||||||
{
|
{
|
||||||
auto endTake = MakeUnique<CSceneEndCondition>();
|
auto endTake = MakeUnique<CSceneEndCondition>();
|
||||||
endTake->Read(line.get());
|
endTake->Read(line.get());
|
||||||
|
if (endTake->immediat)
|
||||||
|
m_endTakeImmediat = true;
|
||||||
m_endTake.push_back(std::move(endTake));
|
m_endTake.push_back(std::move(endTake));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line->GetCommand() == "EndMissionDelay" && !resetObject && m_controller == nullptr)
|
if (line->GetCommand() == "EndMissionDelay" && !resetObject)
|
||||||
{
|
{
|
||||||
m_endTakeWinDelay = line->GetParam("win")->AsFloat(2.0f);
|
m_endTakeWinDelay = line->GetParam("win")->AsFloat(2.0f);
|
||||||
m_endTakeLostDelay = line->GetParam("lost")->AsFloat(2.0f);
|
m_endTakeLostDelay = line->GetParam("lost")->AsFloat(2.0f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line->GetCommand() == "EndMissionResearch" && !resetObject && m_controller == nullptr) //TODO: Is this used anywhere?
|
if (line->GetCommand() == "EndMissionResearch" && !resetObject) // This is not used in any original Colobot levels, but we'll keep it for userlevel creators
|
||||||
{
|
{
|
||||||
m_endTakeResearch |= line->GetParam("type")->AsResearchFlag();
|
m_endTakeResearch |= line->GetParam("type")->AsResearchFlag();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line->GetCommand() == "ObligatoryToken" && !resetObject) //NOTE: This was used only in CeeBot, maybe we should add this to some Colobot exercises?
|
if (line->GetCommand() == "ObligatoryToken" && !resetObject) // NOTE: This was used only in CeeBot, maybe we should add this to some Colobot exercises?
|
||||||
{
|
{
|
||||||
int i = m_obligatoryTotal;
|
int i = m_obligatoryTotal;
|
||||||
if (i < 100) //TODO: remove the limit
|
if (i < 100) //TODO: remove the limit
|
||||||
|
@ -3655,7 +3667,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line->GetCommand() == "ProhibitedToken" && !resetObject) //NOTE: This was used only in CeeBot, maybe we should add this to some Colobot exercises?
|
if (line->GetCommand() == "ProhibitedToken" && !resetObject) // NOTE: This was used only in CeeBot, maybe we should add this to some Colobot exercises?
|
||||||
{
|
{
|
||||||
int i = m_prohibitedTotal;
|
int i = m_prohibitedTotal;
|
||||||
if (i < 100) //TODO: remove the limit
|
if (i < 100) //TODO: remove the limit
|
||||||
|
@ -5008,17 +5020,16 @@ void CRobotMain::UpdateAudio(bool frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRobotMain::SetEndMission(Error result, float delay)
|
//! Set mission result from LevelController script
|
||||||
|
void CRobotMain::SetMissionResultFromScript(Error result, float delay)
|
||||||
{
|
{
|
||||||
if (m_controller != nullptr)
|
m_endTakeWinDelay = delay;
|
||||||
{
|
m_endTakeLostDelay = delay;
|
||||||
m_endTakeWinDelay = delay;
|
m_missionResult = result;
|
||||||
m_endTakeLostDelay = delay;
|
m_missionResultFromScript = true;
|
||||||
m_missionResult = result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Error CRobotMain::CheckEndMissionForGroup(std::vector<CSceneEndCondition*>& endTakes)
|
Error CRobotMain::ProcessEndMissionTakeForGroup(std::vector<CSceneEndCondition*>& endTakes)
|
||||||
{
|
{
|
||||||
Error finalResult = ERR_OK;
|
Error finalResult = ERR_OK;
|
||||||
bool hasWinningConditions = false;
|
bool hasWinningConditions = false;
|
||||||
|
@ -5045,104 +5056,101 @@ Error CRobotMain::CheckEndMissionForGroup(std::vector<CSceneEndCondition*>& endT
|
||||||
return finalResult;
|
return finalResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Checks if the mission is over
|
Error CRobotMain::ProcessEndMissionTake()
|
||||||
Error CRobotMain::CheckEndMission(bool frame)
|
|
||||||
{
|
{
|
||||||
bool isImmediat = false;
|
// Sort end conditions by teams
|
||||||
// Process EndMissionTake, unless we are using MissionController
|
std::map<int, std::vector<CSceneEndCondition*>> teams;
|
||||||
if (m_controller == nullptr)
|
for (std::unique_ptr<CSceneEndCondition>& endTake : m_endTake)
|
||||||
|
teams[endTake->winTeam].push_back(endTake.get());
|
||||||
|
|
||||||
|
int teamCount = 0;
|
||||||
|
bool usesTeamConditions = false;
|
||||||
|
for (auto it : teams)
|
||||||
{
|
{
|
||||||
// Sort end conditions by teams
|
int team = it.first;
|
||||||
std::map<int, std::vector<CSceneEndCondition*>> teams;
|
if (team == 0) continue;
|
||||||
for (std::unique_ptr<CSceneEndCondition>& endTake : m_endTake)
|
usesTeamConditions = true;
|
||||||
{
|
if (!m_objMan->TeamExists(team)) continue;
|
||||||
teams[endTake->winTeam].push_back(endTake.get());
|
teamCount++;
|
||||||
if(endTake->immediat)
|
}
|
||||||
isImmediat = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int teamCount = 0;
|
if (!usesTeamConditions)
|
||||||
bool usesTeamConditions = false;
|
{
|
||||||
for (auto it : teams)
|
m_missionResult = ProcessEndMissionTakeForGroup(teams[0]);
|
||||||
{
|
}
|
||||||
int team = it.first;
|
else
|
||||||
if(team == 0) continue;
|
{
|
||||||
usesTeamConditions = true;
|
// Special handling for teams
|
||||||
if(!m_objMan->TeamExists(team)) continue;
|
m_missionResult = ERR_MISSION_NOTERM;
|
||||||
teamCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!usesTeamConditions)
|
if (teamCount == 0)
|
||||||
{
|
{
|
||||||
m_missionResult = CheckEndMissionForGroup(teams[0]);
|
GetLogger()->Info("All teams died, mission ended with failure\n");
|
||||||
|
m_missionResult = INFO_LOST;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Special handling for teams
|
for (auto it : teams)
|
||||||
m_missionResult = ERR_MISSION_NOTERM;
|
{
|
||||||
|
int team = it.first;
|
||||||
|
if (team == 0) continue;
|
||||||
|
if (!m_objMan->TeamExists(team)) continue;
|
||||||
|
|
||||||
if (teamCount == 0)
|
Error result = ProcessEndMissionTakeForGroup(it.second);
|
||||||
{
|
if (result == INFO_LOST || result == INFO_LOSTq)
|
||||||
GetLogger()->Info("All teams died, mission ended with failure\n");
|
|
||||||
m_missionResult = INFO_LOST;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (auto it : teams)
|
|
||||||
{
|
{
|
||||||
int team = it.first;
|
GetLogger()->Info("Team %d lost\n", team);
|
||||||
if (team == 0) continue;
|
m_displayText->DisplayText(("<<< Team "+boost::lexical_cast<std::string>(team)+" lost! >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 10.0f, Ui::TT_ERROR);
|
||||||
if (!m_objMan->TeamExists(team)) continue;
|
|
||||||
|
|
||||||
Error result = CheckEndMissionForGroup(it.second);
|
m_displayText->SetEnable(false); // To prevent "bot destroyed" messages
|
||||||
if (result == INFO_LOST || result == INFO_LOSTq)
|
m_objMan->DestroyTeam(team);
|
||||||
{
|
m_displayText->SetEnable(true);
|
||||||
GetLogger()->Info("Team %d lost\n", team);
|
|
||||||
m_displayText->DisplayText(("<<< Team "+boost::lexical_cast<std::string>(team)+" lost! >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 10.0f, Ui::TT_ERROR);
|
|
||||||
|
|
||||||
m_displayText->SetEnable(false); // To prevent "bot destroyed" messages
|
|
||||||
m_objMan->DestroyTeam(team);
|
|
||||||
m_displayText->SetEnable(true);
|
|
||||||
}
|
|
||||||
else if(result == ERR_OK)
|
|
||||||
{
|
|
||||||
if (m_winDelay == 0.0f)
|
|
||||||
{
|
|
||||||
GetLogger()->Info("Team %d won\n", team);
|
|
||||||
|
|
||||||
m_displayText->DisplayText(("<<< Team "+boost::lexical_cast<std::string>(team)+" won the game >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f));
|
|
||||||
if (m_missionTimerEnabled && m_missionTimerStarted)
|
|
||||||
{
|
|
||||||
GetLogger()->Info("Mission time: %s\n", TimeFormat(m_missionTimer).c_str());
|
|
||||||
m_displayText->DisplayText(("Time: " + TimeFormat(m_missionTimer)).c_str(), Math::Vector(0.0f,0.0f,0.0f));
|
|
||||||
}
|
|
||||||
m_missionTimerEnabled = m_missionTimerStarted = false;
|
|
||||||
m_winDelay = m_endTakeWinDelay; // wins in two seconds
|
|
||||||
m_lostDelay = 0.0f;
|
|
||||||
m_displayText->SetEnable(false);
|
|
||||||
}
|
|
||||||
m_missionResult = ERR_OK;
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else if (result == ERR_OK)
|
||||||
}
|
|
||||||
|
|
||||||
if (m_missionResult != INFO_LOST && m_missionResult != INFO_LOSTq)
|
|
||||||
{
|
|
||||||
if (m_endTakeResearch != 0)
|
|
||||||
{
|
|
||||||
if (m_endTakeResearch != (m_endTakeResearch&m_researchDone[0]))
|
|
||||||
{
|
{
|
||||||
m_missionResult = ERR_MISSION_NOTERM;
|
if (m_winDelay == 0.0f)
|
||||||
|
{
|
||||||
|
GetLogger()->Info("Team %d won\n", team);
|
||||||
|
|
||||||
|
m_displayText->DisplayText(("<<< Team "+boost::lexical_cast<std::string>(team)+" won the game >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f));
|
||||||
|
if (m_missionTimerEnabled && m_missionTimerStarted)
|
||||||
|
{
|
||||||
|
GetLogger()->Info("Mission time: %s\n", TimeFormat(m_missionTimer).c_str());
|
||||||
|
m_displayText->DisplayText(("Time: " + TimeFormat(m_missionTimer)).c_str(), Math::Vector(0.0f,0.0f,0.0f));
|
||||||
|
}
|
||||||
|
m_missionTimerEnabled = m_missionTimerStarted = false;
|
||||||
|
m_winDelay = m_endTakeWinDelay; // wins in two seconds
|
||||||
|
m_lostDelay = 0.0f;
|
||||||
|
m_displayText->SetEnable(false);
|
||||||
|
}
|
||||||
|
m_missionResult = ERR_OK;
|
||||||
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_missionResult != INFO_LOST && m_missionResult != INFO_LOSTq)
|
||||||
|
{
|
||||||
|
if (m_endTakeResearch != 0)
|
||||||
|
{
|
||||||
|
if (m_endTakeResearch != (m_endTakeResearch&m_researchDone[0]))
|
||||||
|
{
|
||||||
|
m_missionResult = ERR_MISSION_NOTERM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Checks if the mission is over
|
||||||
|
Error CRobotMain::CheckEndMission(bool frame)
|
||||||
|
{
|
||||||
|
// Process EndMissionTake, unless we are using LevelController script for processing ending conditions
|
||||||
|
if (!m_missionResultFromScript) ProcessEndMissionTake();
|
||||||
|
|
||||||
// Take action depending on m_missionResult
|
// Take action depending on m_missionResult
|
||||||
|
|
||||||
if(m_missionResult == INFO_LOSTq)
|
if (m_missionResult == INFO_LOSTq)
|
||||||
{
|
{
|
||||||
if (m_lostDelay == 0.0f)
|
if (m_lostDelay == 0.0f)
|
||||||
{
|
{
|
||||||
|
@ -5154,7 +5162,7 @@ Error CRobotMain::CheckEndMission(bool frame)
|
||||||
return INFO_LOSTq;
|
return INFO_LOSTq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_missionResult == INFO_LOST)
|
if (m_missionResult == INFO_LOST)
|
||||||
{
|
{
|
||||||
if (m_lostDelay == 0.0f)
|
if (m_lostDelay == 0.0f)
|
||||||
{
|
{
|
||||||
|
@ -5180,7 +5188,7 @@ Error CRobotMain::CheckEndMission(bool frame)
|
||||||
|
|
||||||
if (frame)
|
if (frame)
|
||||||
{
|
{
|
||||||
if(m_base != nullptr && !isImmediat)
|
if (m_base != nullptr && !m_endTakeImmediat)
|
||||||
{
|
{
|
||||||
assert(m_base->Implements(ObjectInterfaceType::Controllable));
|
assert(m_base->Implements(ObjectInterfaceType::Controllable));
|
||||||
if(dynamic_cast<CControllableObject*>(m_base)->GetSelectable())
|
if(dynamic_cast<CControllableObject*>(m_base)->GetSelectable())
|
||||||
|
|
|
@ -195,9 +195,10 @@ public:
|
||||||
|
|
||||||
void ResetObject();
|
void ResetObject();
|
||||||
void UpdateAudio(bool frame);
|
void UpdateAudio(bool frame);
|
||||||
void SetEndMission(Error result, float delay);
|
void SetMissionResultFromScript(Error result, float delay);
|
||||||
Error CheckEndMission(bool frame);
|
Error CheckEndMission(bool frame);
|
||||||
Error CheckEndMissionForGroup(std::vector<CSceneEndCondition*>& endTakes);
|
Error ProcessEndMissionTake();
|
||||||
|
Error ProcessEndMissionTakeForGroup(std::vector<CSceneEndCondition*>& endTakes);
|
||||||
int GetObligatoryToken();
|
int GetObligatoryToken();
|
||||||
char* GetObligatoryToken(int i);
|
char* GetObligatoryToken(int i);
|
||||||
int IsObligatoryToken(const char* token);
|
int IsObligatoryToken(const char* token);
|
||||||
|
@ -543,6 +544,8 @@ protected:
|
||||||
ActivePause* m_visitPause = nullptr;
|
ActivePause* m_visitPause = nullptr;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<CSceneEndCondition>> m_endTake;
|
std::vector<std::unique_ptr<CSceneEndCondition>> m_endTake;
|
||||||
|
//! If true, the mission ends immediately after completing the requirements without requiring SpaceShip takeoff
|
||||||
|
bool m_endTakeImmediat = false;
|
||||||
long m_endTakeResearch = 0;
|
long m_endTakeResearch = 0;
|
||||||
float m_endTakeWinDelay = 0.0f;
|
float m_endTakeWinDelay = 0.0f;
|
||||||
float m_endTakeLostDelay = 0.0f;
|
float m_endTakeLostDelay = 0.0f;
|
||||||
|
@ -562,6 +565,8 @@ protected:
|
||||||
std::map<int, int> m_researchDone;
|
std::map<int, int> m_researchDone;
|
||||||
|
|
||||||
Error m_missionResult = ERR_OK;
|
Error m_missionResult = ERR_OK;
|
||||||
|
//! true if m_missionResult has been set by LevelController script, this disables normal EndMissionTake processing
|
||||||
|
bool m_missionResultFromScript = false;
|
||||||
|
|
||||||
ShowLimit m_showLimit[MAXSHOWLIMIT];
|
ShowLimit m_showLimit[MAXSHOWLIMIT];
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,7 @@ public:
|
||||||
|
|
||||||
int lost = -1; // lost if <=
|
int lost = -1; // lost if <=
|
||||||
|
|
||||||
|
//! If this is true, the mission ends as soon as this requirement is met, without having to complete the others
|
||||||
bool immediat = false;
|
bool immediat = false;
|
||||||
|
|
||||||
//! Read from line in scene file
|
//! Read from line in scene file
|
||||||
|
|
|
@ -204,7 +204,7 @@ bool CScriptFunctions::rEndMission(CBotVar* var, CBotVar* result, int& exception
|
||||||
|
|
||||||
delay = var->GetValFloat();
|
delay = var->GetValFloat();
|
||||||
|
|
||||||
CRobotMain::GetInstancePointer()->SetEndMission(ended, delay);
|
CRobotMain::GetInstancePointer()->SetMissionResultFromScript(ended, delay);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue