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 commands
dev-time-step
krzys-h 2016-04-08 20:56:09 +02:00
parent c6c01c332f
commit 4c8da2c503
4 changed files with 123 additions and 109 deletions

View File

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

View File

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

View File

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

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