From 7f38aca7660f5ba80a259537d9bee7c0cecdb3f1 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Fri, 8 Apr 2016 22:15:54 +0200 Subject: [PATCH] Improvements to ObligatoryToken/ProhibitedToken Now you can specify exactly how many times given instruction can occur --- po/colobot.pot | 12 ++++--- po/de.po | 24 +++++++++----- po/fr.po | 24 +++++++++----- po/pl.po | 24 +++++++++----- po/ru.po | 24 +++++++++----- src/common/restext.cpp | 4 +-- src/level/robotmain.cpp | 69 ++++++++++++++--------------------------- src/level/robotmain.h | 16 +++++----- src/script/script.cpp | 58 ++++++++++++++++++---------------- src/script/script.h | 3 +- 10 files changed, 143 insertions(+), 115 deletions(-) diff --git a/po/colobot.pot b/po/colobot.pot index 1c4e61ef..442beb45 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -611,13 +611,13 @@ msgstr "" msgid "\\Face 1" msgstr "" -msgid "\\Face 4" +msgid "\\Face 2" msgstr "" msgid "\\Face 3" msgstr "" -msgid "\\Face 2" +msgid "\\Face 4" msgstr "" msgid "\\No eyeglasses" @@ -1519,10 +1519,11 @@ msgid "Unable to control enemy objects" msgstr "" #, c-format -msgid "\"%s\" missing in this exercise" +msgid "You have to use \"%s\" at least %d times in this exercise (used: %d)" msgstr "" -msgid "Do not use in this exercise" +#, c-format +msgid "You have to use \"%s\" at most %d times in this exercise (used: %d)" msgstr "" msgid "Inappropriate bot" @@ -1738,6 +1739,9 @@ msgstr "" msgid "Public required" msgstr "" +msgid "expression expected after =" +msgstr "" + msgid "Dividing by zero" msgstr "" diff --git a/po/de.po b/po/de.po index d008dfa3..300d209c 100644 --- a/po/de.po +++ b/po/de.po @@ -61,10 +61,6 @@ msgstr "Es fehlt eine offene eckige Klammer \" [ \"" msgid "\" ] \" missing" msgstr "Es fehlt eine geschlossene eckige Klammer \" ] \"" -#, c-format -msgid "\"%s\" missing in this exercise" -msgstr "Es fehlt \"%s\" in Ihrem Programm" - msgid "..behind" msgstr "..hinten" @@ -491,9 +487,6 @@ msgstr "Bildschirm\\Driver und Bildschirmauflösung" msgid "Dividing by zero" msgstr "Teilung durch Null" -msgid "Do not use in this exercise" -msgstr "In dieser Übung verboten" - msgid "Do you really want to destroy the selected building?" msgstr "Wollen Sie das angewählte Gebäude wirklich zerstören ?" @@ -1715,6 +1708,14 @@ msgstr "Sie können unter Wasser nichts tragen" msgid "You found a usable object" msgstr "Sie haben ein brauchbares Objekt gefunden" +#, c-format +msgid "You have to use \"%s\" at least %d times in this exercise (used: %d)" +msgstr "" + +#, c-format +msgid "You have to use \"%s\" at most %d times in this exercise (used: %d)" +msgstr "" + msgid "You must get on the spaceship to take off " msgstr "Gehen Sie an Bord, bevor Sie abheben" @@ -1821,6 +1822,9 @@ msgstr "" msgid "epsitec.com" msgstr "www.epsitec.com" +msgid "expression expected after =" +msgstr "" + #~ msgid " " #~ msgstr " " @@ -1830,6 +1834,9 @@ msgstr "www.epsitec.com" #~ msgid " Missions on this level:" #~ msgstr " Missionen des Userlevels:" +#~ msgid "\"%s\" missing in this exercise" +#~ msgstr "Es fehlt \"%s\" in Ihrem Programm" + #~ msgid "3D sound\\3D positioning of the sound" #~ msgstr "3D-Geräusche\\Orten der Geräusche im Raum" @@ -1863,6 +1870,9 @@ msgstr "www.epsitec.com" #~ msgid "Developed by :" #~ msgstr "Entwickelt von:" +#~ msgid "Do not use in this exercise" +#~ msgstr "In dieser Übung verboten" + #, fuzzy #~ msgid "Do you want to quit Colobot: Gold Edition?" #~ msgstr "Wollen Sie COLOBOT schließen ?" diff --git a/po/fr.po b/po/fr.po index 5db35d47..a57ce326 100644 --- a/po/fr.po +++ b/po/fr.po @@ -54,10 +54,6 @@ msgstr "\" [ \" attendu" msgid "\" ] \" missing" msgstr "\" ] \" attendu" -#, c-format -msgid "\"%s\" missing in this exercise" -msgstr "Il manque \"%s\" dans le programme" - msgid "..behind" msgstr "..derrière" @@ -486,9 +482,6 @@ msgstr "Affichage\\Pilote et résolution d'affichage" msgid "Dividing by zero" msgstr "Division par zéro" -msgid "Do not use in this exercise" -msgstr "Interdit dans cet exercice" - msgid "Do you really want to destroy the selected building?" msgstr "Voulez-vous vraiment détruire le bâtiment sélectionné ?" @@ -1701,6 +1694,14 @@ msgstr "Vous ne pouvez pas transporter un objet sous l'eau" msgid "You found a usable object" msgstr "Vous avez trouvé un objet utilisable" +#, c-format +msgid "You have to use \"%s\" at least %d times in this exercise (used: %d)" +msgstr "" + +#, c-format +msgid "You have to use \"%s\" at most %d times in this exercise (used: %d)" +msgstr "" + msgid "You must get on the spaceship to take off " msgstr "Vous devez embarquer pour pouvoir décoller" @@ -1806,6 +1807,9 @@ msgstr "colobot.info" msgid "epsitec.com" msgstr "epsitec.com" +msgid "expression expected after =" +msgstr "" + #~ msgid " " #~ msgstr " " @@ -1815,6 +1819,9 @@ msgstr "epsitec.com" #~ msgid " Missions on this level:" #~ msgstr " Missions du niveau :" +#~ msgid "\"%s\" missing in this exercise" +#~ msgstr "Il manque \"%s\" dans le programme" + #~ msgid "3D sound\\3D positioning of the sound" #~ msgstr "Bruitages 3D\\Positionnement sonore dans l'espace" @@ -1848,6 +1855,9 @@ msgstr "epsitec.com" #~ msgid "Developed by :" #~ msgstr "Développé par :" +#~ msgid "Do not use in this exercise" +#~ msgstr "Interdit dans cet exercice" + #, fuzzy #~ msgid "Do you want to quit Colobot: Gold Edition?" #~ msgstr "Voulez-vous quitter COLOBOT ?" diff --git a/po/pl.po b/po/pl.po index b4f079c2..5bc0a58d 100644 --- a/po/pl.po +++ b/po/pl.po @@ -60,10 +60,6 @@ msgstr "Oczekiwane \" [ \"" msgid "\" ] \" missing" msgstr "Brak \" ] \"" -#, c-format -msgid "\"%s\" missing in this exercise" -msgstr "Brakuje \"%s\" w tym ćwiczeniu" - msgid "..behind" msgstr "..za" @@ -492,9 +488,6 @@ msgstr "Urządzenie\\Ustawienia sterownika i rozdzielczości" msgid "Dividing by zero" msgstr "Dzielenie przez zero" -msgid "Do not use in this exercise" -msgstr "Do not use in this exercise" - msgid "Do you really want to destroy the selected building?" msgstr "Czy na pewno chcesz zniszczyć zaznaczony budynek?" @@ -1709,6 +1702,14 @@ msgstr "Nie możesz przenosić przedmiotów pod wodą" msgid "You found a usable object" msgstr "Znaleziono użyteczny przedmiot" +#, c-format +msgid "You have to use \"%s\" at least %d times in this exercise (used: %d)" +msgstr "Musisz użyć \"%s\" przynajmniej %d razy w tym ćwiczeniu (użyto: %d)" + +#, c-format +msgid "You have to use \"%s\" at most %d times in this exercise (used: %d)" +msgstr "Musisz użyć \"%s\" najwyżej %d razy w tym ćwiczeniu (użyto: %d)" + msgid "You must get on the spaceship to take off " msgstr "Musisz być na statku kosmicznym aby nim odlecieć" @@ -1814,9 +1815,15 @@ msgstr "colobot.info" msgid "epsitec.com" msgstr "epsitec.com" +msgid "expression expected after =" +msgstr "" + #~ msgid " Drivers:" #~ msgstr " Sterowniki:" +#~ msgid "\"%s\" missing in this exercise" +#~ msgstr "Brakuje \"%s\" w tym ćwiczeniu" + #~ msgid "3D sound\\3D positioning of the sound" #~ msgstr "Dźwięk 3D\\Przestrzenne pozycjonowanie dźwięków" @@ -1844,6 +1851,9 @@ msgstr "epsitec.com" #~ msgid "Details\\Visual quality of 3D objects" #~ msgstr "Szczegóły\\Jakość wizualna obiektów 3D" +#~ msgid "Do not use in this exercise" +#~ msgstr "Do not use in this exercise" + #~ msgid "Do you want to quit Colobot: Gold Edition?" #~ msgstr "Czy na pewno chcesz opuścić grę Colobot: Gold Edition?" diff --git a/po/ru.po b/po/ru.po index 16f252e3..25bb06a3 100644 --- a/po/ru.po +++ b/po/ru.po @@ -59,10 +59,6 @@ msgstr "Ожидалось \" [ \"" msgid "\" ] \" missing" msgstr "Отсутствует \"]\" " -#, c-format -msgid "\"%s\" missing in this exercise" -msgstr "\"%s\" отсутствует в этом упражнении" - msgid "..behind" msgstr "Сзади" @@ -484,9 +480,6 @@ msgstr "Устройство\\Драйвер и настройки разреш msgid "Dividing by zero" msgstr "Деление на ноль (запрещено!)" -msgid "Do not use in this exercise" -msgstr "Не используй в этом упражнении" - msgid "Do you really want to destroy the selected building?" msgstr "Вы действительно хотите уничтожить выбранное здание?" @@ -1706,6 +1699,14 @@ msgstr "Вы не можете нести объекты под водой" msgid "You found a usable object" msgstr "Вы нашли рабочий объект" +#, c-format +msgid "You have to use \"%s\" at least %d times in this exercise (used: %d)" +msgstr "" + +#, c-format +msgid "You have to use \"%s\" at most %d times in this exercise (used: %d)" +msgstr "" + msgid "You must get on the spaceship to take off " msgstr "Вы должны быть на борту корабля, чтобы взлететь" @@ -1812,6 +1813,9 @@ msgstr "" msgid "epsitec.com" msgstr "www.epsitec.com" +msgid "expression expected after =" +msgstr "" + #~ msgid " " #~ msgstr " " @@ -1821,6 +1825,9 @@ msgstr "www.epsitec.com" #~ msgid " Missions on this level:" #~ msgstr " Миссии на этом уровне:" +#~ msgid "\"%s\" missing in this exercise" +#~ msgstr "\"%s\" отсутствует в этом упражнении" + #~ msgid "3D sound\\3D positioning of the sound" #~ msgstr "3D-звук\\Стерео звук" @@ -1854,6 +1861,9 @@ msgstr "www.epsitec.com" #~ msgid "Developed by :" #~ msgstr "Разработка :" +#~ msgid "Do not use in this exercise" +#~ msgstr "Не используй в этом упражнении" + #, fuzzy #~ msgid "Do you want to quit Colobot: Gold Edition?" #~ msgstr "Вы хотите закрыть COLOBOT?" diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 96e51e0d..966f9939 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -632,8 +632,8 @@ void InitializeRestext() stringsErr[ERR_DELETEMOBILE] = TR("Bot destroyed"); stringsErr[ERR_DELETEBUILDING] = TR("Building destroyed"); stringsErr[ERR_ENEMY_OBJECT] = TR("Unable to control enemy objects"); - stringsErr[ERR_OBLIGATORYTOKEN] = TR("\"%s\" missing in this exercise"); - stringsErr[ERR_PROHIBITEDTOKEN] = TR("Do not use in this exercise"); + stringsErr[ERR_OBLIGATORYTOKEN] = TR("You have to use \"%s\" at least %d times in this exercise (used: %d)"); + stringsErr[ERR_PROHIBITEDTOKEN] = TR("You have to use \"%s\" at most %d times in this exercise (used: %d)"); stringsErr[ERR_WRONG_BOT] = TR("Inappropriate bot"); stringsErr[INFO_BUILD] = TR("Building completed"); diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 07b64bbc..b59cc1f3 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -2839,8 +2839,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) m_endTakeWinDelay = 2.0f; m_endTakeLostDelay = 2.0f; m_globalMagnifyDamage = 1.0f; - m_obligatoryTotal = 0; - m_prohibitedTotal = 0; + m_obligatoryTokens.clear(); m_mapShow = true; m_mapImage = false; m_mapFilename[0] = 0; @@ -3656,25 +3655,32 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) 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) { - int i = m_obligatoryTotal; - if (i < 100) //TODO: remove the limit + std::string token = line->GetParam("text")->AsString(); + if (!line->GetParam("min")->IsDefined() && !line->GetParam("max")->IsDefined()) + GetLogger()->Warn("ObligatoryToken without specifying min/max is provided only for backwards compatibility - instead, do this: ObligatoryToken text=\"%s\" min=1\n", token.c_str()); + if (m_obligatoryTokens.count(token)) + throw CLevelParserException("Incorrect ObligatoryToken specification - you cannot define a token twice"); + + m_obligatoryTokens[token].min = line->GetParam("min")->AsInt(line->GetParam("max")->IsDefined() ? -1 : 1); // BACKWARDS COMPATIBILITY: if neither min or max are defined, default to min=1 + m_obligatoryTokens[token].max = line->GetParam("max")->AsInt(-1); + if (m_obligatoryTokens[token].min >= 0 && m_obligatoryTokens[token].max >= 0 && m_obligatoryTokens[token].min > m_obligatoryTokens[token].max) { - strcpy(m_obligatoryToken[i], line->GetParam("text")->AsString().c_str()); - m_obligatoryTotal ++; + throw CLevelParserException("Incorrect ObligatoryToken specification - min cannot be greater than max"); } 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: Kept only for backwards compatibility { - int i = m_prohibitedTotal; - if (i < 100) //TODO: remove the limit - { - strcpy(m_prohibitedToken[i], line->GetParam("text")->AsString().c_str()); - m_prohibitedTotal ++; - } + std::string token = line->GetParam("text")->AsString(); + GetLogger()->Warn("ProhibitedToken is only provided for backwards compatibility - instead, do this: ObligatoryToken text=\"%s\" max=0\n", token.c_str()); + if (m_obligatoryTokens.count(token)) + throw CLevelParserException("Incorrect ObligatoryToken specification - you cannot define a token twice"); + + m_obligatoryTokens[token].min = -1; + m_obligatoryTokens[token].max = 0; continue; } @@ -5227,41 +5233,12 @@ Error CRobotMain::CheckEndMission(bool frame) } -//! Returns the number of instructions required -int CRobotMain::GetObligatoryToken() +//! Returns the list instructions required in CBot program in level +const std::map& CRobotMain::GetObligatoryTokenList() { - return m_obligatoryTotal; + return m_obligatoryTokens; } -//! Returns the name of a required instruction -char* CRobotMain::GetObligatoryToken(int i) -{ - return m_obligatoryToken[i]; -} - -//! Checks if an instruction is part of the obligatory list -int CRobotMain::IsObligatoryToken(const char* token) -{ - for (int i = 0; i < m_obligatoryTotal; i++) - { - if (strcmp(token, m_obligatoryToken[i]) == 0) - return i; - } - return -1; -} - -//! Checks if an instruction is not part of the banned list -bool CRobotMain::IsProhibitedToken(const char* token) -{ - for (int i = 0; i < m_prohibitedTotal; i++) - { - if (strcmp(token, m_prohibitedToken[i]) == 0) - return false; - } - return true; -} - - //! Indicates whether it is possible to control a driving robot bool CRobotMain::GetTrainerPilot() { diff --git a/src/level/robotmain.h b/src/level/robotmain.h index 4f78df3d..3247813d 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -141,6 +141,12 @@ struct ShowLimit float time = 0.0f; }; +struct MinMax +{ + int min = -1; + int max = -1; +}; + const int SATCOM_HUSTON = 0; const int SATCOM_SAT = 1; @@ -199,10 +205,7 @@ public: Error CheckEndMission(bool frame); Error ProcessEndMissionTake(); Error ProcessEndMissionTakeForGroup(std::vector& endTakes); - int GetObligatoryToken(); - char* GetObligatoryToken(int i); - int IsObligatoryToken(const char* token); - bool IsProhibitedToken(const char* token); + const std::map& GetObligatoryTokenList(); void UpdateMap(); bool GetShowMap(); @@ -552,10 +555,7 @@ protected: std::vector> m_audioChange; - int m_obligatoryTotal = 0; - char m_obligatoryToken[100][20] = {}; - int m_prohibitedTotal = 0; - char m_prohibitedToken[100][20] = {}; + std::map m_obligatoryTokens; //! Enabled buildings int m_build = 0; diff --git a/src/script/script.cpp b/src/script/script.cpp index 3beac76e..924081da 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -161,46 +161,52 @@ bool CScript::CheckToken() m_error = CBot::CBotNoErr; m_title[0] = 0; m_mainFunction[0] = 0; - m_token[0] = 0; + m_token.clear(); m_bCompile = false; - std::vector used(m_main->GetObligatoryToken(), false); + std::map used; + std::map cursor1; + std::map cursor2; auto tokens = CBot::CBotToken::CompileTokens(m_script.get()); CBot::CBotToken* bt = tokens.get(); while ( bt != nullptr ) { std::string token = bt->GetString(); - int cursor1 = bt->GetStart(); - int cursor2 = bt->GetEnd(); - int i = m_main->IsObligatoryToken(token.c_str()); - if ( i != -1 ) - { - used[i] = true; // token used - } + // Store only the last occurrence of the token + cursor1[token] = bt->GetStart(); + cursor2[token] = bt->GetEnd(); - if ( !m_main->IsProhibitedToken(token.c_str()) ) - { - m_error = static_cast(ERR_PROHIBITEDTOKEN); - m_cursor1 = cursor1; - m_cursor2 = cursor2; - strcpy(m_title, ""); - m_mainFunction[0] = 0; - return false; - } + used[token]++; bt = bt->GetNext(); } - // At least once every obligatory instruction? - for (unsigned int i = 0; i < used.size(); i++) + for (const auto& it : m_main->GetObligatoryTokenList()) { - if (!used[i]) // token not used? + Error error = ERR_OK; + int allowed = 0; + if (it.second.max >= 0 && used[it.first] > it.second.max) { - strcpy(m_token, m_main->GetObligatoryToken(i)); - m_error = static_cast(ERR_OBLIGATORYTOKEN); - strcpy(m_title, ""); + error = ERR_PROHIBITEDTOKEN; + allowed = it.second.max; + m_cursor1 = cursor1[it.first]; + m_cursor2 = cursor2[it.first]; + } + if (it.second.min >= 0 && used[it.first] < it.second.min) + { + error = ERR_OBLIGATORYTOKEN; + allowed = it.second.min; + } + + if (error != ERR_OK) + { + m_token = it.first; + m_tokenUsed = used[it.first]; + m_tokenAllowed = allowed; + m_error = static_cast(error); + strcpy(m_title, ""); m_mainFunction[0] = 0; return false; } @@ -788,11 +794,11 @@ void CScript::GetError(std::string& error) } else { - if ( m_error == static_cast(ERR_OBLIGATORYTOKEN) ) + if ( m_error == static_cast(ERR_OBLIGATORYTOKEN) || m_error == static_cast(ERR_PROHIBITEDTOKEN) ) { std::string s; GetResource(RES_ERR, m_error, s); - error = StrUtils::Format(s.c_str(), m_token); + error = StrUtils::Format(s.c_str(), m_token.c_str(), m_tokenAllowed, m_tokenUsed); } else if ( m_error < 1000 ) { diff --git a/src/script/script.h b/src/script/script.h index 50f279ef..a1141714 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -122,7 +122,8 @@ protected: char m_title[50] = {}; // script title char m_mainFunction[50] = {}; char m_filename[50] = {}; // file name - char m_token[50] = {}; // missing instruction + std::string m_token = ""; // missing instruction + int m_tokenUsed = 0, m_tokenAllowed = 0; CBot::CBotError m_error = CBot::CBotNoErr; // error (0=ok) int m_cursor1 = 0; int m_cursor2 = 0;