diff --git a/CMakeLists.txt b/CMakeLists.txt index 42a6ac8b..87f0cfc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,9 @@ set(COLOBOT_VERSION_MINOR 1) set(COLOBOT_VERSION_REVISION 10) # Used on official releases -set(COLOBOT_VERSION_RELEASE_CODENAME "-alpha") +#set(COLOBOT_VERSION_RELEASE_CODENAME "-alpha") # Used on unreleased, development builds -#set(COLOBOT_VERSION_UNRELEASED "+alpha") +set(COLOBOT_VERSION_UNRELEASED "+alpha") # Append git characteristics to version if(DEFINED COLOBOT_VERSION_UNRELEASED) diff --git a/data b/data index f4c4364f..928d03d1 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit f4c4364f00afe2a754204dec387cd80124b15c3a +Subproject commit 928d03d15c1e506d3e3a81513c4a2f8607ce2bf6 diff --git a/po/colobot.pot b/po/colobot.pot index 4aee2bc0..419b88e9 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -516,25 +516,19 @@ msgstr "" msgid "Origin of last message\\Shows where the last message was sent from" msgstr "" -msgid "Speed 0.5x\\Half speed" +msgid "Lower speed\\Decrease speed by half" msgstr "" -msgid "Speed 1.0x\\Normal speed" +msgid "Standard speed\\Reset speed to normal" msgstr "" -msgid "Speed 1.5x\\1.5 times faster" +msgid "Higher speed\\Doubles speed" msgstr "" -msgid "Speed 2.0x\\Double speed" +msgid "Quick save\\Immediately save game" msgstr "" -msgid "Speed 3.0x\\Triple speed" -msgstr "" - -msgid "Speed 4.0x\\Quadruple speed" -msgstr "" - -msgid "Speed 6.0x\\Sextuple speed" +msgid "Quick load\\Immediately load game" msgstr "" msgid "Pause\\Pause the game without opening menu" @@ -1533,6 +1527,9 @@ msgstr "" msgid "Inappropriate bot" msgstr "" +msgid "Quicksave slot not found" +msgstr "" + msgid "Building completed" msgstr "" @@ -1773,6 +1770,24 @@ msgstr "" msgid "This parameter needs a default value" msgstr "" +msgid "Missing end quote" +msgstr "" + +msgid "Unknown escape sequence" +msgstr "" + +msgid "Octal value out of range" +msgstr "" + +msgid "Missing hex digits after escape sequence" +msgstr "" + +msgid "Hex value out of range" +msgstr "" + +msgid "Invalid universal character name" +msgstr "" + msgid "Dividing by zero" msgstr "" diff --git a/po/de.po b/po/de.po index 02277956..1c839a09 100644 --- a/po/de.po +++ b/po/de.po @@ -100,9 +100,7 @@ msgid "Already carrying something" msgstr "Trägt schon etwas" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" -msgstr "" -"Alternativer Kameramodus\\Seitwärts bewegen statt rotieren (bei freier " -"Kamera)" +msgstr "Alternativer Kameramodus\\Seitwärts bewegen statt rotieren (bei freier Kamera)" msgid "Ambiguous call to overloaded function" msgstr "" @@ -144,14 +142,10 @@ msgid "Automatic indent\\When program editing" msgstr "Automatisches Einrücken\\Beim Bearbeiten der Programme" msgid "Autosave interval\\How often your game will autosave" -msgstr "" -"Auto-Speichern Zeitintervall\\Wie oft das Spiel automatisch abgespeichert " -"wird" +msgstr "Auto-Speichern Zeitintervall\\Wie oft das Spiel automatisch abgespeichert wird" msgid "Autosave slots\\How many autosave slots you'll have" -msgstr "" -"Auto-Speicherplätze\\Wie viele Plätze zum automatischen Speichern zur " -"Verfügung stehen" +msgstr "Auto-Speicherplätze\\Wie viele Plätze zum automatischen Speichern zur Verfügung stehen" msgid "Autosave\\Enables autosave" msgstr "Auto-Speichern\\Aktiviert die automatische Speicherung" @@ -316,9 +310,7 @@ msgid "Camera back\\Moves the camera backward" msgstr "Kamera weiter\\Bewegung der Kamera rückwärts" msgid "Camera border scrolling\\Scrolling when the mouse touches right or left border" -msgstr "" -"Kamerabewegung am Bildschirmrand\\Die Kamera dreht wenn die Maus den rechten " -"oder linken Rand erreicht" +msgstr "Kamerabewegung am Bildschirmrand\\Die Kamera dreht wenn die Maus den rechten oder linken Rand erreicht" msgid "Camera closer\\Moves the camera forward" msgstr "Kamera näher\\Bewegung der Kamera vorwärts" @@ -688,6 +680,13 @@ msgstr "Anweisungen über das ausgewählte Objekt" msgid "Help balloons\\Explain the function of the buttons" msgstr "Hilfeblasen\\Hilfeblasen" +msgid "Hex value out of range" +msgstr "" + +#, fuzzy +msgid "Higher speed\\Doubles speed" +msgstr "Geschwindigkeit 2.0x\\Spielgeschwindigkeit doppelt so schnell" + msgid "Highest\\Highest graphic quality (lowest frame rate)" msgstr "Max.\\Beste Qualität (niedriges Framerate)" @@ -763,6 +762,9 @@ msgstr "Anweisungen\\Anweisungen für die Mission oder Übung" msgid "Internal error - tell the developers" msgstr "Interner Fehler - Benachrichtige bitte die Entwickler" +msgid "Invalid universal character name" +msgstr "" + msgid "Invert\\Invert values on this axis" msgstr "Invertieren\\Die Werte dieser Achse invertieren" @@ -838,6 +840,23 @@ msgstr "Lade Objekte" msgid "Loading terrain" msgstr "Lade Gelände" +# msgid "Speed 0.5x\\Half speed" +# msgstr "" +# msgid "Speed 1.0x\\Normal speed" +# msgstr "" +# msgid "Speed 1.5x\\1.5 times faster" +# msgstr "" +# msgid "Speed 2.0x\\Double speed" +# msgstr "" +# msgid "Speed 3.0x\\Triple speed" +# msgstr "" +# msgid "Speed 4.0x\\Quadruple speed" +# msgstr "" +# msgid "Speed 6.0x\\Sextuple speed" +# msgstr "" +msgid "Lower speed\\Decrease speed by half" +msgstr "" + msgid "Lowest\\Minimum graphic quality (highest frame rate)" msgstr "Min.\\Minimale Qualität (großes Framerate)" @@ -856,6 +875,12 @@ msgstr "Verkleinern" msgid "Mipmap level\\Mipmap level" msgstr "Mipmap-Level\\Mipmap-Level" +msgid "Missing end quote" +msgstr "" + +msgid "Missing hex digits after escape sequence" +msgstr "" + msgid "Mission name" msgstr "Name der Mission" @@ -1018,6 +1043,9 @@ msgstr "OK\\Programm kompilieren" msgid "Object too close" msgstr "Gegenstand zu nahe" +msgid "Octal value out of range" +msgstr "" + msgid "One step" msgstr "Ein Schritt" @@ -1064,9 +1092,7 @@ msgid "Pause blur\\Blur the background on the pause screen" msgstr "Pausen-Unschärfe\\Während der Pause den Hintergrund unscharf zeichnen" msgid "Pause in background\\Pause the game when the window is unfocused" -msgstr "" -"Pausieren im Hintergrund\\Spiel anhalten,enn das Spielfenster im Hintergrund " -"ist" +msgstr "Pausieren im Hintergrund\\Spiel anhalten,enn das Spielfenster im Hintergrund ist" msgid "Pause/continue" msgstr "Pause/Weitermachen" @@ -1194,6 +1220,16 @@ msgstr "Öffentlich\\Gemeinsamer Ordner für alle Spieler" msgid "Quake at explosions\\The screen shakes at explosions" msgstr "Beben bei Explosionen\\Die Kamera bebt bei Explosionen" +msgid "Quick load\\Immediately load game" +msgstr "" + +msgid "Quick save\\Immediately save game" +msgstr "" + +#, fuzzy +msgid "Quicksave slot not found" +msgstr "Das Objekt existiert nicht" + msgid "Quit\\Quit Colobot: Gold Edition" msgstr "Beenden\\Colobot: Gold Edition schließen" @@ -1404,27 +1440,6 @@ msgstr "Raumschiff" msgid "Spaceship ruin" msgstr "Raumschiffswrack" -msgid "Speed 0.5x\\Half speed" -msgstr "Geschwindigkeit 0.5x\\Halbe Spielgeschwindigkeit" - -msgid "Speed 1.0x\\Normal speed" -msgstr "Geschwindigkeit 1.0x\\Normale Spielgeschwindigkeit" - -msgid "Speed 1.5x\\1.5 times faster" -msgstr "Geschwindigkeit 1.5x\\Spielgeschwindigkeit anderthalb Mal schneller" - -msgid "Speed 2.0x\\Double speed" -msgstr "Geschwindigkeit 2.0x\\Spielgeschwindigkeit doppelt so schnell" - -msgid "Speed 3.0x\\Triple speed" -msgstr "Geschwindigkeit 3.0x\\Dreifache Spielgeschwindigkeit" - -msgid "Speed 4.0x\\Quadruple speed" -msgstr "Geschwindigkeit 4.0x\\Vierfache Spielgeschwindigkeit" - -msgid "Speed 6.0x\\Sextuple speed" -msgstr "Geschwindigkeit 6.0x\\Sechsfache Spielgeschwindigkeit" - msgid "Spider" msgstr "Spinne" @@ -1440,6 +1455,9 @@ msgstr "Standardhandlung\\Führt die Standardhandlung des Roboters aus" msgid "Standard controls\\Standard key functions" msgstr "Alles zurücksetzen\\Standarddefinition aller Tasten" +msgid "Standard speed\\Reset speed to normal" +msgstr "" + msgid "Standard\\Standard appearance settings" msgstr "Standard\\Standardfarben einsetzen" @@ -1525,9 +1543,7 @@ msgid "This label does not exist" msgstr "Dieses Label existiert nicht" msgid "This menu is for userlevels from mods, but you didn't install any" -msgstr "" -"Dieses Menü ist für nachinstallierte Benutzer-Level, aber Du hast keine " -"installiert" +msgstr "Dieses Menü ist für nachinstallierte Benutzer-Level, aber Du hast keine installiert" msgid "This object is currently busy" msgstr "" @@ -1631,6 +1647,9 @@ msgstr "Das Objekt existiert nicht" msgid "Unknown command" msgstr "Befehl unbekannt" +msgid "Unknown escape sequence" +msgstr "" + msgid "Unknown function" msgstr "Unbekannte Funktion" @@ -1740,22 +1759,14 @@ msgstr "Sie haben ein brauchbares Objekt gefunden" #, c-format msgid "You have to use \"%1$s\" at least once in this exercise (used: %2$d)" msgid_plural "You have to use \"%1$s\" at least %3$d times in this exercise (used: %2$d)" -msgstr[0] "" -"In dieser Übung \"%1$s\" muß mindestens einmal verwendet werden (benutzt: %" -"2$d)" -msgstr[1] "" -"In dieser Übung muß \"%1$s\" mindestes %3$d Mal verwendet werden (benutzt: %" -"2$d)" +msgstr[0] "In dieser Übung \"%1$s\" muß mindestens einmal verwendet werden (benutzt: %2$d)" +msgstr[1] "In dieser Übung muß \"%1$s\" mindestes %3$d Mal verwendet werden (benutzt: %2$d)" #, c-format msgid "You have to use \"%1$s\" at most once in this exercise (used: %2$d)" msgid_plural "You have to use \"%1$s\" at most %3$d times in this exercise (used: %2$d)" -msgstr[0] "" -"In dieser Übung darf \"%1$s\" höchstens einmal verwendet werden (benutzt: %" -"2$d)" -msgstr[1] "" -"In dieser Übung darf \"%1$s\" höchstens %3$d Mal verwendet werden (benutzt: %" -"2$d)" +msgstr[0] "In dieser Übung darf \"%1$s\" höchstens einmal verwendet werden (benutzt: %2$d)" +msgstr[1] "In dieser Übung darf \"%1$s\" höchstens %3$d Mal verwendet werden (benutzt: %2$d)" msgid "You must get on the spaceship to take off" msgstr "Begib Dich an Bord, bevor Du abhebst" @@ -1950,9 +1961,6 @@ msgstr "epsitec.com" #~ msgid "Num of decorative objects\\Number of purely ornamental objects" #~ msgstr "Anzahl Ziergegenstände\\Anzahl Gegenstände ohne Funktion" -#~ msgid "Object not found" -#~ msgstr "Das Objekt existiert nicht" - #~ msgid "Planets and stars\\Astronomical objects in the sky" #~ msgstr "Planeten und Sterne\\Kreisende Planeten und Sterne" @@ -1968,9 +1976,27 @@ msgstr "epsitec.com" #~ msgid "Sky\\Clouds and nebulae" #~ msgstr "Himmel\\Himmel und Wolken" +#~ msgid "Speed 0.5x\\Half speed" +#~ msgstr "Geschwindigkeit 0.5x\\Halbe Spielgeschwindigkeit" + +#~ msgid "Speed 1.0x\\Normal speed" +#~ msgstr "Geschwindigkeit 1.0x\\Normale Spielgeschwindigkeit" + +#~ msgid "Speed 1.5x\\1.5 times faster" +#~ msgstr "Geschwindigkeit 1.5x\\Spielgeschwindigkeit anderthalb Mal schneller" + #~ msgid "Speed 3.0x\\Three times faster" #~ msgstr "Geschwindigkeit 3.0x\\Spielgeschwindigkeit drei Mal schneller" +#~ msgid "Speed 3.0x\\Triple speed" +#~ msgstr "Geschwindigkeit 3.0x\\Dreifache Spielgeschwindigkeit" + +#~ msgid "Speed 4.0x\\Quadruple speed" +#~ msgstr "Geschwindigkeit 4.0x\\Vierfache Spielgeschwindigkeit" + +#~ msgid "Speed 6.0x\\Sextuple speed" +#~ msgstr "Geschwindigkeit 6.0x\\Sechsfache Spielgeschwindigkeit" + #~ msgid "Sunbeams\\Sunbeams in the sky" #~ msgstr "Sonnenstrahlen\\Sonnenstrahlen" diff --git a/po/fr.po b/po/fr.po index 2de5e5ab..a2877175 100644 --- a/po/fr.po +++ b/po/fr.po @@ -677,6 +677,13 @@ msgstr "Instructions sur la sélection" msgid "Help balloons\\Explain the function of the buttons" msgstr "Bulles d'aide\\Bulles explicatives" +msgid "Hex value out of range" +msgstr "" + +#, fuzzy +msgid "Higher speed\\Doubles speed" +msgstr "Vitesse 2.0x\\Deux fois plus rapide" + msgid "Highest\\Highest graphic quality (lowest frame rate)" msgstr "Maxi\\Haute qualité (+ lent)" @@ -752,6 +759,9 @@ msgstr "Instructions mission\\Marche à suivre" msgid "Internal error - tell the developers" msgstr "Erreur interne - contacter les développeurs" +msgid "Invalid universal character name" +msgstr "" + msgid "Invert\\Invert values on this axis" msgstr "Inversion\\Inverse les valeurs sur cet axe" @@ -827,6 +837,23 @@ msgstr "Chargement des objets" msgid "Loading terrain" msgstr "Chargement du terrain" +# msgid "Speed 0.5x\\Half speed" +# msgstr "" +# msgid "Speed 1.0x\\Normal speed" +# msgstr "" +# msgid "Speed 1.5x\\1.5 times faster" +# msgstr "" +# msgid "Speed 2.0x\\Double speed" +# msgstr "" +# msgid "Speed 3.0x\\Triple speed" +# msgstr "" +# msgid "Speed 4.0x\\Quadruple speed" +# msgstr "" +# msgid "Speed 6.0x\\Sextuple speed" +# msgstr "" +msgid "Lower speed\\Decrease speed by half" +msgstr "" + msgid "Lowest\\Minimum graphic quality (highest frame rate)" msgstr "Mini\\Qualité minimale (+ rapide)" @@ -845,6 +872,12 @@ msgstr "Taille réduite" msgid "Mipmap level\\Mipmap level" msgstr "Niveau de MIP mapping\\Niveau de MIP mapping" +msgid "Missing end quote" +msgstr "" + +msgid "Missing hex digits after escape sequence" +msgstr "" + msgid "Mission name" msgstr "Nom de la mission" @@ -1007,6 +1040,9 @@ msgstr "D'accord\\Compiler le programme" msgid "Object too close" msgstr "Objet trop proche" +msgid "Octal value out of range" +msgstr "" + msgid "One step" msgstr "Un pas" @@ -1181,6 +1217,16 @@ msgstr "Public\\Dossier commun à tous les joueurs" msgid "Quake at explosions\\The screen shakes at explosions" msgstr "Secousses lors d'explosions\\L'écran vibre lors d'une explosion" +msgid "Quick load\\Immediately load game" +msgstr "" + +msgid "Quick save\\Immediately save game" +msgstr "" + +#, fuzzy +msgid "Quicksave slot not found" +msgstr "Objet n'existe pas" + msgid "Quit\\Quit Colobot: Gold Edition" msgstr "Quitter\\Quitter Colobot : Édition Gold" @@ -1391,27 +1437,6 @@ msgstr "Vaisseau spatial" msgid "Spaceship ruin" msgstr "Epave de vaisseau spatial" -msgid "Speed 0.5x\\Half speed" -msgstr "Vitesse 0.5x\\Demi-vitesse" - -msgid "Speed 1.0x\\Normal speed" -msgstr "Vitesse 1.0x\\Vitesse normale" - -msgid "Speed 1.5x\\1.5 times faster" -msgstr "Vitesse 1.5x\\Une fois et demi plus rapide" - -msgid "Speed 2.0x\\Double speed" -msgstr "Vitesse 2.0x\\Deux fois plus rapide" - -msgid "Speed 3.0x\\Triple speed" -msgstr "Vitesse 3.0x\\Trois fois plus rapide " - -msgid "Speed 4.0x\\Quadruple speed" -msgstr "Vitesse 4.0x\\Quatre fois plus rapide" - -msgid "Speed 6.0x\\Sextuple speed" -msgstr "Vitesse 6.0x\\Six fois plus rapide" - msgid "Spider" msgstr "Araignée" @@ -1427,6 +1452,9 @@ msgstr "Action standard\\Action du bouton avec le cadre rouge" msgid "Standard controls\\Standard key functions" msgstr "Tout réinitialiser\\Remet toutes les touches standards" +msgid "Standard speed\\Reset speed to normal" +msgstr "" + msgid "Standard\\Standard appearance settings" msgstr "Standard\\Remet les couleurs standards" @@ -1616,6 +1644,9 @@ msgstr "Objet n'existe pas" msgid "Unknown command" msgstr "Commande inconnue" +msgid "Unknown escape sequence" +msgstr "" + msgid "Unknown function" msgstr "Routine inconnue" @@ -1932,9 +1963,6 @@ msgstr "epsitec.com" #~ msgid "Num of decorative objects\\Number of purely ornamental objects" #~ msgstr "Nb d'objets décoratifs\\Qualité d'objets non indispensables" -#~ msgid "Object not found" -#~ msgstr "Objet n'existe pas" - #~ msgid "Planets and stars\\Astronomical objects in the sky" #~ msgstr "Planètes et étoiles\\Motifs mobiles dans le ciel" @@ -1950,9 +1978,27 @@ msgstr "epsitec.com" #~ msgid "Sky\\Clouds and nebulae" #~ msgstr "Ciel\\Ciel et nuages" +#~ msgid "Speed 0.5x\\Half speed" +#~ msgstr "Vitesse 0.5x\\Demi-vitesse" + +#~ msgid "Speed 1.0x\\Normal speed" +#~ msgstr "Vitesse 1.0x\\Vitesse normale" + +#~ msgid "Speed 1.5x\\1.5 times faster" +#~ msgstr "Vitesse 1.5x\\Une fois et demi plus rapide" + #~ msgid "Speed 3.0x\\Three times faster" #~ msgstr "Vitesse 3.0x\\Trois fois plus rapide" +#~ msgid "Speed 3.0x\\Triple speed" +#~ msgstr "Vitesse 3.0x\\Trois fois plus rapide " + +#~ msgid "Speed 4.0x\\Quadruple speed" +#~ msgstr "Vitesse 4.0x\\Quatre fois plus rapide" + +#~ msgid "Speed 6.0x\\Sextuple speed" +#~ msgstr "Vitesse 6.0x\\Six fois plus rapide" + #~ msgid "Sunbeams\\Sunbeams in the sky" #~ msgstr "Rayons du soleil\\Rayons selon l'orientation" diff --git a/po/pl.po b/po/pl.po index 2995e516..bb288518 100644 --- a/po/pl.po +++ b/po/pl.po @@ -678,6 +678,12 @@ msgstr "Pomoc na temat zaznaczonego obiektu" msgid "Help balloons\\Explain the function of the buttons" msgstr "Dymki pomocy\\Wyjaśnia funkcje przycisków" +msgid "Hex value out of range" +msgstr "" + +msgid "Higher speed\\Doubles speed" +msgstr "Zwiększ prędkość\\Podwaja prędkość" + msgid "Highest\\Highest graphic quality (lowest frame rate)" msgstr "Najwyższa\\Maksymalna jakość grafiki (najniższa częstotliwość odświeżania)" @@ -753,6 +759,9 @@ msgstr "Rozkazy\\Pokazuje rozkazy dotyczące bieżącej misji" msgid "Internal error - tell the developers" msgstr "Błąd wewnętrzny - powiadom twórców gry" +msgid "Invalid universal character name" +msgstr "" + msgid "Invert\\Invert values on this axis" msgstr "Odwróć\\Odwróć wartości na tej osi" @@ -828,6 +837,9 @@ msgstr "Wczytywanie obiektów" msgid "Loading terrain" msgstr "Wczytywanie terenu" +msgid "Lower speed\\Decrease speed by half" +msgstr "Zmniejsz prędkość\\Zmniejsza prędkość o połowę" + msgid "Lowest\\Minimum graphic quality (highest frame rate)" msgstr "Najniższa\\Minimalna jakość grafiki (najwyższa częstotliwość odświeżania)" @@ -846,6 +858,12 @@ msgstr "Pomniejsz" msgid "Mipmap level\\Mipmap level" msgstr "Poziom mipmap\\Poziom mipmap" +msgid "Missing end quote" +msgstr "" + +msgid "Missing hex digits after escape sequence" +msgstr "" + msgid "Mission name" msgstr "Nazwa misji" @@ -1008,6 +1026,9 @@ msgstr "OK\\Zamyka edytor programu i powraca do gry" msgid "Object too close" msgstr "Obiekt za blisko" +msgid "Octal value out of range" +msgstr "" + msgid "One step" msgstr "Jeden krok" @@ -1182,6 +1203,15 @@ msgstr "Publiczny\\Folder ogólnodostępny" msgid "Quake at explosions\\The screen shakes at explosions" msgstr "Wstrząsy przy wybuchach\\Ekran trzęsie się podczas wybuchów" +msgid "Quick load\\Immediately load game" +msgstr "Szybkie wczytywanie\\Natychmiastowo wczytuje zapisaną grę" + +msgid "Quick save\\Immediately save game" +msgstr "Szybki zapis\\Natychmiastowo zapisuje grę" + +msgid "Quicksave slot not found" +msgstr "Nie odnaleziono slotu szybkiego zapisu" + msgid "Quit\\Quit Colobot: Gold Edition" msgstr "Wyjdź\\Kończy grę Colobot: Gold Edition" @@ -1392,27 +1422,6 @@ msgstr "Statek kosmiczny" msgid "Spaceship ruin" msgstr "Ruiny statku kosmicznego" -msgid "Speed 0.5x\\Half speed" -msgstr "Prędkość 0,5x\\Połowa prękości" - -msgid "Speed 1.0x\\Normal speed" -msgstr "Prędkość 1,0x\\Prędkość normalna" - -msgid "Speed 1.5x\\1.5 times faster" -msgstr "Prędkość 1,5x\\1,5 raza szybciej" - -msgid "Speed 2.0x\\Double speed" -msgstr "Prędkość 2,0x\\Dwa razy szybciej" - -msgid "Speed 3.0x\\Triple speed" -msgstr "Prędkość 3,0x\\Trzy razy szybciej" - -msgid "Speed 4.0x\\Quadruple speed" -msgstr "Prędkość 4,0x\\Cztery razy szybciej" - -msgid "Speed 6.0x\\Sextuple speed" -msgstr "Prędkość 6,0x\\Sześć razy szybciej" - msgid "Spider" msgstr "Pająk" @@ -1428,6 +1437,9 @@ msgstr "Standardowa akcja\\Standardowa akcja robota (podnieś/upuść, strzelaj, msgid "Standard controls\\Standard key functions" msgstr "Standardowa kontrola\\Standardowe klawisze funkcyjne" +msgid "Standard speed\\Reset speed to normal" +msgstr "Standardowa prędkość\\Resetuje prędkość do wartości domyślnej" + msgid "Standard\\Standard appearance settings" msgstr "Standardowe\\Standardowe ustawienia wyglądu" @@ -1617,6 +1629,9 @@ msgstr "Obiekt nieznany" msgid "Unknown command" msgstr "Nieznane polecenie" +msgid "Unknown escape sequence" +msgstr "" + msgid "Unknown function" msgstr "Funkcja nieznana" diff --git a/po/ru.po b/po/ru.po index b460b77a..f2b25354 100644 --- a/po/ru.po +++ b/po/ru.po @@ -686,6 +686,13 @@ msgstr "Справка о выбранном объекте" msgid "Help balloons\\Explain the function of the buttons" msgstr "Подсказки\\Объяснение функций кнопок" +msgid "Hex value out of range" +msgstr "" + +#, fuzzy +msgid "Higher speed\\Doubles speed" +msgstr "Скорость 2.0х\\В два раза быстрее" + msgid "Highest\\Highest graphic quality (lowest frame rate)" msgstr "Высок.\\Самые высокие настройки графики (лучшее качество)" @@ -761,6 +768,9 @@ msgstr "Инструкции\\Показывает инструкции по т msgid "Internal error - tell the developers" msgstr "Внутренняя ошибка - сообщите разработчикам" +msgid "Invalid universal character name" +msgstr "" + msgid "Invert\\Invert values on this axis" msgstr "Инвертир.\\Инвертировать значения по этой оси" @@ -836,6 +846,23 @@ msgstr "Загрузка объектов" msgid "Loading terrain" msgstr "Загрузка местности" +# msgid "Speed 0.5x\\Half speed" +# msgstr "" +# msgid "Speed 1.0x\\Normal speed" +# msgstr "" +# msgid "Speed 1.5x\\1.5 times faster" +# msgstr "" +# msgid "Speed 2.0x\\Double speed" +# msgstr "" +# msgid "Speed 3.0x\\Triple speed" +# msgstr "" +# msgid "Speed 4.0x\\Quadruple speed" +# msgstr "" +# msgid "Speed 6.0x\\Sextuple speed" +# msgstr "" +msgid "Lower speed\\Decrease speed by half" +msgstr "" + msgid "Lowest\\Minimum graphic quality (highest frame rate)" msgstr "Низкое\\Минимальное качество графики (быстро)" @@ -854,6 +881,12 @@ msgstr "Свернуть" msgid "Mipmap level\\Mipmap level" msgstr "Уровень уменьшающей фильтрации\\Уровень уменьшающей фильтрации" +msgid "Missing end quote" +msgstr "" + +msgid "Missing hex digits after escape sequence" +msgstr "" + msgid "Mission name" msgstr "Название миссии" @@ -1018,6 +1051,9 @@ msgstr "ОК\\Закрыть редактор программ и вернуть msgid "Object too close" msgstr "Объект слишком близок" +msgid "Octal value out of range" +msgstr "" + msgid "One step" msgstr "Один шаг" @@ -1193,6 +1229,16 @@ msgstr "Общее\\Общая папка" msgid "Quake at explosions\\The screen shakes at explosions" msgstr "Землетряс. при взрывах\\Тряска экрана при взрывах" +msgid "Quick load\\Immediately load game" +msgstr "" + +msgid "Quick save\\Immediately save game" +msgstr "" + +#, fuzzy +msgid "Quicksave slot not found" +msgstr "Объект не найден" + msgid "Quit\\Quit Colobot: Gold Edition" msgstr "Выход\\Выйти из Colobot: Gold Edition" @@ -1406,27 +1452,6 @@ msgstr "Космический корабль" msgid "Spaceship ruin" msgstr "Обломки корабля" -msgid "Speed 0.5x\\Half speed" -msgstr "Скорость 0.5х\\В два раза медленнее" - -msgid "Speed 1.0x\\Normal speed" -msgstr "Скорость 1.0х\\Нормальная скорость" - -msgid "Speed 1.5x\\1.5 times faster" -msgstr "Скорость 1.5х\\В полтора раза быстрее" - -msgid "Speed 2.0x\\Double speed" -msgstr "Скорость 2.0х\\В два раза быстрее" - -msgid "Speed 3.0x\\Triple speed" -msgstr "Скорость 3.0х\\В три раза быстрее" - -msgid "Speed 4.0x\\Quadruple speed" -msgstr "Скорость 4.0х\\В четыре раза быстрее" - -msgid "Speed 6.0x\\Sextuple speed" -msgstr "Скорость 6.0х\\В шесть раз быстрее" - msgid "Spider" msgstr "Паук" @@ -1442,6 +1467,9 @@ msgstr "Стандартное действие\\Стандартное дейс msgid "Standard controls\\Standard key functions" msgstr "Стандартное управление\\Сделать управление по умолчанию" +msgid "Standard speed\\Reset speed to normal" +msgstr "" + msgid "Standard\\Standard appearance settings" msgstr "По умолчанию\\Настройки внешнего вида по умолчанию" @@ -1632,6 +1660,9 @@ msgstr "Неизвестный объект" msgid "Unknown command" msgstr "Неизвестная команда" +msgid "Unknown escape sequence" +msgstr "" + msgid "Unknown function" msgstr "Неизвестная функция" @@ -1942,9 +1973,6 @@ msgstr "epsitec.com" #~ msgid "Num of decorative objects\\Number of purely ornamental objects" #~ msgstr "Количество декораций\\Количество декоративных объектов" -#~ msgid "Object not found" -#~ msgstr "Объект не найден" - #~ msgid "Planets and stars\\Astronomical objects in the sky" #~ msgstr "Планеты и звезды\\Астрономические объекты в небе" @@ -1960,9 +1988,27 @@ msgstr "epsitec.com" #~ msgid "Sky\\Clouds and nebulae" #~ msgstr "Небо\\Облака и туманности" +#~ msgid "Speed 0.5x\\Half speed" +#~ msgstr "Скорость 0.5х\\В два раза медленнее" + +#~ msgid "Speed 1.0x\\Normal speed" +#~ msgstr "Скорость 1.0х\\Нормальная скорость" + +#~ msgid "Speed 1.5x\\1.5 times faster" +#~ msgstr "Скорость 1.5х\\В полтора раза быстрее" + #~ msgid "Speed 3.0x\\Three times faster" #~ msgstr "Скорость 3.0х\\В три раза быстрее" +#~ msgid "Speed 3.0x\\Triple speed" +#~ msgstr "Скорость 3.0х\\В три раза быстрее" + +#~ msgid "Speed 4.0x\\Quadruple speed" +#~ msgstr "Скорость 4.0х\\В четыре раза быстрее" + +#~ msgid "Speed 6.0x\\Sextuple speed" +#~ msgstr "Скорость 6.0х\\В шесть раз быстрее" + #~ msgid "Sunbeams\\Sunbeams in the sky" #~ msgstr "Солнечные лучи\\Солнечные лучи в небе" diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index f98d4e96..fb701bc4 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -241,6 +241,12 @@ enum CBotError : int CBotErrNoClassName = 5046, //!< class name expected CBotErrNoReturn = 5047, //!< non-void function needs "return;" CBotErrDefaultValue = 5048, //!< this parameter needs a default value + CBotErrEndQuote = 5049, //!< missing end quote + CBotErrBadEscape = 5050, //!< unknown escape sequence + CBotErrOctalRange = 5051, //!< octal value out of range + CBotErrHexDigits = 5052, //!< missing hex digits after escape sequence + CBotErrHexRange = 5053, //!< hex value out of range + CBotErrUnicodeName = 5054, //!< invalid universal character name // Runtime errors CBotErrZeroDiv = 6000, //!< division by zero diff --git a/src/CBot/CBotInstr/CBotExprLitString.cpp b/src/CBot/CBotInstr/CBotExprLitString.cpp index ad85f7a4..58d5fe16 100644 --- a/src/CBot/CBotInstr/CBotExprLitString.cpp +++ b/src/CBot/CBotInstr/CBotExprLitString.cpp @@ -42,15 +42,136 @@ CBotInstr* CBotExprLitString::Compile(CBotToken* &p, CBotCStack* pStack) { CBotCStack* pStk = pStack->TokenStack(); - CBotExprLitString* inst = new CBotExprLitString(); + std::string s = p->GetString(); - inst->SetToken(p); - p = p->GetNext(); + auto it = s.cbegin(); + if (++it != s.cend()) + { + int pos = p->GetStart(); + std::string valstring = ""; + while (it != s.cend() && *it != '\"') + { + pStk->SetStartError(++pos); + if (*it != '\\') // not escape sequence ? + { + valstring += *(it++); + continue; + } - CBotVar* var = CBotVar::Create("", CBotTypString); - pStk->SetVar(var); + if (++it == s.cend()) break; - return pStack->Return(inst, pStk); + if (CharInList(*it, "01234567")) // octal + { + std::string octal = ""; + + for (int i = 0; i < 3; i++) + { + if (!CharInList(*it, "01234567")) break; + ++pos; + octal += *it; + if (++it == s.cend()) break; + } + + unsigned int val = std::stoi(octal, nullptr, 8); + if (val <= 255) + { + valstring.push_back(val); + continue; + } + pStk->SetError(CBotErrOctalRange, pos + 1); + } + else + { + ++pos; + unsigned char c = *(it++); + if (c == '\"' || c == '\'' || c == '\\') valstring += c; + else if (c == 'a') valstring += '\a'; // alert bell + else if (c == 'b') valstring += '\b'; // backspace + else if (c == 'f') valstring += '\f'; // form feed + else if (c == 'n') valstring += '\n'; // new line + else if (c == 'r') valstring += '\r'; // carriage return + else if (c == 't') valstring += '\t'; // horizontal tab + else if (c == 'v') valstring += '\v'; // vertical tab + else if (c == 'x' || c == 'u' || c == 'U') // hex or unicode + { + if (it != s.cend()) + { + std::string hex = ""; + bool isHexCode = (c == 'x'); + size_t maxlen = (c == 'u') ? 4 : 8; + + for (size_t i = 0; isHexCode || i < maxlen; i++) + { + if (!CharInList(*it, "0123456789ABCDEFabcdef")) break; + ++pos; + hex += *it; + if (++it == s.cend()) break; + } + + if (!hex.empty()) + { + unsigned int val = 0; + try + { + val = std::stoi(hex, nullptr, 16); + } + catch (const std::out_of_range& e) + { + pStk->SetError(CBotErrHexRange, pos + 1); + } + + if (pStk->IsOk()) + { + if (isHexCode) // hexadecimal + { + if (val <= 255) + { + valstring.push_back(val); + continue; + } + pStk->SetError(CBotErrHexRange, pos + 1); + } + else if (maxlen == hex.length()) // unicode character + { + if (val < 0xD800 || (0xDFFF < val && val < 0x110000)) + { + valstring += CodePointToUTF8(val); + continue; + } + pStk->SetError(CBotErrUnicodeName, pos + 1); + } + } + } + } + + pStk->SetError(CBotErrHexDigits, pos + 1); + } + else + pStk->SetError(CBotErrBadEscape, pos + 1); // unknown escape code + } + + if (!pStk->IsOk()) break; + } + + if (it == s.cend() || *it != '\"') + pStk->SetError(CBotErrEndQuote, p); + + if (pStk->IsOk()) + { + CBotExprLitString* inst = new CBotExprLitString(); + inst->m_valstring.swap(valstring); + inst->SetToken(p); + p = p->GetNext(); + + CBotVar* var = CBotVar::Create("", CBotTypString); + pStk->SetVar(var); + + return pStack->Return(inst, pStk); + } + } + + pStk->SetError(CBotErrEndQuote, p); + return pStack->Return(nullptr, pStk); } //////////////////////////////////////////////////////////////////////////////// @@ -62,10 +183,7 @@ bool CBotExprLitString::Execute(CBotStack* &pj) CBotVar* var = CBotVar::Create("", CBotTypString); - std::string chaine = m_token.GetString(); - chaine = chaine.substr(1, chaine.length()-2); // removes the quotes - - var->SetValString(chaine); // value of the number + var->SetValString(m_valstring); pile->SetVar(var); // put on the stack diff --git a/src/CBot/CBotInstr/CBotExprLitString.h b/src/CBot/CBotInstr/CBotExprLitString.h index 4336dd15..55c39650 100644 --- a/src/CBot/CBotInstr/CBotExprLitString.h +++ b/src/CBot/CBotInstr/CBotExprLitString.h @@ -58,6 +58,9 @@ public: protected: virtual const std::string GetDebugName() override { return "CBotExprLitString"; } virtual std::string GetDebugData() override; + +private: + std::string m_valstring = ""; }; } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotExprVar.cpp b/src/CBot/CBotInstr/CBotExprVar.cpp index ed77d8ac..ed20c6fd 100644 --- a/src/CBot/CBotInstr/CBotExprVar.cpp +++ b/src/CBot/CBotInstr/CBotExprVar.cpp @@ -76,6 +76,8 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bCheckRe // This is an element of the current class // ads the equivalent of this. before CBotToken token("this"); + // invisible 'this.' highlights member token on error + token.SetPos(p->GetStart(), p->GetEnd()); inst->SetToken(&token); (static_cast(inst))->m_nIdent = -2; // identificator for this @@ -196,6 +198,8 @@ CBotInstr* CBotExprVar::CompileMethode(CBotToken* &p, CBotCStack* pStack) // this is an element of the current class // adds the equivalent of this. before + // invisible 'this.' highlights member token on error + pthis.SetPos(pp->GetStart(), pp->GetEnd()); inst->SetToken(&pthis); (static_cast(inst))->m_nIdent = -2; // ident for this diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp index c24f9861..13b9005b 100644 --- a/src/CBot/CBotInstr/CBotFunction.cpp +++ b/src/CBot/CBotInstr/CBotFunction.cpp @@ -464,6 +464,7 @@ void CBotFunction::RestoreState(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInst { CBotVar* pThis = pile->FindVar("this"); pThis->SetInit(CBotVar::InitType::IS_POINTER); + pThis->SetPointer(pInstance); pThis->SetUniqNum(-2); } @@ -672,6 +673,7 @@ int CBotFunction::DoCall(CBotProgram* program, const std::list& l { CBotTypResult type; CBotFunction* pt = nullptr; + CBotProgram* baseProg = pStack->GetProgram(true); pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type); @@ -695,7 +697,7 @@ int CBotFunction::DoCall(CBotProgram* program, const std::list& l if (pStk3b->GetState() == 0 && !pt->m_MasterClass.empty()) { - CBotVar* pInstance = program->m_thisVar; + CBotVar* pInstance = (baseProg != nullptr) ? baseProg->m_thisVar : nullptr; // make "this" known CBotVar* pThis ; if ( pInstance == nullptr ) @@ -762,8 +764,7 @@ void CBotFunction::RestoreCall(const std::list& localFunctionList CBotFunction* pt = nullptr; CBotStack* pStk1; CBotStack* pStk3; - - // search function to return the ok identifier + CBotProgram* baseProg = pStack->GetProgram(true); pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type); @@ -792,10 +793,11 @@ void CBotFunction::RestoreCall(const std::list& localFunctionList { if ( !pt->m_MasterClass.empty() ) { -// CBotVar* pInstance = m_pProg->m_thisVar; + CBotVar* pInstance = (baseProg != nullptr) ? baseProg->m_thisVar : nullptr; // make "this" known CBotVar* pThis = pStk1->FindVar("this"); pThis->SetInit(CBotVar::InitType::IS_POINTER); + pThis->SetPointer(pInstance); pThis->SetUniqNum(-2); } } diff --git a/src/CBot/CBotInstr/CBotLeftExpr.cpp b/src/CBot/CBotInstr/CBotLeftExpr.cpp index 4c4400a9..3c701eb8 100644 --- a/src/CBot/CBotInstr/CBotLeftExpr.cpp +++ b/src/CBot/CBotInstr/CBotLeftExpr.cpp @@ -72,6 +72,8 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) // this is an element of the current class // adds the equivalent of this. before CBotToken pthis("this"); + // invisible 'this.' highlights member token on error + pthis.SetPos(p->GetStart(), p->GetEnd()); inst->SetToken(&pthis); inst->m_nIdent = -2; // indent for this diff --git a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp index 41321032..54db5976 100644 --- a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp +++ b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp @@ -405,9 +405,20 @@ bool CBotTwoOpExpr::Execute(CBotStack* &pStack) // creates a variable for the result CBotVar* result = CBotVar::Create("", TypeRes); + // get left and right operands + CBotVar* left = pStk1->GetVar(); + CBotVar* right = pStk2->GetVar(); + // creates a variable to perform the calculation in the appropriate type if ( TypeRes != CBotTypString ) // keep string conversion + { TypeRes = std::max(type1.GetType(), type2.GetType()); + } + else + { + left->Update(nullptr); + right->Update(nullptr); + } if ( GetTokenType() == ID_ADD && type1.Eq(CBotTypString) ) { @@ -422,8 +433,6 @@ bool CBotTwoOpExpr::Execute(CBotStack* &pStack) CBotError err = CBotNoErr; // is a operation according to request - CBotVar* left = pStk1->GetVar(); - CBotVar* right = pStk2->GetVar(); switch (GetTokenType()) { diff --git a/src/CBot/CBotToken.cpp b/src/CBot/CBotToken.cpp index d9e33ffd..3c4bc7d9 100644 --- a/src/CBot/CBotToken.cpp +++ b/src/CBot/CBotToken.cpp @@ -241,23 +241,13 @@ void CBotToken::SetPos(int start, int end) } //////////////////////////////////////////////////////////////////////////////// -bool CharInList(const char c, const char* list) -{ - int i = 0; - while (true) - { - if (c == list[i++]) return true; - if (list[i] == 0) return false; - } -} - -static char sep1[] = " \r\n\t,:()[]{}-+*/=;>!~^|&%."; // operational separators +static char sep3[] = ",:()[]{}-+*/=;<>!~^|&%.?"; // operational separators static char num[] = "0123456789"; // point (single) is tested separately static char hexnum[] = "0123456789ABCDEFabcdef"; -static char nch[] = "\"\r\n\t"; // forbidden in chains +static char nch[] = "\r\n\t"; // forbidden in chains //////////////////////////////////////////////////////////////////////////////// CBotToken* CBotToken::NextToken(const char*& program, bool first) @@ -278,14 +268,13 @@ CBotToken* CBotToken::NextToken(const char*& program, bool first) // special case for strings if (token[0] == '\"' ) { - while (c != 0 && !CharInList(c, nch)) + while (c != 0 && c != '\"' && !CharInList(c, nch)) { if ( c == '\\' ) { - c = *(program++); // next character - if ( c == 'n' ) c = '\n'; - if ( c == 'r' ) c = '\r'; - if ( c == 't' ) c = '\t'; + token += c; + c = *(program++); + if (c == 0 || CharInList(c, nch)) break; } token += c; c = *(program++); diff --git a/src/CBot/CBotUtils.cpp b/src/CBot/CBotUtils.cpp index 4e289317..2bb4d8e8 100644 --- a/src/CBot/CBotUtils.cpp +++ b/src/CBot/CBotUtils.cpp @@ -236,4 +236,49 @@ float GetNumFloat(const std::string& str) return static_cast(num); } +bool CharInList(const char c, const char* list) +{ + int i = 0; + + while (list[i] != 0) + { + if (c == list[i++]) return true; + } + + return false; +} + +std::string CodePointToUTF8(unsigned int val) +{ + std::string s = ""; + + if (val < 0xD800 || (0xDFFF < val && val < 0x110000)) + { + if (val < 0x80) + { + s.push_back(val); + } + else if (val < 0x800) + { + s.push_back(0xC0 + (val >> 6)); + s.push_back(0x80 + (val & 0x3F)); + } + else if (val < 0x10000) + { + s.push_back(0xE0 + (val >> 12)); + s.push_back(0x80 + ((val >> 6) & 0x3F)); + s.push_back(0x80 + (val & 0x3F)); + } + else + { + s.push_back(0xF0 + (val >> 18)); + s.push_back(0x80 + ((val >> 12) & 0x3F)); + s.push_back(0x80 + ((val >> 6) & 0x3F)); + s.push_back(0x80 + (val & 0x3F)); + } + } + + return s; +} + } // namespace CBot diff --git a/src/CBot/CBotUtils.h b/src/CBot/CBotUtils.h index 418e10bf..6db107a9 100644 --- a/src/CBot/CBotUtils.h +++ b/src/CBot/CBotUtils.h @@ -96,6 +96,21 @@ long GetNumInt(const std::string& str); */ float GetNumFloat(const std::string& str); +/*! + * \brief Search a null-terminated string for a char value. + * \param c The char to find. + * \param list The string to search. + * \return true if the char is found. + */ +bool CharInList(const char c, const char* list); + +/*! + * \brief Converts a Unicode code point to UTF-8 encoded character. + * \param val Code point value. + * \return UTF-8 encoded string or empty string. + */ +std::string CodePointToUTF8(unsigned int val); + template class CBotLinkedList { public: diff --git a/src/app/app.cpp b/src/app/app.cpp index f019ccc0..c780e20e 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -61,9 +61,6 @@ #include #include - -template<> CApplication* CSingleton::m_instance = nullptr; - char CApplication::m_languageLocale[] = { 0 }; diff --git a/src/app/input.cpp b/src/app/input.cpp index 87cedf34..ebaf62e3 100644 --- a/src/app/input.cpp +++ b/src/app/input.cpp @@ -32,9 +32,6 @@ #include #include - -template<> CInput* CSingleton::m_instance = nullptr; - CInput::CInput() : m_keyPresses() { @@ -62,13 +59,11 @@ CInput::CInput() { INPUT_SLOT_HELP, "help" }, { INPUT_SLOT_PROG, "prog" }, { INPUT_SLOT_VISIT, "visit" }, - { INPUT_SLOT_SPEED05, "speed05" }, - { INPUT_SLOT_SPEED10, "speed10" }, - { INPUT_SLOT_SPEED15, "speed15" }, - { INPUT_SLOT_SPEED20, "speed20" }, - { INPUT_SLOT_SPEED30, "speed30" }, - { INPUT_SLOT_SPEED40, "speed40" }, - { INPUT_SLOT_SPEED60, "speed60" }, + { INPUT_SLOT_SPEED_DEC, "speed_dec" }, + { INPUT_SLOT_SPEED_RESET,"speed_reset" }, + { INPUT_SLOT_SPEED_INC, "speed_inc" }, + { INPUT_SLOT_QUICKSAVE, "quicksave" }, + { INPUT_SLOT_QUICKLOAD, "quickload" }, { INPUT_SLOT_PAUSE, "pause" }, { INPUT_SLOT_CMDLINE, "cmdline" }, }; @@ -277,13 +272,11 @@ void CInput::SetDefaultInputBindings() m_inputBindings[INPUT_SLOT_HELP ].primary = KEY(F1); m_inputBindings[INPUT_SLOT_PROG ].primary = KEY(F2); m_inputBindings[INPUT_SLOT_VISIT ].primary = KEY(KP_PERIOD); - m_inputBindings[INPUT_SLOT_SPEED05].primary = KEY(F3); - m_inputBindings[INPUT_SLOT_SPEED10].primary = KEY(F4); - m_inputBindings[INPUT_SLOT_SPEED15].primary = KEY(F5); - m_inputBindings[INPUT_SLOT_SPEED20].primary = KEY(F6); - m_inputBindings[INPUT_SLOT_SPEED30].primary = KEY(F7); - m_inputBindings[INPUT_SLOT_SPEED40].primary = KEY(F8); - m_inputBindings[INPUT_SLOT_SPEED60].primary = KEY(F9); + m_inputBindings[INPUT_SLOT_QUICKSAVE].primary = KEY(F5); + m_inputBindings[INPUT_SLOT_SPEED_DEC].primary = KEY(F6); + m_inputBindings[INPUT_SLOT_SPEED_RESET].primary = KEY(F7); + m_inputBindings[INPUT_SLOT_SPEED_INC].primary = KEY(F8); + m_inputBindings[INPUT_SLOT_QUICKLOAD].primary = KEY(F9); m_inputBindings[INPUT_SLOT_PAUSE].primary = KEY(PAUSE); m_inputBindings[INPUT_SLOT_PAUSE].secondary = KEY(p); m_inputBindings[INPUT_SLOT_CMDLINE].primary = KEY(BACKQUOTE); diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index a3848280..c5f7e50a 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -37,8 +37,6 @@ #include #include -template<> CPathManager* CSingleton::m_instance = nullptr; - CPathManager::CPathManager(CSystemUtils* systemUtils) : m_systemUtils(systemUtils) { diff --git a/src/common/config_file.cpp b/src/common/config_file.cpp index 6148eaec..94496ca1 100644 --- a/src/common/config_file.cpp +++ b/src/common/config_file.cpp @@ -34,9 +34,6 @@ #include #include - -template<> CConfigFile* CSingleton::m_instance = nullptr; - namespace bp = boost::property_tree; CConfigFile::CConfigFile() diff --git a/src/common/error.h b/src/common/error.h index 7522c01a..0ef8121e 100644 --- a/src/common/error.h +++ b/src/common/error.h @@ -112,6 +112,7 @@ enum Error ERR_PROHIBITEDTOKEN = 801, //!< instruction prohibited ERR_AIM_IMPOSSIBLE = 900, //!< cannot aim at specified angle(s) ERR_WRONG_BOT = 910, //!< inappropriate bot + ERR_NO_QUICK_SLOT = 920, //!< quicksave slot not found INFO_FIRST = 10000, //! < first information INFO_BUILD = 10001, //! < construction builded diff --git a/src/common/key.h b/src/common/key.h index d249860c..4341519e 100644 --- a/src/common/key.h +++ b/src/common/key.h @@ -100,13 +100,11 @@ enum InputSlot INPUT_SLOT_HELP, INPUT_SLOT_PROG, INPUT_SLOT_VISIT, - INPUT_SLOT_SPEED05, - INPUT_SLOT_SPEED10, - INPUT_SLOT_SPEED15, - INPUT_SLOT_SPEED20, - INPUT_SLOT_SPEED30, - INPUT_SLOT_SPEED40, - INPUT_SLOT_SPEED60, + INPUT_SLOT_SPEED_DEC, + INPUT_SLOT_SPEED_RESET, + INPUT_SLOT_SPEED_INC, + INPUT_SLOT_QUICKSAVE, + INPUT_SLOT_QUICKLOAD, INPUT_SLOT_PAUSE, INPUT_SLOT_CMDLINE, diff --git a/src/common/logger.cpp b/src/common/logger.cpp index b5e5b9cb..ed9bdb5d 100644 --- a/src/common/logger.cpp +++ b/src/common/logger.cpp @@ -22,10 +22,6 @@ #include - -template<> CLogger* CSingleton::m_instance = nullptr; - - CLogger::CLogger() { #if DEV_BUILD diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 87812d66..ac526af8 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -240,13 +240,11 @@ void InitializeRestext() stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_HELP] = TR("Instructions\\Shows the instructions for the current mission"); stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_PROG] = TR("Programming help\\Gives more detailed help with programming"); stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_VISIT] = TR("Origin of last message\\Shows where the last message was sent from"); - stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED05] = TR("Speed 0.5x\\Half speed"); - stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED10] = TR("Speed 1.0x\\Normal speed"); - stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED15] = TR("Speed 1.5x\\1.5 times faster"); - stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED20] = TR("Speed 2.0x\\Double speed"); - stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED30] = TR("Speed 3.0x\\Triple speed"); - stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED40] = TR("Speed 4.0x\\Quadruple speed"); - stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED60] = TR("Speed 6.0x\\Sextuple speed"); + stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED_DEC] = TR("Lower speed\\Decrease speed by half"); + stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED_RESET] = TR("Standard speed\\Reset speed to normal"); + stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_SPEED_INC] = TR("Higher speed\\Doubles speed"); + stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_QUICKSAVE] = TR("Quick save\\Immediately save game"); + stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_QUICKLOAD] = TR("Quick load\\Immediately load game"); stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_PAUSE] = TR("Pause\\Pause the game without opening menu"); stringsEvent[EVENT_INTERFACE_KEY+INPUT_SLOT_CMDLINE] = TR("Cheat console\\Show cheat console"); @@ -643,6 +641,7 @@ void InitializeRestext() stringsErr[ERR_DELETEBUILDING] = TR("Building destroyed"); stringsErr[ERR_ENEMY_OBJECT] = TR("Unable to control enemy objects"); stringsErr[ERR_WRONG_BOT] = TR("Inappropriate bot"); + stringsErr[ERR_NO_QUICK_SLOT] = TR("Quicksave slot not found"); stringsErr[INFO_BUILD] = TR("Building completed"); stringsErr[INFO_CONVERT] = TR("Titanium available"); @@ -732,6 +731,12 @@ void InitializeRestext() stringsCbot[CBot::CBotErrNoClassName] = TR("Class name expected"); stringsCbot[CBot::CBotErrNoReturn] = TR("Non-void function needs \"return;\""); stringsCbot[CBot::CBotErrDefaultValue] = TR("This parameter needs a default value"); + stringsCbot[CBot::CBotErrEndQuote] = TR("Missing end quote"); + stringsCbot[CBot::CBotErrBadEscape] = TR("Unknown escape sequence"); + stringsCbot[CBot::CBotErrOctalRange] = TR("Octal value out of range"); + stringsCbot[CBot::CBotErrHexDigits] = TR("Missing hex digits after escape sequence"); + stringsCbot[CBot::CBotErrHexRange] = TR("Hex value out of range"); + stringsCbot[CBot::CBotErrUnicodeName] = TR("Invalid universal character name"); stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 4519b079..cf7dcc75 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -32,8 +32,6 @@ #include "sound/sound.h" -template<> CSettings* CSingleton::m_instance = nullptr; - CSettings::CSettings() { m_tooltips = true; @@ -123,6 +121,8 @@ void CSettings::SaveSettings() GetConfigFile().SetFloatProperty("Edit", "WindowPosY", m_windowPos.y); GetConfigFile().SetFloatProperty("Edit", "WindowDimX", m_windowDim.x); GetConfigFile().SetFloatProperty("Edit", "WindowDimY", m_windowDim.y); + GetConfigFile().SetBoolProperty ("Edit", "WindowMaximized", m_windowMax); + GetConfigFile().SetBoolProperty("Edit", "IOPublic", m_IOPublic); GetConfigFile().SetFloatProperty("Edit", "IOPosX", m_IOPos.x); GetConfigFile().SetFloatProperty("Edit", "IOPosY", m_IOPos.y); @@ -283,6 +283,7 @@ void CSettings::LoadSettings() GetConfigFile().GetFloatProperty("Edit", "WindowPosY", m_windowPos.y); GetConfigFile().GetFloatProperty("Edit", "WindowDimX", m_windowDim.x); GetConfigFile().GetFloatProperty("Edit", "WindowDimY", m_windowDim.y); + GetConfigFile().GetBoolProperty ("Edit", "WindowMaximized", m_windowMax); GetConfigFile().GetBoolProperty ("Edit", "IOPublic", m_IOPublic); GetConfigFile().GetFloatProperty("Edit", "IOPosX", m_IOPos.x); @@ -395,6 +396,18 @@ Math::Point CSettings::GetWindowDim() return m_windowDim; } +void CSettings::SetWindowMax(bool max) +{ + m_windowMax = max; + GetConfigFile().SetBoolProperty("Edit", "WindowMaximized", m_windowMax); + GetConfigFile().Save(); +} + +bool CSettings::GetWindowMax() +{ + return m_windowMax; +} + void CSettings::SetIOPublic(bool mode) { m_IOPublic = mode; diff --git a/src/common/settings.h b/src/common/settings.h index 4e9b7620..92916840 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -70,6 +70,9 @@ public: void SetWindowDim(Math::Point dim); Math::Point GetWindowDim(); + + void SetWindowMax(bool max); + bool GetWindowMax(); //@} //! Managing windows open/save @@ -98,6 +101,7 @@ protected: float m_fontSize; Math::Point m_windowPos; Math::Point m_windowDim; + bool m_windowMax; bool m_IOPublic; Math::Point m_IOPos; diff --git a/src/common/singleton.h b/src/common/singleton.h index 005f5243..254fc23d 100644 --- a/src/common/singleton.h +++ b/src/common/singleton.h @@ -73,3 +73,4 @@ private: CSingleton(const CSingleton &) = delete; }; +template T* CSingleton::m_instance = nullptr; diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 943491b6..74c6179d 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -64,8 +64,6 @@ #include #include -template<> Gfx::CEngine* CSingleton::m_instance = nullptr; - // Graphics module namespace namespace Gfx { @@ -2633,6 +2631,10 @@ void CEngine::SetFocus(float focus) float farPlane = m_deepView[0] * m_clippingDistance; float aspect = static_cast(m_size.x) / static_cast(m_size.y); + + // Compute H-FoV from V-FoV and aspect ratio. + m_hfov = 2.0f * atan(aspect * tan(focus / 2.0f)); + Math::LoadProjectionMatrix(m_matProj, m_focus, aspect, 0.5f, farPlane); } @@ -2641,6 +2643,16 @@ float CEngine::GetFocus() return m_focus; } +float CEngine::GetVFovAngle() +{ + return m_focus; +} + +float CEngine::GetHFovAngle() +{ + return m_hfov; +} + void CEngine::SetShadowColor(float value) { m_shadowColor = value; @@ -3740,6 +3752,7 @@ void CEngine::RenderShadowMap() m_shadowMapping = false; m_offscreenShadowRendering = false; m_qualityShadows = false; + CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP); return; } @@ -4728,8 +4741,9 @@ void CEngine::DrawBackgroundImage() if (a > Math::PI/4.0f) a = Math::PI/4.0f; if (a < -Math::PI/4.0f) a = -Math::PI/4.0f; - u1 = -m_eyeDirH/Math::PI; - u2 = u1+1.0f/Math::PI; + // Note the background covers Math::PI radians, i.e. it repeats twice per rotation! + u1 = (-m_eyeDirH - GetHFovAngle()/2.0f) / Math::PI; + u2 = u1 + (GetHFovAngle() / Math::PI); v1 = (1.0f-h)*(0.5f+a/(2.0f*Math::PI/4.0f))+0.1f; v2 = v1+h; diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index 834de1e3..655bc024 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -869,6 +869,9 @@ public: //! Specifies the location and direction of view void SetViewParams(const Math::Vector &eyePt, const Math::Vector &lookatPt, const Math::Vector &upVec); + //! Updates the textures used for drawing ground spot + void UpdateGroundSpotTextures(); + //! Loads texture, creating it if not already present Texture LoadTexture(const std::string& name); //! Loads texture from existing image @@ -918,12 +921,16 @@ public: void SetTerrainVision(float vision); //@{ - //! Management of camera angle - /** + //! Management of camera vertical field-of-view angle. + /** This is specified in radians. + Horizontal FoV is calculated based on vertical FoV and aspect ratio. 0.75 = normal 1.50 = wide-angle */ void SetFocus(float focus); + //! Deprecated alias for GetVFovAngle float GetFocus(); + float GetVFovAngle(); + float GetHFovAngle(); //@} //@{ @@ -1197,9 +1204,6 @@ protected: //! Draws the user interface over the scene void DrawInterface(); - //! Updates the textures used for drawing ground spot - void UpdateGroundSpotTextures(); - //! Draws old-style shadow spots void DrawShadowSpots(); //! Draws the gradient background @@ -1318,8 +1322,10 @@ protected: Math::Matrix m_matProj; //! View matrix for 3D scene Math::Matrix m_matView; - //! Camera angle for 3D scene + //! Camera vertical field-of-view angle for 3D scene. A.k.a. m_vfov float m_focus; + //! Horizontal field-of-view angle, calculated from vertical FOV and aspect ratio + float m_hfov; //! Projection matrix for rendering shadow maps Math::Matrix m_shadowProjMat; diff --git a/src/graphics/engine/planet.cpp b/src/graphics/engine/planet.cpp index 1aad335e..44a3ef8b 100644 --- a/src/graphics/engine/planet.cpp +++ b/src/graphics/engine/planet.cpp @@ -115,8 +115,16 @@ void CPlanet::Draw() Math::Point p1, p2; - float a = eyeDirH + planet.angle.x; - p1.x = Math::Mod(a, Math::PI*2.0f)-0.5f; + // Determine the 2D coordinates of the centre of the planet. + + // Not sure why this is + when you'd expect -. Perhaps one of the angles is inverted. + // Compute the camera-relative angles. (0, 0) is straight ahead (the dead centre of the screen). + + // Why -1.0f? Simply because the old formula included that, and we need it to + // be consistent for the outer space cutscenes to work. + float a = planet.angle.x + eyeDirH - 1.0f; + a = Math::Mod(a+Math::PI, Math::PI*2.0f)-Math::PI; // normalize to -pi <= a < pi + p1.x = a/m_engine->GetHFovAngle() + 0.5f; a = eyeDirV + planet.angle.y; p1.y = 0.4f+(Math::Mod(a+Math::PI, Math::PI*2.0f)-Math::PI)*(2.0f/Math::PI); diff --git a/src/level/player_profile.cpp b/src/level/player_profile.cpp index bedd76cd..21bb3b25 100644 --- a/src/level/player_profile.cpp +++ b/src/level/player_profile.cpp @@ -518,6 +518,8 @@ void CPlayerProfile::LoadScene(std::string dir) CLevelParserLine* line = levelParser.Get("Mission"); cat = GetLevelCategoryFromDir(line->GetParam("base")->AsString()); + if (dir == "../../crashsave") LoadFinishedLevels(cat); + rank = line->GetParam("rank")->AsInt(); if (cat == LevelCategory::CustomLevels) { diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 6159652e..3df62fe5 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -125,9 +125,6 @@ const Gfx::Color COLOR_REF_ALIEN = Gfx::Color(135.0f/256.0f, 170.0f/256.0f, 13. const Gfx::Color COLOR_REF_GREEN = Gfx::Color(135.0f/256.0f, 170.0f/256.0f, 13.0f/256.0f); // green const Gfx::Color COLOR_REF_WATER = Gfx::Color( 25.0f/256.0f, 255.0f/256.0f, 240.0f/256.0f); // cyan - -template<> CRobotMain* CSingleton::m_instance = nullptr; - //! Constructor of robot application CRobotMain::CRobotMain() { @@ -971,33 +968,25 @@ bool CRobotMain::ProcessEvent(Event &event) { StartDisplayVisit(EVENT_NULL); } - if (data->slot == INPUT_SLOT_SPEED05) + if (data->slot == INPUT_SLOT_SPEED_DEC) { - SetSpeed(0.5f); + SetSpeed(GetSpeed()*0.5f); } - if (data->slot == INPUT_SLOT_SPEED10) + if (data->slot == INPUT_SLOT_SPEED_RESET) { SetSpeed(1.0f); } - if (data->slot == INPUT_SLOT_SPEED15) + if (data->slot == INPUT_SLOT_SPEED_INC) { - SetSpeed(1.5f); + SetSpeed(GetSpeed()*2.0f); } - if (data->slot == INPUT_SLOT_SPEED20) + if (data->slot == INPUT_SLOT_QUICKSAVE) { - SetSpeed(2.0f); + QuickSave(); } - if (data->slot == INPUT_SLOT_SPEED30) + if (data->slot == INPUT_SLOT_QUICKLOAD) { - SetSpeed(3.0f); - } - if (data->slot == INPUT_SLOT_SPEED40) - { - SetSpeed(4.0f); - } - if (data->slot == INPUT_SLOT_SPEED60) - { - SetSpeed(6.0f); + QuickLoad(); } if (data->key == KEY(c) && ((event.kmodState & KEY_MOD(CTRL)) != 0) && m_engine->GetShowStats()) { @@ -2307,37 +2296,7 @@ void CRobotMain::InitEye() //! Advances the entire scene bool CRobotMain::EventFrame(const Event &event) { - // TODO: For some reason we're getting one big event with event.rTime > 0.1f after loading before the movie starts? - if (!m_immediatSatCom && !m_beginSatCom && !m_movieLock && - m_gameTime > 0.1f && m_phase == PHASE_SIMUL) - { - m_displayText->DisplayError(INFO_BEGINSATCOM, Math::Vector(0.0f,0.0f,0.0f)); - m_beginSatCom = true; // message appears - } - m_time += event.rTime; - if (!m_movieLock && !m_pause->IsPauseType(PAUSE_ENGINE)) - { - m_gameTime += event.rTime; - m_gameTimeAbsolute += m_app->GetRealRelTime() / 1e9f; - } - - if (!m_movieLock && !m_pause->IsPauseType(PAUSE_ENGINE) && m_missionTimerStarted) - m_missionTimer += event.rTime; - - if (!m_pause->IsPauseType(PAUSE_ENGINE) && m_autosave && m_gameTimeAbsolute >= m_autosaveLast+(m_autosaveInterval*60) && m_phase == PHASE_SIMUL) - { - if (m_levelCategory == LevelCategory::Missions || - m_levelCategory == LevelCategory::FreeGame || - m_levelCategory == LevelCategory::CustomLevels ) - { - if (!IOIsBusy() && m_missionType != MISSION_CODE_BATTLE) - { - m_autosaveLast = m_gameTimeAbsolute; - Autosave(); - } - } - } m_water->EventProcess(event); m_cloud->EventProcess(event); @@ -2422,6 +2381,40 @@ bool CRobotMain::EventFrame(const Event &event) if (toto != nullptr) dynamic_cast(toto)->EventProcess(event); + // NOTE: m_movieLock is set only after the first update of CAutoBase finishes + + if (m_phase == PHASE_SIMUL) + { + if (!m_immediatSatCom && !m_beginSatCom && !m_movieLock) + { + m_displayText->DisplayError(INFO_BEGINSATCOM, Math::Vector(0.0f, 0.0f, 0.0f)); + m_beginSatCom = true; // message appears + } + + if (!m_pause->IsPauseType(PAUSE_ENGINE) && !m_movieLock) + { + m_gameTime += event.rTime; + m_gameTimeAbsolute += m_app->GetRealRelTime() / 1e9f; + + if (m_missionTimerStarted) + m_missionTimer += event.rTime; + + if (m_autosave && m_gameTimeAbsolute >= m_autosaveLast + (m_autosaveInterval * 60)) + { + if (m_levelCategory == LevelCategory::Missions || + m_levelCategory == LevelCategory::FreeGame || + m_levelCategory == LevelCategory::CustomLevels) + { + if (!IOIsBusy() && m_missionType != MISSION_CODE_BATTLE) + { + m_autosaveLast = m_gameTimeAbsolute; + Autosave(); + } + } + } + } + } + HiliteFrame(event.rTime); // Moves the film indicator. @@ -3648,12 +3641,13 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) throw CLevelParserException("Unknown command: '" + line->GetCommand() + "' in " + line->GetLevelFilename() + ":" + boost::lexical_cast(line->GetLineNumber())); } + // Do this here to prevent the first frame from taking a long time to render + m_engine->UpdateGroundSpotTextures(); + m_ui->GetLoadingScreen()->SetProgress(1.0f, RT_LOADING_FINISHED); if (m_ui->GetLoadingScreen()->IsVisible()) { - // Force render of the "Loading finished" screen - // TODO: For some reason, rendering of the first frame after the simulation starts is very slow - // We're doing this because it looks weird when the progress bar is finished but it still says "Loading programs" + // Force render of the "Loading finished" screen because it looks weird when the progress bar disappears in the middle m_app->Render(); } @@ -3695,7 +3689,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) // TODO: m_engine->TimeInit(); ?? m_input->ResetKeyStates(); m_time = 0.0f; - m_gameTime = 0.0f; + if (m_sceneReadPath.empty()) m_gameTime = 0.0f; m_gameTimeAbsolute = 0.0f; m_autosaveLast = 0.0f; m_infoUsed = 0; @@ -4548,6 +4542,7 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s else line->AddParam("chap", MakeUnique(m_levelChap)); line->AddParam("rank", MakeUnique(m_levelRank)); + line->AddParam("gametime", MakeUnique(GetGameTime())); levelParser.AddLine(std::move(line)); line = MakeUnique("Map"); @@ -4728,6 +4723,9 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) int objCounter = 0; for (auto& line : levelParser.GetLines()) { + if (line->GetCommand() == "Mission") + m_gameTime = line->GetParam("gametime")->AsFloat(0.0f); + if (line->GetCommand() == "Map") m_map->ZoomMap(line->GetParam("zoom")->AsFloat()); @@ -5563,6 +5561,31 @@ void CRobotMain::Autosave() m_playerProfile->SaveScene(dir, info); } +void CRobotMain::QuickSave() +{ + GetLogger()->Info("Quicksave!\n"); + + char infostr[100]; + time_t now = time(nullptr); + strftime(infostr, 99, "%y.%m.%d %H:%M", localtime(&now)); + std::string info = std::string("[QUICKSAVE]") + infostr; + std::string dir = m_playerProfile->GetSaveFile(std::string("quicksave")); + + m_playerProfile->SaveScene(dir, info); +} + +void CRobotMain::QuickLoad() +{ + std::string dir = m_playerProfile->GetSaveFile(std::string("quicksave")); + if(!CResourceManager::Exists(dir)) + { + m_displayText->DisplayError(ERR_NO_QUICK_SLOT, Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 1000.0f); + GetLogger()->Debug("Quicksave slot not found\n"); + return; + } + m_playerProfile->LoadScene(dir); +} + void CRobotMain::SetExitAfterMission(bool exit) { m_exitAfterMission = exit; @@ -5824,6 +5847,7 @@ void CRobotMain::CreateCodeBattleInterface() pw->CreateButton(pos, ddim, 13, EVENT_CODE_BATTLE_SPECTATOR); } + if (!m_scoreboard) return; pos.y += ddim.y; ddim.y = textHeight; int i = 0; diff --git a/src/level/robotmain.h b/src/level/robotmain.h index fee753f0..9b1511c0 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -510,6 +510,8 @@ protected: void AutosaveRotate(); void Autosave(); + void QuickSave(); + void QuickLoad(); bool DestroySelectedObject(); void PushToSelectionHistory(CObject* obj); CObject* PopFromSelectionHistory(); diff --git a/src/object/auto/autofactory.cpp b/src/object/auto/autofactory.cpp index 7dfc280d..45fd357c 100644 --- a/src/object/auto/autofactory.cpp +++ b/src/object/auto/autofactory.cpp @@ -405,12 +405,12 @@ bool CAutoFactory::EventProcess(const Event &event) { Program* program = dynamic_cast(vehicle)->AddProgram(); - if (boost::regex_search(m_program, boost::regex("^[A-Za-z0-9_]+$"))) // Public function name? + if (boost::regex_match(m_program, boost::regex("[A-Za-z0-9_]+"))) // Public function name? { std::string code = "extern void object::Start_"+m_program+"()\n{\n\t\n\t//Automatically generated by object.factory()\n\t"+m_program+"();\n\t\n}\n"; program->script->SendScript(code.c_str()); } - else if (boost::regex_search(m_program, boost::regex("\\.txt$"))) // File name (with .txt extension)? + else if (boost::regex_match(m_program, boost::regex(".*\\.txt"))) // File name (with .txt extension)? { program->script->ReadScript(m_program.c_str()); } diff --git a/src/object/interface/damageable_object.h b/src/object/interface/damageable_object.h index 0813a286..a8f3afb5 100644 --- a/src/object/interface/damageable_object.h +++ b/src/object/interface/damageable_object.h @@ -59,4 +59,11 @@ public: //! Damage the object, with the given force. Returns true if the object has been fully destroyed (assuming the object is destroyable, of course). If force == infinity, destroy immediately (this is the default value) /** NOTE: You should never assume that after this function exits, the object is destroyed, unless it returns true. Even if you specify force = infinity, if may still sometimes decide not to destroy the object. */ virtual bool DamageObject(DamageType type, float force = std::numeric_limits::infinity(), CObject* killer = nullptr) = 0; + + + //! Set the status that means the object is currently taking damage + virtual void SetDamaging(bool damaging) = 0; + //! Is object currently taking damage? + virtual bool IsDamaging() = 0; + }; diff --git a/src/object/motion/motionvehicle.cpp b/src/object/motion/motionvehicle.cpp index b5851716..14d46dcd 100644 --- a/src/object/motion/motionvehicle.cpp +++ b/src/object/motion/motionvehicle.cpp @@ -1783,6 +1783,7 @@ bool CMotionVehicle::EventFrameCanoni(const Event &event) float energy = GetObjectEnergyLevel(m_object); if (energy == 0.0f) return true; + if (energy > 1.0f) energy = 1.0f; //fix issue with cheated cells, see issue #1009 factor = 0.5f+energy*0.5f; if ( bOnBoard ) factor *= 0.8f; diff --git a/src/object/object_manager.cpp b/src/object/object_manager.cpp index a33a89fc..e8323a5e 100644 --- a/src/object/object_manager.cpp +++ b/src/object/object_manager.cpp @@ -36,10 +36,6 @@ #include - -template<> CObjectManager* CSingleton::m_instance = nullptr; - - CObjectManager::CObjectManager(Gfx::CEngine* engine, Gfx::CTerrain* terrain, Gfx::COldModelManager* oldModelManager, diff --git a/src/object/old_object.cpp b/src/object/old_object.cpp index dbe92b32..beaa7c3d 100644 --- a/src/object/old_object.cpp +++ b/src/object/old_object.cpp @@ -137,6 +137,7 @@ COldObject::COldObject(int id) m_bVirusMode = false; m_virusTime = 0.0f; m_lastVirusParticle = 0.0f; + m_damaging = false; m_dying = DeathType::Alive; m_bFlat = false; m_gunGoalV = 0.0f; @@ -387,6 +388,14 @@ bool COldObject::DamageObject(DamageType type, float force, CObject* killer) float shield = GetShield(); shield -= loss; SetShield(shield); + + // Sending info about taking damage + if (!m_damaging) + { + SetDamaging(true); + m_main->UpdateShortcuts(); + } + m_damageTime = m_time; } else { @@ -394,6 +403,7 @@ bool COldObject::DamageObject(DamageType type, float force, CObject* killer) { // Dead immediately SetShield(0.0f); + SetDamaging(false); } } dead = (GetShield() <= 0.0f); @@ -424,7 +434,6 @@ bool COldObject::DamageObject(DamageType type, float force, CObject* killer) { m_engine->GetPyroManager()->Create(Gfx::PT_SHOTT, this, loss); } - return false; } @@ -440,6 +449,7 @@ void COldObject::DestroyObject(DestructionType type, CObject* killer) if (Implements(ObjectInterfaceType::Shielded)) { SetShield(0.0f); + SetDamaging(false); } Gfx::PyroType pyroType = Gfx::PT_NULL; @@ -1010,6 +1020,8 @@ void COldObject::Write(CLevelParserLine* line) if ( m_virusTime != 0.0f ) line->AddParam("virusTime", MakeUnique(m_virusTime)); + line->AddParam("lifetime", MakeUnique(m_aTime)); + // Sets the parameters of the command line. CLevelParserParamVec cmdline; for(float value : GetCmdLine()) @@ -1144,6 +1156,8 @@ void COldObject::Read(CLevelParserLine* line) m_bVirusMode = line->GetParam("virusMode")->AsBool(false); m_virusTime = line->GetParam("virusTime")->AsFloat(0.0f); + m_aTime = line->GetParam("lifetime")->AsFloat(0.0f); + if ( m_motion != nullptr ) { m_motion->Read(line); @@ -2148,6 +2162,12 @@ bool COldObject::EventFrame(const Event &event) SetShield(GetShield() + event.rTime*(1.0f/GetShieldFullRegenTime())); } + if (m_damaging && m_time - m_damageTime > 2.0f) + { + SetDamaging(false); + m_main->UpdateShortcuts(); + } + return true; } @@ -2652,6 +2672,15 @@ float COldObject::GetMagnifyDamage() return m_magnifyDamage; } +void COldObject::SetDamaging(bool damaging) +{ + m_damaging = damaging; +} + +bool COldObject::IsDamaging() +{ + return m_damaging; +} void COldObject::SetDying(DeathType deathType) { diff --git a/src/object/old_object.h b/src/object/old_object.h index 697e05db..48b71ee7 100644 --- a/src/object/old_object.h +++ b/src/object/old_object.h @@ -237,6 +237,9 @@ public: void SetMagnifyDamage(float factor) override; float GetMagnifyDamage() override; + void SetDamaging(bool damaging); + bool IsDamaging() override; + void SetDying(DeathType deathType) override; DeathType GetDying() override; bool IsDying() override; @@ -356,6 +359,8 @@ protected: bool m_bSelectable; // selectable object bool m_bCheckToken; // object with audited tokens bool m_underground; // object active but undetectable + bool m_damaging; + float m_damageTime; DeathType m_dying; bool m_bFlat; bool m_bTrainer; // drive vehicle (without remote) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index 5d6cd475..6584465e 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -803,7 +803,8 @@ Error CTaskGoto::Start(Math::Vector goal, float altitude, Error CTaskGoto::IsEnded() { Math::Vector pos; - float limit, angle = 0.0f, dist, h, level; + float limit, angle = 0.0f, h, level; + volatile float dist; //fix for issue #844 if ( m_engine->GetPause() ) return ERR_CONTINUE; if ( m_error != ERR_OK ) return m_error; @@ -916,7 +917,9 @@ Error CTaskGoto::IsEnded() if ( m_goalMode == TGG_EXPRESS ) { dist = Math::DistanceProjected(m_goal, pos); - if ( dist < 10.0f && dist > m_lastDistance ) + float margin = 10.0f; + if ( m_object->Implements(ObjectInterfaceType::Flying) ) margin = 20.0f; + if ( dist < margin && dist > m_lastDistance ) { return ERR_STOP; } diff --git a/src/object/task/taskrecover.cpp b/src/object/task/taskrecover.cpp index 834811a7..ab5bc52d 100644 --- a/src/object/task/taskrecover.cpp +++ b/src/object/task/taskrecover.cpp @@ -376,5 +376,5 @@ bool CTaskRecover::Abort() CObject* CTaskRecover::SearchRuin() { - return CObjectManager::GetInstancePointer()->FindNearest(nullptr, m_recoverPos, {OBJECT_RUINmobilew1, OBJECT_RUINmobilew2, OBJECT_RUINmobilet1, OBJECT_RUINmobilet2, OBJECT_RUINmobiler1, OBJECT_RUINmobiler2}, 40.0f/g_unit); + return CObjectManager::GetInstancePointer()->FindNearest(nullptr, m_recoverPos, {OBJECT_RUINmobilew1, OBJECT_RUINmobilew2, OBJECT_RUINmobilet1, OBJECT_RUINmobilet2, OBJECT_RUINmobiler1, OBJECT_RUINmobiler2, OBJECT_RUINdoor, OBJECT_RUINsupport, OBJECT_RUINradar}, 40.0f/g_unit); } diff --git a/src/object/task/taskterraform.cpp b/src/object/task/taskterraform.cpp index f71b9854..f46c68df 100644 --- a/src/object/task/taskterraform.cpp +++ b/src/object/task/taskterraform.cpp @@ -363,12 +363,109 @@ bool CTaskTerraform::Terraform() type = pObj->GetType(); if ( type == OBJECT_NULL ) continue; - if ( type == OBJECT_TEEN34 ) // stone? + if ( type == OBJECT_TEEN34 || + type == OBJECT_POWER || + type == OBJECT_ATOMIC || + type == OBJECT_STONE || + type == OBJECT_URANIUM || + type == OBJECT_METAL || + type == OBJECT_BULLET || + type == OBJECT_BBOX || + type == OBJECT_KEYa || + type == OBJECT_KEYb || + type == OBJECT_KEYc || + type == OBJECT_KEYd || + type == OBJECT_TNT || + type == OBJECT_NEST || + type == OBJECT_BOMB || + type == OBJECT_PLANT0 || + type == OBJECT_PLANT1 || + type == OBJECT_PLANT2 || + type == OBJECT_PLANT3 || + type == OBJECT_PLANT4 || + type == OBJECT_PLANT5 || + type == OBJECT_PLANT6 || + type == OBJECT_PLANT7 || + type == OBJECT_PLANT15 || + type == OBJECT_PLANT16 || + type == OBJECT_PLANT17 || + type == OBJECT_PLANT18 || + type == OBJECT_PLANT19 || + type == OBJECT_MUSHROOM1 || + type == OBJECT_MUSHROOM2 || + type == OBJECT_FACTORY || + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_PARA || + type == OBJECT_START || + type == OBJECT_END || + type == OBJECT_EGG || + type == OBJECT_RUINmobilew1 || + type == OBJECT_RUINmobilew2 || + type == OBJECT_RUINmobilet1 || + type == OBJECT_RUINmobilet2 || + type == OBJECT_RUINdoor || + type == OBJECT_RUINsupport || + type == OBJECT_RUINradar || + type == OBJECT_BARRIER0 || + type == OBJECT_APOLLO4 ) // almost everything? { dist = Math::Distance(m_terraPos, pObj->GetPosition()); - if ( dist > 20.0f ) continue; - m_engine->GetPyroManager()->Create(Gfx::PT_FRAGT, pObj); + if (type == OBJECT_BULLET || + type == OBJECT_NEST || + type == OBJECT_EGG) // Alien Organic? + { + if ( dist > 5.0f ) continue; + m_engine->GetPyroManager()->Create(Gfx::PT_FRAGO, pObj); + } + else if (type == OBJECT_TNT || + type == OBJECT_BOMB) // Explosives? + { + if ( dist > 5.0f ) continue; + m_engine->GetPyroManager()->Create(Gfx::PT_EXPLOT, pObj); + dynamic_cast(m_object)->DamageObject(DamageType::Explosive, 0.9f); + } + else if (type == OBJECT_PLANT0 || + type == OBJECT_PLANT1 || + type == OBJECT_PLANT2 || + type == OBJECT_PLANT3 || + type == OBJECT_PLANT4 || + type == OBJECT_PLANT5 || + type == OBJECT_PLANT6 || + type == OBJECT_PLANT7 || + type == OBJECT_PLANT15 || + type == OBJECT_PLANT16 || + type == OBJECT_PLANT17 || + type == OBJECT_PLANT18 || + type == OBJECT_PLANT19 || + type == OBJECT_MUSHROOM1 || + type == OBJECT_MUSHROOM2) // Plants? + { + if ( dist > 7.5f ) continue; + m_engine->GetPyroManager()->Create(Gfx::PT_EGG, pObj); + } + else if (type == OBJECT_FACTORY || + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_PARA) // Buildings? + { + if ( dist > 15.0f ) continue; + dynamic_cast(pObj)->DamageObject(DamageType::Explosive, 0.2f); + } + else // Other? + { + if ( dist > 5.0f ) continue; + m_engine->GetPyroManager()->Create(Gfx::PT_FRAGT, pObj); + } } else { @@ -388,6 +485,14 @@ bool CTaskTerraform::Terraform() if (type == OBJECT_SPIDER) actionType = MSS_BACK1; motion->SetAction(actionType, 0.8f+Math::Rand()*0.3f); dynamic_cast(pObj)->SetFixed(true); // not moving + + if ( dist > 5.0f ) continue; + m_engine->GetPyroManager()->Create(Gfx::PT_EXPLOO, pObj); + } + else if ( type == OBJECT_BEE || type == OBJECT_WORM ) + { + if ( dist > 5.0f ) continue; + m_engine->GetPyroManager()->Create(Gfx::PT_EXPLOO, pObj); } } } diff --git a/src/object/task/taskturn.cpp b/src/object/task/taskturn.cpp index d07dfde1..69799b86 100644 --- a/src/object/task/taskturn.cpp +++ b/src/object/task/taskturn.cpp @@ -109,7 +109,7 @@ Error CTaskTurn::IsEnded() if ( m_bLeft ) { - if ( angle <= m_startAngle+m_angle ) + if ( angle <= m_finalAngle ) { m_physics->SetMotorSpeedZ(0.0f); //? m_physics->SetCirMotionY(MO_MOTSPEED, 0.0f); @@ -118,10 +118,15 @@ Error CTaskTurn::IsEnded() m_object->SetRotationY(m_finalAngle); return ERR_STOP; } + if ( !m_bDecel && angle <= m_startAngle+m_angle) + { + m_bDecel = true; + m_physics->SetMotorSpeedZ(-0.5f); + } } else { - if ( angle >= m_startAngle+m_angle ) + if ( angle >= m_finalAngle ) { m_physics->SetMotorSpeedZ(0.0f); //? m_physics->SetCirMotionY(MO_MOTSPEED, 0.0f); @@ -130,6 +135,11 @@ Error CTaskTurn::IsEnded() m_object->SetRotationY(m_finalAngle); return ERR_STOP; } + if ( !m_bDecel && angle >= m_startAngle+m_angle) + { + m_bDecel = true; + m_physics->SetMotorSpeedZ(0.5f); + } } return ERR_CONTINUE; diff --git a/src/object/task/taskturn.h b/src/object/task/taskturn.h index ddc696cb..4232950e 100644 --- a/src/object/task/taskturn.h +++ b/src/object/task/taskturn.h @@ -41,4 +41,5 @@ protected: float m_finalAngle = 0.0f; bool m_bLeft = false; bool m_bError = false; + bool m_bDecel = false; }; diff --git a/src/script/cbottoken.cpp b/src/script/cbottoken.cpp index 5d24673e..4dd5c8ec 100644 --- a/src/script/cbottoken.cpp +++ b/src/script/cbottoken.cpp @@ -271,6 +271,7 @@ std::string GetHelpFilename(const char *token) if ( strcmp(token, "factory" ) == 0 ) helpfile = "cbot/factory"; if ( strcmp(token, "destroy" ) == 0 ) helpfile = "cbot/destroy"; if ( strcmp(token, "search" ) == 0 ) helpfile = "cbot/search"; + if ( strcmp(token, "searchall" ) == 0 ) helpfile = "cbot/searchall"; if ( strcmp(token, "radar" ) == 0 ) helpfile = "cbot/radar"; if ( strcmp(token, "radarall" ) == 0 ) helpfile = "cbot/radarall"; if ( strcmp(token, "direction" ) == 0 ) helpfile = "cbot/direct"; @@ -421,6 +422,7 @@ bool IsFunction(const char *token) if ( strcmp(token, "takeoff" ) == 0 ) return true; if ( strcmp(token, "destroy" ) == 0 ) return true; if ( strcmp(token, "search" ) == 0 ) return true; + if ( strcmp(token, "searchall" ) == 0 ) return true; if ( strcmp(token, "radar" ) == 0 ) return true; if ( strcmp(token, "radarall" ) == 0 ) return true; if ( strcmp(token, "detect" ) == 0 ) return true; @@ -522,7 +524,8 @@ const char* GetHelpText(const char *token) if ( strcmp(token, "research" ) == 0 ) return "object.research ( type );"; if ( strcmp(token, "takeoff" ) == 0 ) return "object.takeoff ( );"; if ( strcmp(token, "destroy" ) == 0 ) return "object.destroy ( );"; - if ( strcmp(token, "search" ) == 0 ) return "search ( cat, pos );"; + if ( strcmp(token, "search" ) == 0 ) return "search ( cat, pos, min, max, sens, filter );"; + if ( strcmp(token, "searchall" ) == 0 ) return "searchall ( cat, pos, min, max, sens, filter );"; if ( strcmp(token, "radar" ) == 0 ) return "radar ( cat, angle, focus, min, max, sens, filter );"; if ( strcmp(token, "radarall" ) == 0 ) return "radarall ( cat, angle, focus, min, max, sens, filter );"; if ( strcmp(token, "detect" ) == 0 ) return "detect ( cat );"; diff --git a/src/script/script.cpp b/src/script/script.cpp index 8253caea..f9a70e3b 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -589,6 +589,56 @@ void CScript::UpdateList(Ui::CList* list) list->SetState(Ui::STATE_ENABLE); } +// Colorize a string literal with escape sequences also colored + +void HighlightString(Ui::CEdit* edit, const std::string& s, int start) +{ + edit->SetFormat(start, start + 1, Gfx::FONT_HIGHLIGHT_STRING); + + auto it = s.cbegin() + 1; + + ++start; + while (it != s.cend() && *it != '\"') + { + if (*(it++) != '\\') // not escape sequence + { + edit->SetFormat(start, start + 1, Gfx::FONT_HIGHLIGHT_STRING); + ++start; + continue; + } + + if (it == s.cend()) break; + + int end = start + 2; + + if (CBot::CharInList(*it, "01234567")) // octal escape sequence + { + for (int i = 0; ++it != s.cend() && i < 2; i++, end++) + { + if (!CBot::CharInList(*it, "01234567")) break; + } + } + else if (*it == 'x' || *it == 'u' || *it == 'U') // hex or unicode escape + { + bool isHexCode = (*it == 'x'); + int maxlen = (*it == 'u') ? 4 : 8; + + for (int i = 0; ++it != s.cend(); i++, end++) + { + if (!isHexCode && i >= maxlen) break; + if (!CBot::CharInList(*it, "0123456789ABCDEFabcdef")) break; + } + } + else // n, r, t, etc. + ++it; + + edit->SetFormat(start, end, Gfx::FONT_HIGHLIGHT_NONE); + start = end; + } + + if (it != s.cend()) + edit->SetFormat(start, start + 1, Gfx::FONT_HIGHLIGHT_STRING); +} // Colorize the text according to syntax. @@ -643,10 +693,16 @@ void CScript::ColorizeScript(Ui::CEdit* edit, int rangeStart, int rangeEnd) { color = Gfx::FONT_HIGHLIGHT_CONST; } - else if (type == CBot::TokenTypString || type == CBot::TokenTypNum) // string literals and numbers + else if (type == CBot::TokenTypNum) // numbers { color = Gfx::FONT_HIGHLIGHT_STRING; } + else if (type == CBot::TokenTypString) // string literals + { + HighlightString(edit, token, cursor1); + bt = bt->GetNext(); + continue; + } assert(cursor1 < cursor2); edit->SetFormat(cursor1, cursor2, color); diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index 5fc97564..8a774907 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -79,9 +79,9 @@ CBotTypResult CScriptFunctions::cClassOneFloat(CBotVar* thisclass, CBotVar* &var return cOneFloat(var, nullptr); } -// Compiling a procedure with a "dot". +// Compile a parameter of type "point". -CBotTypResult CScriptFunctions::cPoint(CBotVar* &var, void* user) +CBotTypResult cPoint(CBotVar* &var, void* user) { if ( var == nullptr ) return CBotTypResult(CBotErrLowParam); @@ -695,66 +695,113 @@ bool CScriptFunctions::rDelete(CBotVar* var, CBotVar* result, int& exception, vo CObjectManager::GetInstancePointer()->DeleteObject(obj); } } - return true; + + // Returning "false" here makes sure the program doesn't try to keep executing if the robot just destroyed itself + // using delete(this.id) + // See issue #925 + return false; } - - -// Compilation of the instruction "search(type, pos)". - -CBotTypResult CScriptFunctions::cSearch(CBotVar* &var, void* user) +CBotTypResult compileSearch(CBotVar* &var, void* user, CBotTypResult returnValue) { - CBotVar* array; - CBotTypResult ret; - if ( var == nullptr ) return CBotTypResult(CBotErrLowParam); if ( var->GetType() == CBotTypArrayPointer ) { - array = var->GetItemList(); - if ( array == nullptr ) return CBotTypResult(CBotTypPointer); - if ( array->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + CBotTypResult type = var->GetTypResult().GetTypElem(); + if ( type.GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadParam); // type } - else if ( var->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + else if ( var->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // type var = var->GetNext(); - if ( var != nullptr ) - { - ret = cPoint(var, user); - if ( ret.GetType() != 0 ) return ret; - if ( var != nullptr ) return CBotTypResult(CBotErrOverParam); - } - return CBotTypResult(CBotTypPointer, "object"); + if ( var == nullptr ) return returnValue; + + CBotTypResult ret = cPoint(var, user); // pos + if ( ret.GetType() != 0 ) return ret; + + if ( var == nullptr ) return returnValue; + if ( var->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // min + var = var->GetNext(); + if ( var == nullptr ) return returnValue; + if ( var->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // max + var = var->GetNext(); + if ( var == nullptr ) return returnValue; + if ( var->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // sense + var = var->GetNext(); + if ( var == nullptr ) return returnValue; + if ( var->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // filter + var = var->GetNext(); + if ( var == nullptr ) return returnValue; + return CBotTypResult(CBotErrOverParam); } -// Instruction "search(type, pos)". +// Compilation of "search(type, pos, min, max, sens, filter)". -bool CScriptFunctions::rSearch(CBotVar* var, CBotVar* result, int& exception, void* user) +CBotTypResult CScriptFunctions::cSearch(CBotVar* &var, void* user) { - CObject* pThis = static_cast(user)->m_object; - CObject *pBest; - CBotVar* array; - Math::Vector pos, oPos; - bool bArray; - int type; + return compileSearch(var, user, CBotTypResult(CBotTypPointer, "object")); +} + +CBotTypResult CScriptFunctions::cSearchAll(CBotVar* &var, void* user) +{ + return compileSearch(var, user, CBotTypResult(CBotTypArrayPointer, CBotTypResult(CBotTypPointer, "object"))); +} + +bool runSearch(CBotVar* var, Math::Vector pos, int& exception, std::function, Math::Vector, float, float, bool, RadarFilter)> code) +{ + CBotVar* array; + RadarFilter filter; + float minDist, maxDist, sens; + int type; + bool bArray = false; + + type = OBJECT_NULL; + array = nullptr; + minDist = 0.0f*g_unit; + maxDist = 1000.0f*g_unit; + sens = 1.0f; + filter = FILTER_NONE; - if ( var->GetType() == CBotTypArrayPointer ) - { - array = var->GetItemList(); - bArray = true; - } - else - { - type = var->GetValInt(); - bArray = false; - } - var = var->GetNext(); if ( var != nullptr ) { - if ( !GetPoint(var, exception, pos) ) return true; - } - else - { - pos = pThis->GetPosition(); + if ( var->GetType() == CBotTypArrayPointer ) + { + array = var->GetItemList(); + bArray = true; + } + else + { + type = var->GetValInt(); + bArray = false; + } + + var = var->GetNext(); + if ( var != nullptr ) + { + if ( !GetPoint(var, exception, pos) ) return false; + + if ( var != nullptr ) + { + minDist = var->GetValFloat(); + + var = var->GetNext(); + if ( var != nullptr ) + { + maxDist = var->GetValFloat(); + + var = var->GetNext(); + if ( var != nullptr ) + { + sens = var->GetValFloat(); + + var = var->GetNext(); + if ( var != nullptr ) + { + filter = static_cast(var->GetValInt()); + } + } + } + } + } } std::vector type_v; @@ -774,18 +821,47 @@ bool CScriptFunctions::rSearch(CBotVar* var, CBotVar* result, int& exception, vo } } - pBest = CObjectManager::GetInstancePointer()->Radar(pThis, pos, 0.0f, type_v, 0.0f, Math::PI*2.0f, 0.0f, 1000.0f, false, FILTER_NONE, true); + return code(type_v, pos, minDist, maxDist, sens < 0, filter); +} - if ( pBest == nullptr ) - { - result->SetPointer(nullptr); - } - else - { - result->SetPointer(pBest->GetBotVar()); - } +bool CScriptFunctions::rSearch(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CObject* pThis = static_cast(user)->m_object; - return true; + return runSearch(var, pThis->GetPosition(), exception, [&result, pThis](std::vector types, Math::Vector pos, float minDist, float maxDist, bool furthest, RadarFilter filter) + { + CObject* pBest = CObjectManager::GetInstancePointer()->Radar(pThis, pos, 0.0f, types, 0.0f, Math::PI*2.0f, minDist, maxDist, furthest, filter, true); + + if ( pBest == nullptr ) + { + result->SetPointer(nullptr); + } + else + { + result->SetPointer(pBest->GetBotVar()); + } + + return true; + }); +} + +bool CScriptFunctions::rSearchAll(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CObject* pThis = static_cast(user)->m_object; + + return runSearch(var, pThis->GetPosition(), exception, [&result, pThis](std::vector types, Math::Vector pos, float minDist, float maxDist, bool furthest, RadarFilter filter) + { + std::vector best = CObjectManager::GetInstancePointer()->RadarAll(pThis, pos, 0.0f, types, 0.0f, Math::PI*2.0f, minDist, maxDist, furthest, filter, true); + + int i = 0; + result->SetInit(CBotVar::InitType::DEF); + for (CObject* obj : best) + { + result->GetItem(i++, true)->SetPointer(obj->GetBotVar()); + } + + return true; + }); } @@ -2521,7 +2597,7 @@ CBotTypResult CScriptFunctions::cTopo(CBotVar* &var, void* user) CBotTypResult ret; if ( var == nullptr ) return CBotTypResult(CBotErrLowParam); - ret = CScriptFunctions::cPoint(var, user); + ret = cPoint(var, user); if ( ret.GetType() != 0 ) return ret; if ( var == nullptr ) return CBotTypResult(CBotTypFloat); @@ -3249,6 +3325,7 @@ void CScriptFunctions::Init() CBotProgram::AddFunction("retobjectbyid", rGetObjectById, cGetObject); CBotProgram::AddFunction("delete", rDelete, cDelete); CBotProgram::AddFunction("search", rSearch, cSearch); + CBotProgram::AddFunction("searchall", rSearchAll, cSearchAll); CBotProgram::AddFunction("radar", rRadar, cRadar); CBotProgram::AddFunction("radarall", rRadarAll, cRadarAll); CBotProgram::AddFunction("detect", rDetect, cDetect); diff --git a/src/script/scriptfunc.h b/src/script/scriptfunc.h index c3b3d2d1..5778a9f1 100644 --- a/src/script/scriptfunc.h +++ b/src/script/scriptfunc.h @@ -57,6 +57,7 @@ private: static CBot::CBotTypResult cGetObject(CBot::CBotVar* &var, void* user); static CBot::CBotTypResult cDelete(CBot::CBotVar* &var, void* user); static CBot::CBotTypResult cSearch(CBot::CBotVar* &var, void* user); + static CBot::CBotTypResult cSearchAll(CBot::CBotVar* &var, void* user); static CBot::CBotTypResult cRadar(CBot::CBotVar* &var, void* user); static CBot::CBotTypResult cRadarAll(CBot::CBotVar* &var, void* user); static CBot::CBotTypResult cDetect(CBot::CBotVar* &var, void* user); @@ -81,7 +82,6 @@ private: static CBot::CBotTypResult cPenDown(CBot::CBotVar* &var, void* user); static CBot::CBotTypResult cOnePoint(CBot::CBotVar* &var, void* user); - static CBot::CBotTypResult cPoint(CBot::CBotVar* &var, void* user); static CBot::CBotTypResult cOneObject(CBot::CBotVar* &var, void* user); static bool rEndMission(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); @@ -97,6 +97,7 @@ private: static bool rGetObject(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rDelete(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rSearch(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); + static bool rSearchAll(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rRadar(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rRadarAll(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rDetect(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); diff --git a/src/ui/controls/control.h b/src/ui/controls/control.h index 1a193757..b3fb3967 100644 --- a/src/ui/controls/control.h +++ b/src/ui/controls/control.h @@ -57,7 +57,8 @@ enum ControlState STATE_FRAME = (1<<13), // framework highlighting STATE_WARNING = (1<<14), // framework hatched yellow / black STATE_VALUE = (1<<15), // displays the value - STATE_RUN = (1<<16) // running program + STATE_RUN = (1<<16), // running program + STATE_DAMAGE = (1<<17) // taking damage }; diff --git a/src/ui/controls/edit.cpp b/src/ui/controls/edit.cpp index 7d08b260..f5e02671 100644 --- a/src/ui/controls/edit.cpp +++ b/src/ui/controls/edit.cpp @@ -47,9 +47,9 @@ namespace Ui { -const float MARGX = (5.0f/640.0f); -const float MARGY = (5.0f/480.0f); -const float MARGYS = (4.0f/480.0f); +const float MARGX = (3.75f/640.0f); +const float MARGY = (3.75f/480.0f); +const float MARGYS = (2.75f/480.0f); const float MARGY1 = (1.0f/480.0f); //! time limit for double-click const float DELAY_DBCLICK = 0.75f; diff --git a/src/ui/controls/shortcut.cpp b/src/ui/controls/shortcut.cpp index ef4f5439..036c10d6 100644 --- a/src/ui/controls/shortcut.cpp +++ b/src/ui/controls/shortcut.cpp @@ -209,6 +209,27 @@ void CShortcut::Draw() DrawIcon(m_pos, m_dim, uv1, uv2); } + if ( (m_state & STATE_DAMAGE) && Math::Mod(m_time, 0.7f) >= 0.3f ) + { + Math::Point uv1, uv2; + float dp; + + m_engine->SetTexture("textures/interface/button2.png"); + m_engine->SetState(Gfx::ENG_RSTATE_TTEXTURE_BLACK); + + uv1.x = 159.0f/256.0f; + uv1.y = 240.0f/256.0f; + uv2.x = 145.0f/256.0f; + uv2.y = 256.0f/256.0f; + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + DrawIcon(m_pos, m_dim, uv1, uv2); + } } // Draw the vertex array. diff --git a/src/ui/mainshort.cpp b/src/ui/mainshort.cpp index d9fffa26..23b58ee9 100644 --- a/src/ui/mainshort.cpp +++ b/src/ui/mainshort.cpp @@ -247,6 +247,7 @@ bool CMainShort::UpdateShortcuts() assert(m_shortcuts[i]->Implements(ObjectInterfaceType::Controllable)); pc->SetState(STATE_CHECK, dynamic_cast(m_shortcuts[i])->GetSelect()); pc->SetState(STATE_RUN, m_shortcuts[i]->Implements(ObjectInterfaceType::Programmable) && dynamic_cast(m_shortcuts[i])->IsProgram()); + pc->SetState(STATE_DAMAGE, dynamic_cast(m_shortcuts[i])->IsDamaging()); } } return true; diff --git a/src/ui/studio.cpp b/src/ui/studio.cpp index e3545cbb..b06b6f5a 100644 --- a/src/ui/studio.cpp +++ b/src/ui/studio.cpp @@ -249,16 +249,16 @@ bool CStudio::EventProcess(const Event &event) { m_editActualPos = m_editFinalPos = pw->GetPos(); m_editActualDim = m_editFinalDim = pw->GetDim(); - m_settings->SetWindowPos(m_editActualPos); - m_settings->SetWindowDim(m_editActualDim); + m_windowPos = m_editActualPos; + m_windowDim = m_editActualDim; AdjustEditScript(); } if ( event.type == pw->GetEventTypeReduce() ) { if ( m_bEditMinimized ) { - m_editFinalPos = m_settings->GetWindowPos(); - m_editFinalDim = m_settings->GetWindowDim(); + m_editFinalPos = m_windowPos; + m_editFinalDim = m_windowDim; m_bEditMinimized = false; m_bEditMaximized = false; } @@ -283,8 +283,8 @@ bool CStudio::EventProcess(const Event &event) { if ( m_bEditMaximized ) { - m_editFinalPos = m_settings->GetWindowPos(); - m_editFinalDim = m_settings->GetWindowDim(); + m_editFinalPos = m_windowPos; + m_editFinalDim = m_windowDim; m_bEditMinimized = false; m_bEditMaximized = false; } @@ -579,8 +579,27 @@ void CStudio::StartEditScript(CScript *script, std::string name, Program* progra pw = static_cast(m_interface->SearchControl(EVENT_WINDOW6)); if (pw != nullptr) pw->ClearState(STATE_VISIBLE | STATE_ENABLE); - pos = m_editFinalPos = m_editActualPos = m_settings->GetWindowPos(); - dim = m_editFinalDim = m_editActualDim = m_settings->GetWindowDim(); + m_dialogPos = m_settings->GetIOPos(); + m_dialogDim = m_settings->GetIODim(); + m_windowPos = m_settings->GetWindowPos(); + m_windowDim = m_settings->GetWindowDim(); + m_bEditMaximized = m_settings->GetWindowMax(); + + if ( m_bEditMaximized ) + { + m_editFinalPos.x = 0.00f; + m_editFinalPos.y = 0.00f; + m_editFinalDim.x = 1.00f; + m_editFinalDim.y = 1.00f; + } + else + { + m_editFinalPos = m_windowPos; + m_editFinalDim = m_windowDim; + } + pos = m_editActualPos = m_editFinalPos; + dim = m_editActualDim = m_editFinalDim; + pw = m_interface->CreateWindows(pos, dim, 8, EVENT_WINDOW3); if (pw == nullptr) return; @@ -597,6 +616,8 @@ void CStudio::StartEditScript(CScript *script, std::string name, Program* progra pw->SetMinDim(Math::Point(0.49f, 0.50f)); pw->SetMaximized(m_bEditMaximized); pw->SetMinimized(m_bEditMinimized); + + // stop camera control if maximized m_main->SetEditFull(m_bEditMaximized); edit = pw->CreateEdit(pos, dim, 0, EVENT_STUDIO_EDIT); @@ -910,6 +931,12 @@ bool CStudio::StopEditScript(bool closeWithErrors) m_runningPause = nullptr; m_main->SetEditLock(false, true); m_camera->SetType(m_editCamera); + + m_settings->SetIOPos(m_dialogPos); + m_settings->SetIODim(m_dialogDim); + m_settings->SetWindowPos(m_windowPos); + m_settings->SetWindowDim(m_windowDim); + m_settings->SetWindowMax(m_bEditMaximized); return true; } @@ -1249,8 +1276,8 @@ void CStudio::AdjustDialog() pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW9)); if ( pw == nullptr ) return; - wpos = pw->GetPos(); - wdim = pw->GetDim(); + m_dialogPos = wpos = pw->GetPos(); + m_dialogDim = wdim = pw->GetDim(); pw->SetPos(wpos); // to move the buttons on the titlebar if ( m_dialog == SD_OPEN || @@ -1300,10 +1327,9 @@ void CStudio::AdjustDialog() pe->SetPos(ppos); pe->SetDim(ddim); - nch = static_cast< int >((ddim.x*640.0f-22.0f)/8.0f); - name = pe->GetText(100); + nch = static_cast< int >((ddim.x*640.0f-22.0f)/5.75f); + name = pe->GetText(nch); // truncates the text according to max pe->SetMaxChar(nch); - name[nch] = 0; // truncates the text according to max pe->SetText(name); } @@ -1361,19 +1387,10 @@ void CStudio::AdjustDialog() bool CStudio::EventDialog(const Event &event) { - CWindow* pw; - Math::Point wpos, wdim; - - pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW9)); - if ( pw == nullptr ) return false; - if ( event.type == EVENT_WINDOW9 ) // window is moved? { - wpos = pw->GetPos(); - wdim = pw->GetDim(); - m_settings->SetIOPos(wpos); - m_settings->SetIODim(wdim); AdjustDialog(); + return true; } if ( m_dialog == SD_OPEN || @@ -1418,6 +1435,9 @@ bool CStudio::EventDialog(const Event &event) return true; } + CWindow* pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW9)); + if ( pw == nullptr ) return false; + if ( event.type == EVENT_DIALOG_CANCEL || (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(ESCAPE)) || event.type == pw->GetEventTypeClose() ) diff --git a/src/ui/studio.h b/src/ui/studio.h index f3369754..9efddc0f 100644 --- a/src/ui/studio.h +++ b/src/ui/studio.h @@ -108,6 +108,10 @@ protected: Math::Point m_editActualDim; Math::Point m_editFinalPos; Math::Point m_editFinalDim; + Math::Point m_windowPos; + Math::Point m_windowDim; + Math::Point m_dialogPos; + Math::Point m_dialogDim; float m_time; float m_fixInfoTextTime; diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 9e264e1e..f94b72d3 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1533,6 +1533,120 @@ TEST_F(CBotUT, String) " ASSERT(c == \"Colobot!\");\n" "}\n" ); + + ExecuteTest( + "extern void MissingEndQuote()\n" + "{\n" + " \"Colobot...\n" + "}\n", + CBotErrEndQuote + ); +} + +TEST_F(CBotUT, StringEscapeCodes) +{ + ExecuteTest( + "extern void HexEscapeCodes()\n" + "{\n" + " ASSERT(\" \\x07 \" == \" \\a \");\n" + " ASSERT(\" \\x08 \" == \" \\b \");\n" + " ASSERT(\" \\x09 \" == \" \\t \");\n" + " ASSERT(\" \\x0A \" == \" \\n \");\n" + " ASSERT(\" \\x0B \" == \" \\v \");\n" + " ASSERT(\" \\x0C \" == \" \\f \");\n" + " ASSERT(\" \\x0D \" == \" \\r \");\n" + " ASSERT(\" \\x22 \" == \" \\\" \");\n" + " ASSERT(\" \\x27 \" == \" \\\' \");\n" + " ASSERT(\" \\x5C \" == \" \\\\ \");\n" + " string test = \"\\x31 \\x32 \\x33\";\n" + " ASSERT(test == \"1 2 3\");\n" + "}\n" + "extern void OctalEscapeCodes()\n" + "{\n" + " ASSERT(\" \\000 \" == \" \\x00 \");\n" + " ASSERT(\" \\007 \" == \" \\x07 \");\n" + " ASSERT(\" \\010 \" == \" \\x08 \");\n" + " ASSERT(\" \\011 \" == \" \\x09 \");\n" + " ASSERT(\" \\012 \" == \" \\x0A \");\n" + " ASSERT(\" \\013 \" == \" \\x0B \");\n" + " ASSERT(\" \\014 \" == \" \\x0C \");\n" + " ASSERT(\" \\015 \" == \" \\x0D \");\n" + " ASSERT(\" \\042 \" == \" \\x22 \");\n" + " ASSERT(\" \\047 \" == \" \\x27 \");\n" + " ASSERT(\" \\134 \" == \" \\x5C \");\n" + " string test = \"\\101 \\102 \\103\";\n" + " ASSERT(test == \"A B C\");\n" + "}\n" + "extern void UnicodeEscapeCodesToUTF_8()\n" + "{\n" + " ASSERT(\" \\u0000 \" == \" \\0 \");\n" + " ASSERT(\" \\u0007 \" == \" \\a \");\n" + " ASSERT(\" \\u0008 \" == \" \\b \");\n" + " ASSERT(\" \\u0009 \" == \" \\t \");\n" + " ASSERT(\" \\u000A \" == \" \\n \");\n" + " ASSERT(\" \\u000B \" == \" \\v \");\n" + " ASSERT(\" \\u000C \" == \" \\f \");\n" + " ASSERT(\" \\u000D \" == \" \\r \");\n" + " ASSERT(\" \\u0022 \" == \" \\\" \");\n" + " ASSERT(\" \\u0027 \" == \" \\\' \");\n" + " ASSERT(\" \\u005C \" == \" \\\\ \");\n" + "\n" + " ASSERT(\"\\u00A9\" == \"\\xC2\\xA9\");\n" + " ASSERT(\"\\u00AE\" == \"\\xC2\\xAE\");\n" + " ASSERT(\"\\u262E\" == \"\\xE2\\x98\\xAE\");\n" + " ASSERT(\"\\u262F\" == \"\\xE2\\x98\\xAF\");\n" + " ASSERT(\"\\U0001F60E\" == \"\\xF0\\x9F\\x98\\x8E\");\n" + " ASSERT(\"\\U0001F61C\" == \"\\xF0\\x9F\\x98\\x9C\");\n" + " ASSERT(\"\\U0001F6E0\" == \"\\xF0\\x9F\\x9B\\xA0\");\n" + "}\n" + "extern void UnicodeMaxCharacterNameToUTF_8()\n" + "{\n" + " ASSERT(\"\\U0010FFFF\" == \"\\xF4\\x8F\\xBF\\xBF\");\n" + "}\n" + ); +} + +TEST_F(CBotUT, StringEscapeCodeErrors) +{ + ExecuteTest( + "extern void UnknownEscapeSequence()\n" + "{\n" + " \"Unknown: \\p \";\n" + "}\n", + CBotErrBadEscape + ); + + ExecuteTest( + "extern void MissingHexDigits()\n" + "{\n" + " \" \\x \";\n" + "}\n", + CBotErrHexDigits + ); + + ExecuteTest( + "extern void HexValueOutOfRange()\n" + "{\n" + " \" \\x100 \";\n" + "}\n", + CBotErrHexRange + ); + + ExecuteTest( + "extern void OctalValueOutOfRange()\n" + "{\n" + " \" \\400 \";\n" + "}\n", + CBotErrOctalRange + ); + + ExecuteTest( + "extern void BadUnicodeCharacterName()\n" + "{\n" + " \" \\U00110000 \";\n" + "}\n", + CBotErrUnicodeName + ); } // TODO: not implemented, see issue #694 diff --git a/tools/release.py b/tools/release.py old mode 100644 new mode 100755 index 81392726..6ab155af --- a/tools/release.py +++ b/tools/release.py @@ -1,9 +1,10 @@ +#!/usr/bin/env python3 + # Script to use when releasing new versions # Run from main repo directory with data submodule pulled in data/ # -# Note: this has not yet been tested thoughtly, VERIFY EVERYTHING THIS SCRIPT DOES MANUALLY -# # Will automatically: +# * Make sure you don't have any uncommited local changes # * Make sure you don't forget to pull any changes before you start # * Get current version number from CMakeLists.txt # * Merge dev -> master in the data submodule @@ -13,26 +14,48 @@ # * Bump version number in main repo # * Tag release in main repo # * Update dev in main repo to point to the new merge commit +# * Push everything to remote +# * Create release drafts on GitHub # # After finished, verify everything is correct and push the changes import os import re +import sys +import subprocess +import io + +try: + git_root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).strip() +except subprocess.CalledProcessError: + print('\033[1;31m[!] Not inside a git repository!\033[0m') + sys.exit(1) +os.chdir(git_root) +while not os.path.isdir('.git'): + # Likely inside a submodule + # TODO: There is a command called `git rev-parse --show-superproject-working-tree` but it's quite new so not always available :/ + os.chdir('..') + +print('\033[1;34m[*] Make sure you don\'t have any uncommited local changes...\033[0m') +if subprocess.check_output(['git', 'status', '--porcelain']): # This also handles data subdirectory automatically + print('\033[1;31m[!] You have uncommited local changes!\033[0m') + os.system('git status') # Show the changes + sys.exit(1) print('\033[1;34m[*] Make sure all remote changes are pulled...\033[0m') -os.system('git checkout dev') -os.system('git pull --ff') -os.system('git checkout master') -os.system('git pull --ff') +subprocess.check_call(['git', 'checkout', 'dev']) +subprocess.check_call(['git', 'pull', '--ff']) +subprocess.check_call(['git', 'checkout', 'master']) +subprocess.check_call(['git', 'pull', '--ff']) os.chdir('data') -os.system('git checkout dev') -os.system('git pull --ff') -os.system('git checkout master') -os.system('git pull --ff') +subprocess.check_call(['git', 'checkout', 'dev']) +subprocess.check_call(['git', 'pull', '--ff']) +subprocess.check_call(['git', 'checkout', 'master']) +subprocess.check_call(['git', 'pull', '--ff']) os.chdir('..') print('\033[1;34m[*] Get version numbers...\033[0m') -os.system('git checkout dev') +subprocess.check_call(['git', 'checkout', 'dev']) major = None minor = None revision = None @@ -40,7 +63,7 @@ codename = None data = open('CMakeLists.txt', 'r').readlines() for i in range(len(data)): - m = re.match(r'^set\(COLOBOT_VERSION_(MAJOR|MINOR|REVISION)( +)([0-9])+\)$', data[i]) + m = re.match(r'^set\(COLOBOT_VERSION_(MAJOR|MINOR|REVISION)( +)([0-9]+)\)$', data[i]) if m: x = int(m.group(3)) if m.group(1) == 'MAJOR': @@ -60,43 +83,79 @@ for i in range(len(data)): codename = m.group(4) data[i] = ('#' if comment else '')+'set(COLOBOT_VERSION_'+m.group(2)+m.group(3)+'"'+m.group(4)+'")\n' -os.system('git checkout master') +subprocess.check_call(['git', 'checkout', 'master']) version = '%d.%d.%d%s' % (major, minor, revision, codename) +version_human = '%s %d.%d.%d' % (codename.strip('-'), major, minor, revision) print('\033[1;32m[+] Building version '+version+'\033[0m') print('\033[1;34m[*] Merge data...\033[0m') os.chdir('data') -os.system('git merge dev --no-ff -m "Release '+version+': Merge branch \'dev\'"') +subprocess.check_call(['git', 'merge', 'dev', '--no-ff', '-m', 'Release '+version+': Merge branch \'dev\'']) print('\033[1;34m[*] Tag data...\033[0m') -os.system('git tag colobot-gold-'+version) +subprocess.check_call(['git', 'tag', 'colobot-gold-'+version]) print('\033[1;34m[*] Update dev on data...\033[0m') -os.system('git checkout dev') -os.system('git merge master --ff') -os.system('git checkout master') +subprocess.check_call(['git', 'checkout', 'dev']) +subprocess.check_call(['git', 'merge', 'master', '--ff']) +subprocess.check_call(['git', 'checkout', 'master']) print('\033[1;34m[*] Merge main...\033[0m') os.chdir('..') -os.system('git merge dev --no-ff -m "Release '+version+': Merge branch \'dev\'"') +subprocess.check_call(['git', 'merge', 'dev', '--no-ff', '-m', 'Release '+version+': Merge branch \'dev\'']) print('\033[1;34m[*] Bump version number\033[0m') open('CMakeLists.txt', 'w').writelines(data) -os.system('git commit data CMakeLists.txt -m "Release '+version+': Bump version"') +subprocess.check_call(['git', 'commit', 'data', 'CMakeLists.txt', '-m', 'Release '+version+': Bump version']) print('\033[1;34m[*] Tag main...\033[0m') -os.system('git tag colobot-gold-'+version) +subprocess.check_call(['git', 'tag', 'colobot-gold-'+version]) print('\033[1;34m[*] Update dev on main...\033[0m') -os.system('git checkout dev') -os.system('git merge master --ff') +subprocess.check_call(['git', 'checkout', 'dev']) +subprocess.check_call(['git', 'merge', 'master', '--ff']) for i in range(len(data)): m = re.match(r'^(#?)set\(COLOBOT_VERSION_(UNRELEASED|RELEASE_CODENAME)(.*)\)$', data[i]) if m: comment = (m.group(2) == 'RELEASE_CODENAME') data[i] = ('#' if comment else '')+'set(COLOBOT_VERSION_'+m.group(2)+m.group(3)+')\n' open('CMakeLists.txt', 'w').writelines(data) -os.system('git commit CMakeLists.txt -m "Post-release '+version+'"') -os.system('git checkout master') +subprocess.check_call(['git', 'commit', 'CMakeLists.txt', '-m', 'Post-release '+version]) +subprocess.check_call(['git', 'checkout', 'master']) -print('\033[1;32m[+] Done! Push when ready\033[0m') +print('\033[1;32m[+] Done preparing!\033[0m') +resp = "" +while resp != "yes": + resp = input("\033[1;35m[?] Ready to push? (type \'yes\'): \033[0m") + +print('\033[1;34m[*] Pushing...\033[0m') +os.chdir('data') +subprocess.check_call(['git', 'push', 'origin', 'master', 'dev', 'colobot-gold-'+version]) +os.chdir('..') +subprocess.check_call(['git', 'push', 'origin', 'master', 'dev', 'colobot-gold-'+version]) + +hub_available = True +try: + subprocess.check_call(['hub', '--version']) +except subprocess.CalledProcessError: + hub_available = False + print('\033[1;33m[!] hub is not available, will skip creating release drafts!\033[0m') + +if hub_available: + print('\033[1;34m[*] Making release drafts on GitHub...\033[0m') + os.chdir('data') + release_notes = io.StringIO() + release_notes.write('Colobot: Gold Edition - '+version_human+' - data files\n') + release_notes.write('\n') + release_notes.write('Data files for release '+version) + subprocess.check_call(['hub', 'release', 'create', '--draft', '-m', release_notes.getvalue(), 'colobot-gold-'+version]) + os.chdir('..') + release_notes = io.StringIO() + release_notes.write('Colobot: Gold Edition - '+version_human+'\n') + release_notes.write('\n') + release_notes.write('Release '+version+'\n') + release_notes.write('\n') + release_notes.write('[put release notes URL here]') + subprocess.check_call(['hub', 'release', 'create', '--draft', '-m', release_notes.getvalue(), 'colobot-gold-'+version]) + +print('\033[1;32m[+] Done!\033[0m')