From 4c8da2c5036fc8a9ab0123b7f0a29a2a15eb260d Mon Sep 17 00:00:00 2001 From: krzys-h Date: Fri, 8 Apr 2016 20:56:09 +0200 Subject: [PATCH] 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 --- src/level/robotmain.cpp | 220 ++++++++++++++++++----------------- src/level/robotmain.h | 9 +- src/level/scene_conditions.h | 1 + src/script/scriptfunc.cpp | 2 +- 4 files changed, 123 insertions(+), 109 deletions(-) diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 274118b7..b3c34de1 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -1236,19 +1236,22 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) if (cmd == "controller") { - if (m_controller != nullptr) + if (m_controller == nullptr) { - // 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(); + GetLogger()->Error("No LevelController on the map to select\n"); + return; } + + // 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; } @@ -2831,6 +2834,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) m_endingLostRank = 0; m_audioChange.clear(); m_endTake.clear(); + m_endTakeImmediat = false; m_endTakeResearch = 0; m_endTakeWinDelay = 2.0f; m_endTakeLostDelay = 2.0f; @@ -2870,6 +2874,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) m_teamNames.clear(); m_missionResult = ERR_MISSION_NOTERM; + m_missionResultFromScript = false; } //NOTE: Reset timer always, even when only resetting object positions @@ -2999,7 +3004,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) continue; } - if (line->GetCommand() == "AudioChange" && !resetObject && m_controller == nullptr) + if (line->GetCommand() == "AudioChange" && !resetObject) { auto audioChange = MakeUnique(); audioChange->Read(line.get()); @@ -3009,7 +3014,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) continue; } - if (line->GetCommand() == "Audio" && !resetObject && m_controller == nullptr) + if (line->GetCommand() == "Audio" && !resetObject) { 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 (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); assert(m_controller->Implements(ObjectInterfaceType::Programmable)); assert(m_controller->Implements(ObjectInterfaceType::ProgramStorage)); @@ -3625,26 +3635,28 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) continue; } - if (line->GetCommand() == "EndMissionTake" && !resetObject && m_controller == nullptr) + if (line->GetCommand() == "EndMissionTake" && !resetObject) { auto endTake = MakeUnique(); endTake->Read(line.get()); + if (endTake->immediat) + m_endTakeImmediat = true; m_endTake.push_back(std::move(endTake)); continue; } - if (line->GetCommand() == "EndMissionDelay" && !resetObject && m_controller == nullptr) + if (line->GetCommand() == "EndMissionDelay" && !resetObject) { m_endTakeWinDelay = line->GetParam("win")->AsFloat(2.0f); m_endTakeLostDelay = line->GetParam("lost")->AsFloat(2.0f); 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(); 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; if (i < 100) //TODO: remove the limit @@ -3655,7 +3667,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) 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; 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_missionResult = result; - } + m_endTakeWinDelay = delay; + m_endTakeLostDelay = delay; + m_missionResult = result; + m_missionResultFromScript = true; } -Error CRobotMain::CheckEndMissionForGroup(std::vector& endTakes) +Error CRobotMain::ProcessEndMissionTakeForGroup(std::vector& endTakes) { Error finalResult = ERR_OK; bool hasWinningConditions = false; @@ -5045,104 +5056,101 @@ Error CRobotMain::CheckEndMissionForGroup(std::vector& endT return finalResult; } -//! Checks if the mission is over -Error CRobotMain::CheckEndMission(bool frame) +Error CRobotMain::ProcessEndMissionTake() { - bool isImmediat = false; - // Process EndMissionTake, unless we are using MissionController - if (m_controller == nullptr) + // Sort end conditions by teams + std::map> teams; + for (std::unique_ptr& 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 - std::map> teams; - for (std::unique_ptr& endTake : m_endTake) - { - teams[endTake->winTeam].push_back(endTake.get()); - if(endTake->immediat) - isImmediat = true; - } + int team = it.first; + if (team == 0) continue; + usesTeamConditions = true; + if (!m_objMan->TeamExists(team)) continue; + teamCount++; + } - int teamCount = 0; - bool usesTeamConditions = false; - for (auto it : teams) - { - int team = it.first; - if(team == 0) continue; - usesTeamConditions = true; - if(!m_objMan->TeamExists(team)) continue; - teamCount++; - } + if (!usesTeamConditions) + { + m_missionResult = ProcessEndMissionTakeForGroup(teams[0]); + } + else + { + // Special handling for teams + m_missionResult = ERR_MISSION_NOTERM; - 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 { - // Special handling for teams - m_missionResult = ERR_MISSION_NOTERM; + for (auto it : teams) + { + int team = it.first; + if (team == 0) continue; + if (!m_objMan->TeamExists(team)) continue; - if (teamCount == 0) - { - GetLogger()->Info("All teams died, mission ended with failure\n"); - m_missionResult = INFO_LOST; - } - else - { - for (auto it : teams) + Error result = ProcessEndMissionTakeForGroup(it.second); + if (result == INFO_LOST || result == INFO_LOSTq) { - int team = it.first; - if (team == 0) continue; - if (!m_objMan->TeamExists(team)) continue; + GetLogger()->Info("Team %d lost\n", team); + m_displayText->DisplayText(("<<< Team "+boost::lexical_cast(team)+" lost! >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 10.0f, Ui::TT_ERROR); - Error result = CheckEndMissionForGroup(it.second); - if (result == INFO_LOST || result == INFO_LOSTq) - { - GetLogger()->Info("Team %d lost\n", team); - m_displayText->DisplayText(("<<< Team "+boost::lexical_cast(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(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; - } + m_displayText->SetEnable(false); // To prevent "bot destroyed" messages + m_objMan->DestroyTeam(team); + m_displayText->SetEnable(true); } - } - } - - if (m_missionResult != INFO_LOST && m_missionResult != INFO_LOSTq) - { - if (m_endTakeResearch != 0) - { - if (m_endTakeResearch != (m_endTakeResearch&m_researchDone[0])) + else if (result == ERR_OK) { - m_missionResult = ERR_MISSION_NOTERM; + if (m_winDelay == 0.0f) + { + GetLogger()->Info("Team %d won\n", team); + + m_displayText->DisplayText(("<<< Team "+boost::lexical_cast(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 - if(m_missionResult == INFO_LOSTq) + if (m_missionResult == INFO_LOSTq) { if (m_lostDelay == 0.0f) { @@ -5154,7 +5162,7 @@ Error CRobotMain::CheckEndMission(bool frame) return INFO_LOSTq; } - if(m_missionResult == INFO_LOST) + if (m_missionResult == INFO_LOST) { if (m_lostDelay == 0.0f) { @@ -5180,7 +5188,7 @@ Error CRobotMain::CheckEndMission(bool frame) if (frame) { - if(m_base != nullptr && !isImmediat) + if (m_base != nullptr && !m_endTakeImmediat) { assert(m_base->Implements(ObjectInterfaceType::Controllable)); if(dynamic_cast(m_base)->GetSelectable()) diff --git a/src/level/robotmain.h b/src/level/robotmain.h index deea9e82..4f78df3d 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -195,9 +195,10 @@ public: void ResetObject(); void UpdateAudio(bool frame); - void SetEndMission(Error result, float delay); + void SetMissionResultFromScript(Error result, float delay); Error CheckEndMission(bool frame); - Error CheckEndMissionForGroup(std::vector& endTakes); + Error ProcessEndMissionTake(); + Error ProcessEndMissionTakeForGroup(std::vector& endTakes); int GetObligatoryToken(); char* GetObligatoryToken(int i); int IsObligatoryToken(const char* token); @@ -543,6 +544,8 @@ protected: ActivePause* m_visitPause = nullptr; std::vector> m_endTake; + //! If true, the mission ends immediately after completing the requirements without requiring SpaceShip takeoff + bool m_endTakeImmediat = false; long m_endTakeResearch = 0; float m_endTakeWinDelay = 0.0f; float m_endTakeLostDelay = 0.0f; @@ -562,6 +565,8 @@ protected: std::map m_researchDone; 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]; diff --git a/src/level/scene_conditions.h b/src/level/scene_conditions.h index 33a65088..258cc7ef 100644 --- a/src/level/scene_conditions.h +++ b/src/level/scene_conditions.h @@ -77,6 +77,7 @@ public: 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; //! Read from line in scene file diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index d072c79a..e4da1f16 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -204,7 +204,7 @@ bool CScriptFunctions::rEndMission(CBotVar* var, CBotVar* result, int& exception delay = var->GetValFloat(); - CRobotMain::GetInstancePointer()->SetEndMission(ended, delay); + CRobotMain::GetInstancePointer()->SetMissionResultFromScript(ended, delay); return true; }