diff --git a/CMakeLists.txt b/CMakeLists.txt index f658d005..e3e28781 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,9 @@ set(COLOBOT_VERSION_MINOR 1) set(COLOBOT_VERSION_REVISION 9) # 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) @@ -132,8 +132,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") message(STATUS "Detected GCC version 4.7+") - set(NORMAL_CXX_FLAGS "-std=gnu++11 -Wall -Wold-style-cast -pedantic-errors") - set(NORMAL_CXX_FLAGS "${NORMAL_CXX_FLAGS} -fno-delete-null-pointer-checks") # Temporary hack, see #828 + set(NORMAL_CXX_FLAGS "-std=gnu++11 -Wall -Werror -Wold-style-cast -pedantic-errors") + set(NORMAL_CXX_FLAGS "${NORMAL_CXX_FLAGS} -Wno-error=deprecated-declarations") # updated version of physfs is not available on some platforms so we keep using deprecated functions, see #958 set(RELEASE_CXX_FLAGS "-O2") set(DEBUG_CXX_FLAGS "-g -O0") set(TEST_CXX_FLAGS "-pthread") @@ -145,7 +145,8 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") message(STATUS "Detected Clang version 3.1+") - set(NORMAL_CXX_FLAGS "-std=c++11 -Wall -Wold-style-cast -pedantic-errors") + set(NORMAL_CXX_FLAGS "-std=c++11 -Wall -Werror -Wold-style-cast -pedantic-errors") + set(NORMAL_CXX_FLAGS "${NORMAL_CXX_FLAGS} -Wno-error=deprecated-declarations") # updated version of physfs is not available on some platforms so we keep using deprecated functions, see #958 set(RELEASE_CXX_FLAGS "-O2") set(DEBUG_CXX_FLAGS "-g -O0") set(TEST_CXX_FLAGS "-pthread") diff --git a/Doxyfile.in b/Doxyfile.in index c621ae33..178001f3 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -351,7 +351,7 @@ IDL_PROPERTY_SUPPORT = YES # all members of a group must be documented explicitly. # The default value is: NO. -DISTRIBUTE_GROUP_DOC = NO +DISTRIBUTE_GROUP_DOC = YES # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that @@ -993,7 +993,7 @@ USE_HTAGS = NO # See also: Section \class. # The default value is: YES. -VERBATIM_HEADERS = YES +VERBATIM_HEADERS = NO # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: http://clang.llvm.org/) for more accurate parsing at the diff --git a/Jenkinsfile b/Jenkinsfile index 926f4e2e..2d1b76cf 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,50 +1,67 @@ -node('master') { - stage 'Pull changes' - checkout scm - - stage 'Build Windows' - sh 'mkdir -p build/windows' - dir('build/windows') { - sh ''' - cmake \ - -DCMAKE_INSTALL_PREFIX=/install \ - -DCMAKE_TOOLCHAIN_FILE=/opt/mxe/usr/i686-w64-mingw32.static/share/cmake/mxe-conf.cmake \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDEV_BUILD=1 -DPORTABLE=1 -DTOOLS=1 -DTESTS=0 ../.. - make - rm -rf install - DESTDIR=. make install - ''' - } - sh 'rm -f windows-debug.zip' - zip zipFile: 'windows-debug.zip', archive: true, dir: 'build/windows/install' - - stage 'Build Linux' - sh 'mkdir -p build/linux' - dir('build/linux') { - sh ''' - cmake \ - -DCMAKE_INSTALL_PREFIX=/install -DCOLOBOT_INSTALL_BIN_DIR=/install -DCOLOBOT_INSTALL_LIB_DIR=/install -DCOLOBOT_INSTALL_DATA_DIR=/install/data -DCOLOBOT_INSTALL_I18N_DIR=/install/lang -DCMAKE_SKIP_INSTALL_RPATH=ON \ - -DBOOST_STATIC=ON -DGLEW_STATIC=ON -DGLEW_LIBRARY=/usr/lib64/libGLEW.a \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDEV_BUILD=1 -DPORTABLE=1 -DTOOLS=1 -DTESTS=1 -DDESKTOP=0 ../.. - make - rm -rf install - DESTDIR=. make install - patchelf --set-rpath '.' install/colobot - ''' - } - sh 'rm -f linux-debug.zip' - zip zipFile: 'linux-debug.zip', archive: true, dir: 'build/linux/install' - - stage 'Doxygen' - dir('build/linux') { - sh 'make doc' - } - publishHTML target: [$class: 'HtmlPublisherTarget', reportName: 'Doxygen', reportDir: 'build/linux/doc/html', reportFiles: 'index.html'] - - stage 'Run tests' - dir('build/linux') { - sh './colobot_ut --gtest_output=xml:gtestresults.xml || true' - } - step([$class: 'XUnitBuilder', testTimeMargin: '3000', thresholdMode: 1, thresholds: [[$class: 'FailedThreshold', failureNewThreshold: '', failureThreshold: '', unstableNewThreshold: '', unstableThreshold: '0'], [$class: 'SkippedThreshold', failureNewThreshold: '', failureThreshold: '', unstableNewThreshold: '', unstableThreshold: '']], tools: [[$class: 'GoogleTestType', deleteOutputFiles: true, failIfNotNew: true, pattern: 'build/linux/gtestresults.xml', skipNoTestFiles: false, stopProcessingIfError: true]]]) +#!/usr/bin/env groovy +if (env.BRANCH_NAME.startsWith('PR-')) { + properties([[$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', artifactNumToKeepStr: '1']]]) +} else { + properties([[$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', artifactDaysToKeepStr: '30', artifactNumToKeepStr: '20']]]) } +node('master') { + stage('Pull changes') { + checkout scm + } + + stage('Build Windows') { + sh 'mkdir -p build/windows' + dir('build/windows') { + sh ''' + cmake \ + -DCMAKE_INSTALL_PREFIX=/install \ + -DCMAKE_TOOLCHAIN_FILE=/opt/mxe/usr/i686-w64-mingw32.static/share/cmake/mxe-conf.cmake \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDEV_BUILD=1 -DPORTABLE=1 -DTOOLS=1 -DTESTS=0 ../.. + make + rm -rf install + DESTDIR=. make install + ''' + } + sh 'rm -f windows-debug.zip' + zip zipFile: 'windows-debug.zip', archive: true, dir: 'build/windows/install' + } + + stage('Build Linux') { + sh 'mkdir -p build/linux' + dir('build/linux') { + sh ''' + cmake \ + -DCMAKE_INSTALL_PREFIX=/install -DCOLOBOT_INSTALL_BIN_DIR=/install -DCOLOBOT_INSTALL_LIB_DIR=/install -DCOLOBOT_INSTALL_DATA_DIR=/install/data -DCOLOBOT_INSTALL_I18N_DIR=/install/lang -DCMAKE_SKIP_INSTALL_RPATH=ON \ + -DBOOST_STATIC=ON -DGLEW_STATIC=ON -DGLEW_LIBRARY=/usr/lib64/libGLEW.a \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDEV_BUILD=1 -DPORTABLE=1 -DTOOLS=1 -DTESTS=1 -DDESKTOP=0 ../.. + make + rm -rf install + DESTDIR=. make install + patchelf --set-rpath '.' install/colobot + ''' + } + sh 'rm -f linux-debug.zip' + zip zipFile: 'linux-debug.zip', archive: true, dir: 'build/linux/install' + } + + stage('Doxygen') { + dir('build/linux') { + sh 'make doc' + } + publishHTML target: [$class: 'HtmlPublisherTarget', reportName: 'Doxygen', reportDir: 'build/linux/doc/html', reportFiles: 'index.html'] + } + + stage('Run tests') { + dir('build/linux') { + sh './colobot_ut --gtest_output=xml:gtestresults.xml || true' + } + step([$class: 'XUnitBuilder', testTimeMargin: '3000', thresholdMode: 1, thresholds: [[$class: 'FailedThreshold', failureNewThreshold: '', failureThreshold: '', unstableNewThreshold: '', unstableThreshold: '0'], [$class: 'SkippedThreshold', failureNewThreshold: '', failureThreshold: '', unstableNewThreshold: '', unstableThreshold: '']], tools: [[$class: 'GoogleTestType', deleteOutputFiles: true, failIfNotNew: true, pattern: 'build/linux/gtestresults.xml', skipNoTestFiles: false, stopProcessingIfError: true]]]) + } + + // Clean workspace after building pull requests + // to save disk space on the Jenkins host + if (env.BRANCH_NAME.startsWith('PR-')) { + cleanWs() + } +} diff --git a/README.md b/README.md index be44f545..c7d4a9a5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is official repository for the open-source Colobot: Gold Edition project de The source code contained here was released by Epsitec -- the original creator of the game -- on open source (GPLv3) license. The code was given and the rights granted specifically to ICC community in March 2012. Since then, we have been developing the game further. -More information for developers (in English) can be found on the [developer wiki](http://colobot.info/wiki/dev/) or [our forum](http://colobot.info/forum/). However, the freshest source of information is our IRC channels (see below). +More information for developers (in English) can be found on [our forum](http://colobot.info/forum/). However, the freshest source of information is our IRC channels (see below). This repository contains only the source code of the project. The game requires also data files which are now provided as git submodule and are hosted in [separate repository](https://github.com/colobot/colobot-data). diff --git a/data b/data index f96cf394..2794fd57 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit f96cf394a327210ac9e40d68d68963f25633f758 +Subproject commit 2794fd57c32412d4c9103d20c1ec0c888e1e97a0 diff --git a/po/colobot.pot b/po/colobot.pot index 1f183eea..4aee2bc0 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -260,6 +260,19 @@ msgstr "" msgid "Generating" msgstr "" +msgid "Results" +msgstr "" + +msgid "The battle has ended" +msgstr "" + +#, c-format +msgid "%s: %d pts" +msgstr "" + +msgid "Code battle" +msgstr "" + msgid "Cancel" msgstr "" @@ -1346,6 +1359,9 @@ msgstr "" msgid "Unknown command" msgstr "" +msgid "This object is currently busy" +msgstr "" + msgid "Impossible when flying" msgstr "" @@ -1598,6 +1614,18 @@ msgstr "" msgid "Press \\key help; to read instructions on your SatCom" msgstr "" +#, c-format +msgid "<<< Team %s finished! >>>" +msgstr "" + +#, c-format +msgid "<<< Team %s lost! >>>" +msgstr "" + +#, c-format +msgid "<<< Team %s recieved %d points >>>" +msgstr "" + msgid "Opening bracket missing" msgstr "" @@ -1733,6 +1761,18 @@ msgstr "" msgid "Ambiguous call to overloaded function" msgstr "" +msgid "Function needs return type \"void\"" +msgstr "" + +msgid "Class name expected" +msgstr "" + +msgid "Non-void function needs \"return;\"" +msgstr "" + +msgid "This parameter needs a default value" +msgstr "" + msgid "Dividing by zero" msgstr "" diff --git a/po/de.po b/po/de.po index 05a31006..02277956 100644 --- a/po/de.po +++ b/po/de.po @@ -1,21 +1,21 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. +# Chris , 2016. msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: DATE\n" -"PO-Revision-Date: 2014-07-28 09:27+0200\n" -"Last-Translator: krzys_h \n" -"Language-Team: LANGUAGE \n" +"PO-Revision-Date: 2016-10-28 21:29+0200\n" +"Last-Translator: Chris \n" +"Language-Team: Colobot: Gold Edition freelancer\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Pootle 2.5.1.1\n" +"X-Generator: Virtaal 0.7.1\n" "X-Language: de_DE\n" "X-Source-Language: en_US\n" "X-POOTLE-MTIME: 1406536037.000000\n" @@ -29,6 +29,10 @@ msgstr "Es fehlt eine offene eckige Klammer \" [ \"" msgid "\" ] \" missing" msgstr "Es fehlt eine geschlossene eckige Klammer \" ] \"" +#, c-format +msgid "%s: %d pts" +msgstr "" + msgid "..behind" msgstr "..hinten" @@ -42,7 +46,7 @@ msgid "1) First click on the key you want to redefine." msgstr "1) Klicken Sie auf die neu zu definierende Taste." msgid "2) Then press the key you want to use instead." -msgstr "2) Drücken Sie auf die neue Taste." +msgstr "2) Drücken Sie auf die neu zu verwendende Taste." msgid "<< Back \\Back to the previous screen" msgstr "<< Zurück \\Zurück zum Hauptmenü" @@ -50,6 +54,18 @@ msgstr "<< Zurück \\Zurück zum Hauptmenü" msgid "<<< Sorry; mission failed >>>" msgstr "<<< Mission gescheitert >>>" +#, c-format +msgid "<<< Team %s finished! >>>" +msgstr "" + +#, c-format +msgid "<<< Team %s lost! >>>" +msgstr "" + +#, c-format +msgid "<<< Team %s recieved %d points >>>" +msgstr "" + msgid "<<< Well done; mission accomplished >>>" msgstr "<<< Bravo, Mission vollendet >>>" @@ -57,7 +73,7 @@ msgid "A label must be followed by \"for\"; \"while\"; \"do\" or \"switch\"" msgstr "Ein Label kann nur vor den Anweisungen \"for\", \"while\", \"do\" oder \"switch\" vorkommen" msgid "A variable can not be declared twice" -msgstr "Eine Variable wird zum zweiten Mal deklariert" +msgstr "Eine Variable darf nicht zwei Mal deklariert werden" msgid "Abort\\Abort the current mission" msgstr "Abbrechen\\Mission abbrechen" @@ -72,7 +88,7 @@ msgid "Access to solutions\\Show program \"4: Solution\" in the exercises" msgstr "Lösung zugänglich\\Die Lösung ist im Programmslot \"4: Lösung\" zugänglich" msgid "Add new program" -msgstr "" +msgstr "Neues Programm hinzufügen" msgid "Alien Queen" msgstr "Insektenkönigin" @@ -85,6 +101,8 @@ 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)" msgid "Ambiguous call to overloaded function" msgstr "" @@ -99,7 +117,7 @@ msgid "Analyzes only organic matter" msgstr "Analysiert nur Orgastoff" msgid "Anisotropy level\\Anisotropy level" -msgstr "" +msgstr "Anisotropie-Level\\Anisotropie-Level" msgid "Ant" msgstr "Ameise" @@ -111,7 +129,7 @@ msgid "Appearance\\Choose your appearance" msgstr "Aussehen\\Erscheinungsbild des Astronauten einstellen" msgid "Apply changes\\Activates the changed settings" -msgstr "Änderungen ausführen\\Getätigte Einstellungen ausführen" +msgstr "Änderungen anwenden\\Getätigte Einstellungen anwenden" msgid "Appropriate constructor missing" msgstr "Es gibt keinen geeigneten Konstruktor" @@ -127,19 +145,22 @@ 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" 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" msgid "Autosave\\Enables autosave" -msgstr "" +msgstr "Auto-Speichern\\Aktiviert die automatische Speicherung" msgid "Back" msgstr "Vorherg. Seite" -#, fuzzy msgid "Background sound:\\Volume of audio tracks" -msgstr "Geräuschkulisse:\\Lautstärke der Soundtracks der CD" +msgstr "Hintergrundgeräusche:\\Lautstärke der Soundtracks" msgid "Backward (\\key down;)" msgstr "Rückwärts (\\key down;)" @@ -157,7 +178,7 @@ msgid "Black box" msgstr "Flugschreiber" msgid "Blood\\Display blood when the astronaut is hit" -msgstr "" +msgstr "Blut\\Blut anzeigen, wenn der Astronaut verletzt wird" msgid "Blue" msgstr "Blau" @@ -294,28 +315,25 @@ msgstr "Kamera (\\key camera;)" msgid "Camera back\\Moves the camera backward" msgstr "Kamera weiter\\Bewegung der Kamera rückwärts" -#, fuzzy msgid "Camera border scrolling\\Scrolling when the mouse touches right or left border" -msgstr "Kameradrehung mit der Maus\\Die Kamera dreht wenn die Maus den 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" -#, fuzzy msgid "Camera down\\Turns the camera down" -msgstr "Kamera näher\\Bewegung der Kamera vorwärts" +msgstr "Kamera ab\\Bewegt die Kamera abwärts" -#, fuzzy msgid "Camera left\\Turns the camera left" -msgstr "Kamera näher\\Bewegung der Kamera vorwärts" +msgstr "Kamera links\\Schwenkt die Kamera nach links" -#, fuzzy msgid "Camera right\\Turns the camera right" -msgstr "Drehung nach rechts\\Steuer rechts" +msgstr "Kamera rechts\\Schwenkt die Kamera nach rechts" -#, fuzzy msgid "Camera up\\Turns the camera up" -msgstr "Kamera (\\key camera;)" +msgstr "Kamera auf\\Bewegt die Kamera aufwärts" msgid "Can not produce not researched object" msgstr "Das erforschte Objekt kann nicht produziert werden" @@ -333,13 +351,13 @@ msgid "Cancel\\Cancel all changes" msgstr "Abbrechen\\Editor schließen" msgid "Challenges" -msgstr "Challenges" +msgstr "Herausforderungen" msgid "Challenges in the chapter:" -msgstr "Liste der Challenges des Kapitels:" +msgstr "Herausforderungen in diesem Kapitel:" msgid "Challenges\\Programming challenges" -msgstr "Challenges\\Herausforderungen" +msgstr "Herausforderungen\\Programmieraufgaben" msgid "Change camera\\Switches between onboard camera and following camera" msgstr "Andere Kamera\\Sichtpunkt einstellen" @@ -351,20 +369,22 @@ msgid "Chapters:" msgstr "Liste der Kapitel:" msgid "Cheat console\\Show cheat console" -msgstr "" +msgstr "Mogel-Konsole\\Zeige die Mogel-Konsole" msgid "Checkpoint" -msgstr "Checkpoint" +msgstr "Kontrollstelle" + +msgid "Class name expected" +msgstr "" msgid "Climb\\Increases the power of the jet" msgstr "Steigen\\Leistung des Triebwerks steigern" msgid "Clone program" -msgstr "" +msgstr "Programm duplizieren" -#, fuzzy msgid "Clone selected program" -msgstr "Gewähltes Programm bearbeiten" +msgstr "Gewähltes Programm duplizieren" msgid "Close" msgstr "Schließen" @@ -372,11 +392,14 @@ msgstr "Schließen" msgid "Closing bracket missing" msgstr "Es fehlt eine geschlossene Klammer \")\"" -msgid "Code battles" +msgid "Code battle" msgstr "" +msgid "Code battles" +msgstr "Programmierschlacht" + msgid "Code battles\\Program your robot to be the best of them all!" -msgstr "" +msgstr "Programmierschlacht\\Schreibe das Programm für den besten Roboter!" msgid "Colobot rules!" msgstr "Colobot ist wunderbar!" @@ -385,7 +408,7 @@ msgid "Colobot: Gold Edition" msgstr "Colobot: Gold Edition" msgid "Command line" -msgstr "Befehleingabe" +msgstr "Befehlseingabe" msgid "Compilation ok (0 errors)" msgstr "Kompilieren OK (0 Fehler)" @@ -403,7 +426,7 @@ msgid "Controls\\Keyboard, joystick and mouse settings" msgstr "Steuerung\\Auswahl der Tasten" msgid "Converts ore to titanium" -msgstr "Konverter Erz-Titan" +msgstr "Konvertiert Erz in Titan" msgid "Copy" msgstr "Kopieren" @@ -414,12 +437,11 @@ msgstr "Kopieren (Ctrl+C)" msgid "Current mission saved" msgstr "Mission gespeichert" -#, fuzzy msgid "Custom levels:" -msgstr "Userlevels:" +msgstr "Benutzerdefinierte Level:" msgid "Custom levels\\Levels from mods created by the users" -msgstr "" +msgstr "Benutzerdefinierte Level\\Level die von anderen Spieler erstellt wurden" msgid "Customize your appearance" msgstr "Aussehen einstellen" @@ -430,9 +452,8 @@ msgstr "Ausschneiden (Ctrl+X)" msgid "Defense tower" msgstr "Geschützturm" -#, fuzzy msgid "Delete mark" -msgstr "Zerstören" +msgstr "Markierung entfernen" msgid "Delete player\\Deletes the player from the list" msgstr "Spieler löschen\\Löscht den Spieler aus der Liste" @@ -459,7 +480,7 @@ msgid "Device\\Driver and resolution settings" msgstr "Bildschirm\\Driver und Bildschirmauflösung" msgid "Dividing by zero" -msgstr "Teilung durch Null" +msgstr "Division durch Null" msgid "Do you really want to destroy the selected building?" msgstr "Wollen Sie das angewählte Gebäude wirklich zerstören ?" @@ -484,10 +505,10 @@ msgid "Dynamic lighting\\Mobile light sources" msgstr "Dynamische Beleuchtung\\Dynamische Beleuchtung" msgid "Dynamic shadows ++\\Dynamic shadows + self shadowing" -msgstr "" +msgstr "Dynamische Schatten ++\\Dynamische Schatten + Eigenschatten" msgid "Dynamic shadows\\Beautiful shadows!" -msgstr "" +msgstr "Dynamische Schatten\\Hübsche Schatten!" msgid "Edit the selected program" msgstr "Gewähltes Programm bearbeiten" @@ -529,7 +550,7 @@ msgid "Explosive" msgstr "Sprengstoff" msgid "Expression expected after =" -msgstr "" +msgstr "Nach = wird ein Ausdruck erwartet" msgid "Extend shield (\\key action;)" msgstr "Schutzschild ausfahren (\\key action;)" @@ -556,7 +577,7 @@ msgid "Fixed mine" msgstr "Landmine" msgid "Flat ground not large enough" -msgstr "Ebener Boden nicht groß genug" +msgstr "Ebene Fläche nicht groß genug" msgid "Fog\\Fog" msgstr "Nebel\\Nebelschwaden" @@ -569,7 +590,7 @@ msgid "Folder: %s" msgstr "Ordner: %s" msgid "Font size" -msgstr "Zeichengröße" +msgstr "Schriftgröße" msgid "Forward" msgstr "Nächste Seite" @@ -599,13 +620,13 @@ msgid "Found key D (site for derrick)" msgstr "Markierung für vergrabenen Schlüssel D" msgid "Free game" -msgstr "Freestyle" +msgstr "Freies Spiel" msgid "Free game on this planet:" msgstr "Liste der freien Levels des Planeten:" msgid "Free game\\Free game without a specific goal" -msgstr "Freestyle\\Freies Spielen ohne vorgegebenes Ziel" +msgstr "Freies Spiel\\Freies Spielen ohne vorgegebenes Ziel" msgid "Full screen\\Full screen or window mode" msgstr "Vollbildschirm\\Vollbildschirm oder Fenster" @@ -616,6 +637,9 @@ msgstr "Diese Funktion gibt es schon" msgid "Function name missing" msgstr "Hier muss der Name der Funktion stehen" +msgid "Function needs return type \"void\"" +msgstr "" + msgid "Game speed" msgstr "Spielgeschwindigkeit" @@ -626,10 +650,10 @@ msgid "Gantry crane" msgstr "Träger" msgid "Generating" -msgstr "" +msgstr "Generieren" msgid "Gold Edition development by:" -msgstr "Goldausgabe Entwicklung von:" +msgstr "Gold-Edition entwickelt von:" msgid "Goto: destination occupied" msgstr "Ziel ist schon besetzt" @@ -641,7 +665,7 @@ msgid "Grab or drop (\\key action;)" msgstr "Nehmen oder hinlegen (\\key action;)" msgid "Graphics\\Graphics settings" -msgstr "Grafik\\Grafische Einstellungen" +msgstr "Grafik\\Grafik-Einstellungen" msgid "Green" msgstr "Grün" @@ -662,7 +686,7 @@ msgid "Help about selected object" msgstr "Anweisungen über das ausgewählte Objekt" msgid "Help balloons\\Explain the function of the buttons" -msgstr "Hilfsblasen\\Hilfsblasen" +msgstr "Hilfeblasen\\Hilfeblasen" msgid "Highest\\Highest graphic quality (lowest frame rate)" msgstr "Max.\\Beste Qualität (niedriges Framerate)" @@ -697,9 +721,8 @@ msgstr "Roboter ungeeignet" msgid "Inappropriate cell type" msgstr "Falscher Batterietyp" -#, fuzzy msgid "Inappropriate object" -msgstr "Roboter ungeeignet" +msgstr "Objekt ungeeignet" msgid "Incorrect index type" msgstr "Falscher Typ für einen Index" @@ -738,10 +761,10 @@ msgid "Instructions\\Shows the instructions for the current mission" msgstr "Anweisungen\\Anweisungen für die Mission oder Übung" msgid "Internal error - tell the developers" -msgstr "Interner Fehler - Benachrichtige die Entwickler" +msgstr "Interner Fehler - Benachrichtige bitte die Entwickler" msgid "Invert\\Invert values on this axis" -msgstr "" +msgstr "Invertieren\\Die Werte dieser Achse invertieren" msgid "Jet temperature" msgstr "Triebwerktemperatur" @@ -801,39 +824,37 @@ msgid "Load\\Loads the selected mission" msgstr "Laden\\Öffnet eine gespeicherte Mission" msgid "Loading basic level settings" -msgstr "" +msgstr "Lade Level-Grundeinstellungen" -#, fuzzy msgid "Loading finished!" -msgstr "Programm beendet" +msgstr "Laden beendet!" msgid "Loading music" -msgstr "" +msgstr "Lade Musik" -#, fuzzy msgid "Loading objects" -msgstr "Liste der Objekte" +msgstr "Lade Objekte" msgid "Loading terrain" -msgstr "" +msgstr "Lade Gelände" msgid "Lowest\\Minimum graphic quality (highest frame rate)" msgstr "Min.\\Minimale Qualität (großes Framerate)" msgid "Lunar Roving Vehicle" -msgstr "Lunar Roving Vehicle" +msgstr "Mondlandefahrzeug" msgid "MSAA\\Multisample anti-aliasing" -msgstr "" +msgstr "MSAA\\Multisample anti-aliasing" msgid "Maximize" msgstr "Großes Fenster" msgid "Minimize" -msgstr "Reduzieren" +msgstr "Verkleinern" msgid "Mipmap level\\Mipmap level" -msgstr "" +msgstr "Mipmap-Level\\Mipmap-Level" msgid "Mission name" msgstr "Name der Mission" @@ -853,13 +874,11 @@ msgstr "Umkehr X\\Umkehr der Kameradrehung X-Achse" msgid "Mouse inversion Y\\Inversion of the scrolling direction on the Y axis" msgstr "Umkehr Y\\Umkehr der Kameradrehung Y-Achse" -#, fuzzy msgid "Move selected program down" -msgstr "Gewähltes Programm bearbeiten" +msgstr "Gewähltes Programm nach unten" -#, fuzzy msgid "Move selected program up" -msgstr "Gewähltes Programm bearbeiten" +msgstr "Gewähltes Programm nach oben" msgid "Mute\\No sound" msgstr "Kein Ton\\Keine Geräusche und Geräuschkulisse" @@ -889,7 +908,7 @@ msgid "Next object\\Selects the next object" msgstr "Nächstes auswählen\\Nächstes Objekt auswählen" msgid "No" -msgstr "" +msgstr "Nein" msgid "No energy in the subsoil" msgstr "Kein unterirdisches Energievorkommen" @@ -901,10 +920,10 @@ msgid "No function running" msgstr "Keine Funktion wird ausgeführt" msgid "No function with this name accepts this kind of parameter" -msgstr "Keine Funktion mit diesem Namen verträgt Parameter diesen Typs" +msgstr "Keine Funktion mit diesem Namen akzeptiert Parameter diesen Typs" msgid "No function with this name accepts this number of parameters" -msgstr "Keine Funktion mit diesem Namen verträgt diese Anzahl Parameter" +msgstr "Keine Funktion mit diesem Namen akzeptiert diese Anzahl Parameter" msgid "No information exchange post within range" msgstr "Kein Infoserver in Reichweite" @@ -934,6 +953,9 @@ msgid "No uranium to transform" msgstr "Kein konvertierbares Platin" msgid "No userlevels installed!" +msgstr "Keine benutzerdefinierten Level vorhanden" + +msgid "Non-void function needs \"return;\"" msgstr "" msgid "Normal size" @@ -1012,7 +1034,7 @@ msgid "Opening bracket missing" msgstr "Es fehlt eine offene Klammer \"(\"" msgid "Operation impossible with value \"nan\"" -msgstr "Operation mit dem Wert \"nan\"" +msgstr "Operation mit dem Wert \"nan\" nicht möglich" msgid "Options" msgstr "Einstellungen" @@ -1039,16 +1061,18 @@ msgid "Paste (Ctrl+V)" msgstr "Einfügen (Ctrl+V)" msgid "Pause blur\\Blur the background on the pause screen" -msgstr "" +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" msgid "Pause/continue" msgstr "Pause/Weitermachen" msgid "Pause\\Pause the game without opening menu" -msgstr "" +msgstr "Pause\\Spiel pausieren, ohne das Menü zu öffnen" msgid "Phazer shooter" msgstr "Phazershooter" @@ -1093,10 +1117,10 @@ msgid "Player" msgstr "Spieler" msgid "Player name" -msgstr "Name " +msgstr "Spielername " msgid "Player's name" -msgstr "Name " +msgstr "Name des Spielers" msgid "Power cell" msgstr "Elektrolytische Batterie" @@ -1117,7 +1141,7 @@ msgid "Press \\key help; to read instructions on your SatCom" msgstr "Beziehen Sie sich auf Ihren SatCom, indem Sie auf \\key help; drücken" msgid "Previous" -msgstr "Vorherg" +msgstr "Vorherg." msgid "Previous object\\Selects the previous object" msgstr "Vorherg. Auswahl\\Das vorhergehende Objekt auswählen" @@ -1132,11 +1156,10 @@ msgid "Private\\Private folder" msgstr "Privat\\Privater Ordner" msgid "Processing level file" -msgstr "" +msgstr "Verarbeite Level-Datei" -#, fuzzy msgid "Program cloned" -msgstr "Programm beendet" +msgstr "Programm dupliziert" msgid "Program editor" msgstr "Programmeditor" @@ -1172,7 +1195,7 @@ msgid "Quake at explosions\\The screen shakes at explosions" msgstr "Beben bei Explosionen\\Die Kamera bebt bei Explosionen" msgid "Quit\\Quit Colobot: Gold Edition" -msgstr "" +msgstr "Beenden\\Colobot: Gold Edition schließen" msgid "Quit\\Quit the current mission or exercise" msgstr "Mission verlassen\\Eine Mission oder Übung verlassen" @@ -1207,11 +1230,9 @@ msgstr "Überreste einer Apollo-Mission" msgid "Remove a flag" msgstr "Sammelt die Fahne ein" -#, fuzzy msgid "Remove selected program" -msgstr "Gewähltes Programm bearbeiten" +msgstr "Gewähltes Programm entfernen" -#, fuzzy msgid "Render distance\\Maximum visibility" msgstr "Sichtweite\\Maximale Sichtweite" @@ -1227,9 +1248,8 @@ msgstr "Forschungsprogramm schon ausgeführt" msgid "Research program completed" msgstr "Forschungsprogramm abgeschlossen" -#, fuzzy msgid "Reserved keyword of CBOT language" -msgstr "Dieses Wort ist reserviert" +msgstr "Reserviertes Schlüsselwort in der CBOT Sprache" msgid "Resolution" msgstr "Auflösung" @@ -1238,15 +1258,18 @@ msgid "Resolution:" msgstr "Auflösung:" msgid "Resources" -msgstr "" +msgstr "Ressourcen" msgid "Restart\\Restart the mission from the beginning" msgstr "Neu anfangen\\Die Mission von vorne anfangen" msgid "Restoring CBot execution state" -msgstr "" +msgstr "CBOT Ausführungsstatus wiederherstellen" msgid "Restoring saved objects" +msgstr "Gespeicherte Objekte wiederherstellen" + +msgid "Results" msgstr "" msgid "Return to start" @@ -1316,10 +1339,10 @@ msgid "Semicolon terminator missing" msgstr "Es fehlt ein Strichpunkt \";\" am Ende der Anweisung" msgid "Shadow resolution\\Higher means better range and quality, but slower" -msgstr "" +msgstr "Schatten-Auflösung\\Höher heißt bessere Qualität, aber langsamer" msgid "Shield level" -msgstr "Schäden" +msgstr "Schildstärke" msgid "Shield radius" msgstr "Reichweite Schutzschild" @@ -1345,9 +1368,8 @@ msgstr "Zeigt die Lösung" msgid "Sign \" : \" missing" msgstr "Es fehlt ein Doppelpunkt \" : \"" -#, fuzzy msgid "Simple shadows\\Shadows spots on the ground" -msgstr "Schatten\\Schlagschatten auf dem Boden" +msgstr "Einfacher Schatten\\Schlagschatten auf dem Boden" msgid "Size 1" msgstr "Größe 1" @@ -1380,11 +1402,10 @@ msgid "Spaceship" msgstr "Raumschiff" msgid "Spaceship ruin" -msgstr "Raumschiffruine" +msgstr "Raumschiffswrack" -#, fuzzy msgid "Speed 0.5x\\Half speed" -msgstr "Geschwindigkeit 1.0x\\Normale Spielgeschwindigkeit" +msgstr "Geschwindigkeit 0.5x\\Halbe Spielgeschwindigkeit" msgid "Speed 1.0x\\Normal speed" msgstr "Geschwindigkeit 1.0x\\Normale Spielgeschwindigkeit" @@ -1395,17 +1416,14 @@ msgstr "Geschwindigkeit 1.5x\\Spielgeschwindigkeit anderthalb Mal schneller" msgid "Speed 2.0x\\Double speed" msgstr "Geschwindigkeit 2.0x\\Spielgeschwindigkeit doppelt so schnell" -#, fuzzy msgid "Speed 3.0x\\Triple speed" -msgstr "Geschwindigkeit 2.0x\\Spielgeschwindigkeit doppelt so schnell" +msgstr "Geschwindigkeit 3.0x\\Dreifache Spielgeschwindigkeit" -#, fuzzy msgid "Speed 4.0x\\Quadruple speed" -msgstr "Geschwindigkeit 2.0x\\Spielgeschwindigkeit doppelt so schnell" +msgstr "Geschwindigkeit 4.0x\\Vierfache Spielgeschwindigkeit" -#, fuzzy msgid "Speed 6.0x\\Sextuple speed" -msgstr "Geschwindigkeit 2.0x\\Spielgeschwindigkeit doppelt so schnell" +msgstr "Geschwindigkeit 6.0x\\Sechsfache Spielgeschwindigkeit" msgid "Spider" msgstr "Spinne" @@ -1428,12 +1446,11 @@ msgstr "Standard\\Standardfarben einsetzen" msgid "Start" msgstr "Startfläche" -#, fuzzy msgid "Starting..." -msgstr "Startfläche" +msgstr "Starte..." msgid "Still working ..." -msgstr "Prozess im Gang ..." +msgstr "Verarbeitung ..." msgid "String missing" msgstr "Hier wird eine Zeichenkette erwartet" @@ -1469,12 +1486,15 @@ msgid "Target bot" msgstr "Mobile Zielscheibe" msgid "Terrain relief" -msgstr "" +msgstr "Geländestruktur" msgid "Texture filtering\\Texture filtering" -msgstr "" +msgstr "Texturfilterung\\Texturfilterung" msgid "Textures" +msgstr "Texturen" + +msgid "The battle has ended" msgstr "" msgid "The expression must return a boolean value" @@ -1496,7 +1516,7 @@ msgid "This class does not exist" msgstr "Diese Klasse existiert nicht" msgid "This is example code that cannot be run directly" -msgstr "" +msgstr "Das ist ein Beispiel Programm, das nicht direkt ausgeführt werden kann" msgid "This is not a member of this class" msgstr "Dieses Element gibt es nicht in dieser Klasse" @@ -1506,13 +1526,21 @@ 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" + +msgid "This object is currently busy" +msgstr "" msgid "This object is not a member of a class" msgstr "Das Objekt ist nicht eine Instanz einer Klasse" -msgid "This program is read-only, clone it to edit" +msgid "This parameter needs a default value" msgstr "" +msgid "This program is read-only, clone it to edit" +msgstr "Dieses Programm ist schreibgeschützt. Dupliziere es zum Bearbeiten" + msgid "Thump (\\key action;)" msgstr "Stampfen (\\key action;)" @@ -1589,10 +1617,10 @@ msgid "Type declaration missing" msgstr "Hier muss ein Variablentyp stehen" msgid "Unable to control enemy objects" -msgstr "" +msgstr "Feindliches Objekt kann nicht gesteuert werden" msgid "Undo (Ctrl+Z)" -msgstr "Widerrufen (Ctrl+Z)" +msgstr "Rückgängig (Ctrl+Z)" msgid "Unit" msgstr "Einheit" @@ -1691,7 +1719,7 @@ msgid "Yellow flag" msgstr "Gelbe Fahne" msgid "Yes" -msgstr "" +msgstr "Ja" msgid "You can fly with the keys (\\key gup;) and (\\key gdown;)" msgstr "Sie können jetzt mit den Tasten \\key gup; und \\key gdown; fliegen" @@ -1702,27 +1730,35 @@ msgstr "Sie können keinen radioaktiven Gegenstand tragen" msgid "You can not carry an object under water" msgstr "Sie können unter Wasser nichts tragen" -#, fuzzy, c-format +#, c-format msgid "You cannot use \"%s\" in this exercise (used: %d)" -msgstr "In dieser Übung verboten" +msgstr "\"%s\" kann in dieser Übung nicht verwendet werden (benutzt: %d)" msgid "You found a usable object" msgstr "Sie haben ein brauchbares Objekt gefunden" -#, fuzzy, c-format +#, 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 verboten" -msgstr[1] "In dieser Übung verboten" +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)" -#, fuzzy, c-format +#, 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 verboten" -msgstr[1] "In dieser Übung verboten" +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 "Gehen Sie an Bord, bevor Sie abheben" +msgstr "Begib Dich an Bord, bevor Du abhebst" msgid "Zoom mini-map" msgstr "Zoom Minikarte" @@ -1773,7 +1809,7 @@ msgid "\\Red flags" msgstr "\\Rote Fahne" msgid "\\Return to Colobot: Gold Edition" -msgstr "" +msgstr "\\Zurück zu Colobot: Gold-Edition" msgid "\\SatCom on standby" msgstr "\\SatCom in Standby" diff --git a/po/fr.po b/po/fr.po index 9fb7a344..2de5e5ab 100644 --- a/po/fr.po +++ b/po/fr.po @@ -1,11 +1,12 @@ # Didier Raboud , 2012, 2015, 2016. +# Martin Quinson , 2016 msgid "" msgstr "" "Project-Id-Version: Colobot 0.1.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: DATE\n" -"PO-Revision-Date: 2016-03-25 15:01+0100\n" -"Last-Translator: Didier Raboud \n" +"PO-Revision-Date: 2016-12-02 15:31+0100\n" +"Last-Translator: Martin Quinson \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,6 +25,10 @@ msgstr "\" [ \" attendu" msgid "\" ] \" missing" msgstr "\" ] \" attendu" +#, c-format +msgid "%s: %d pts" +msgstr "" + msgid "..behind" msgstr "..derrière" @@ -45,6 +50,18 @@ msgstr "<< Retour \\Retour au niveau précédent" msgid "<<< Sorry; mission failed >>>" msgstr "<<< Désolé; mission échouée >>>" +#, c-format +msgid "<<< Team %s finished! >>>" +msgstr "" + +#, c-format +msgid "<<< Team %s lost! >>>" +msgstr "" + +#, c-format +msgid "<<< Team %s recieved %d points >>>" +msgstr "" + msgid "<<< Well done; mission accomplished >>>" msgstr "<<< Bravo; mission terminée >>>" @@ -79,10 +96,10 @@ msgid "Already carrying something" msgstr "Porte déjà quelque chose" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" -msgstr "" +msgstr "Mode caméra alternatif\\Déplacements latéraux au lieu des rotations (pour la caméra libre)" msgid "Ambiguous call to overloaded function" -msgstr "" +msgstr "Appel ambigu à une fonction surchargée" msgid "Analysis already performed" msgstr "Analyse déjà effectuée" @@ -294,21 +311,17 @@ msgstr "Défilement dans les bords\\Défilement lorsque la souris touche les bor msgid "Camera closer\\Moves the camera forward" msgstr "Caméra plus proche\\Avance la caméra" -#, fuzzy msgid "Camera down\\Turns the camera down" -msgstr "Caméra plus proche\\Avance la caméra" +msgstr "Baisser caméra\\Baisse la caméra" -#, fuzzy msgid "Camera left\\Turns the camera left" -msgstr "Caméra plus proche\\Avance la caméra" +msgstr "Caméra à gauche\\Tourne la caméra vers la gauche" -#, fuzzy msgid "Camera right\\Turns the camera right" -msgstr "Tourner à droite\\Moteur à droite" +msgstr "Caméra à droite\\Tourne la caméra vers la droite" -#, fuzzy msgid "Camera up\\Turns the camera up" -msgstr "Caméra (\\key camera;)" +msgstr "Lever caméra\\Monte la caméra" msgid "Can not produce not researched object" msgstr "Impossible de créer un objet n'ayant pas été recherché" @@ -349,6 +362,9 @@ msgstr "Console de triche\\Montre la console de triche" msgid "Checkpoint" msgstr "Indicateur" +msgid "Class name expected" +msgstr "" + msgid "Climb\\Increases the power of the jet" msgstr "Monter\\Augmenter la puissance du réacteur" @@ -364,6 +380,10 @@ msgstr "Fermer" msgid "Closing bracket missing" msgstr "Il manque une parenthèse fermante" +#, fuzzy +msgid "Code battle" +msgstr "Batailles de code" + msgid "Code battles" msgstr "Batailles de code" @@ -519,7 +539,7 @@ msgid "Explosive" msgstr "Explosif" msgid "Expression expected after =" -msgstr "" +msgstr "Expression attendue après =" msgid "Extend shield (\\key action;)" msgstr "Déploie le bouclier (\\key action;)" @@ -606,6 +626,9 @@ msgstr "Cette fonction existe déjà" msgid "Function name missing" msgstr "Nom de la fonction attendu" +msgid "Function needs return type \"void\"" +msgstr "" + msgid "Game speed" msgstr "Vitesse du jeu" @@ -730,7 +753,7 @@ msgid "Internal error - tell the developers" msgstr "Erreur interne - contacter les développeurs" msgid "Invert\\Invert values on this axis" -msgstr "" +msgstr "Inversion\\Inverse les valeurs sur cet axe" msgid "Jet temperature" msgstr "Température du réacteur" @@ -921,6 +944,9 @@ msgstr "Pas d'uranium à transformer" msgid "No userlevels installed!" msgstr "Pas de niveaux spéciaux installés !" +msgid "Non-void function needs \"return;\"" +msgstr "" + msgid "Normal size" msgstr "Taille normale" @@ -1024,10 +1050,10 @@ msgid "Paste (Ctrl+V)" msgstr "Coller (Ctrl+V)" msgid "Pause blur\\Blur the background on the pause screen" -msgstr "" +msgstr "Flouter les pauses\\Floute le fond de l'écran de pause" msgid "Pause in background\\Pause the game when the window is unfocused" -msgstr "" +msgstr "Pause en arrière-plan\\Met le jeu en pause quand la fenêtre n'a plus le focus" msgid "Pause/continue" msgstr "Pause/continuer" @@ -1219,15 +1245,18 @@ msgid "Resolution:" msgstr "Résolutions :" msgid "Resources" -msgstr "" +msgstr "Ressources" msgid "Restart\\Restart the mission from the beginning" msgstr "Recommencer\\Recommencer la mission au début" msgid "Restoring CBot execution state" -msgstr "" +msgstr "Restaurer l'état d'exécution CBOT" msgid "Restoring saved objects" +msgstr "Restaurer des objets sauvés" + +msgid "Results" msgstr "" msgid "Return to start" @@ -1452,6 +1481,9 @@ msgstr "Filtrage de textures\\Filtrage de textures" msgid "Textures" msgstr "Textures" +msgid "The battle has ended" +msgstr "" + msgid "The expression must return a boolean value" msgstr "L'expression doit ętre un boolean" @@ -1482,9 +1514,15 @@ msgstr "Cette étiquette n'existe pas" msgid "This menu is for userlevels from mods, but you didn't install any" msgstr "Ce menu donne accès aux niveaux spéciaux (importés ou personnalisés), mais aucun n'est installé." +msgid "This object is currently busy" +msgstr "" + msgid "This object is not a member of a class" msgstr "L'objet n'est pas une instance d'une classe" +msgid "This parameter needs a default value" +msgstr "" + msgid "This program is read-only, clone it to edit" msgstr "Ce programme est en lecture-seule, le dupliquer pour pouvoir l'éditer" @@ -1677,24 +1715,24 @@ msgstr "Vous ne pouvez pas transporter un objet radioactif" msgid "You can not carry an object under water" msgstr "Vous ne pouvez pas transporter un objet sous l'eau" -#, fuzzy, c-format +#, c-format msgid "You cannot use \"%s\" in this exercise (used: %d)" -msgstr "Interdit dans cet exercice" +msgstr "Vous ne pouvez pas utiliser «%s» dans cet exercice (utilisé : %d)" msgid "You found a usable object" msgstr "Vous avez trouvé un objet utilisable" -#, fuzzy, c-format +#, 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] "Interdit dans cet exercice" -msgstr[1] "Interdit dans cet exercice" +msgstr[0] "Vous devez utiliser «%1$s» au moins une fois dans cet exercice (utilisé %2$d fois)" +msgstr[1] "Vous devez utiliser «%1$s» au moins %3$d fois dans cet exercice (utilisé %2$d fois)" -#, fuzzy, c-format +#, 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] "Interdit dans cet exercice" -msgstr[1] "Interdit dans cet exercice" +msgstr[0] "Vous devez utiliser «%1$s» au plus une fois dans cet exercice (utilisé %2$d fois)" +msgstr[1] "Vous devez utiliser «%1$s» au plus %3$d fois dans cet exercice (utilisé %2$d fois)" msgid "You must get on the spaceship to take off" msgstr "Vous devez embarquer pour pouvoir décoller" @@ -1864,9 +1902,8 @@ msgstr "epsitec.com" #~ msgid "Developed by :" #~ msgstr "Développé par :" -#, fuzzy #~ msgid "Do you want to quit Colobot: Gold Edition?" -#~ msgstr "Voulez-vous quitter COLOBOT ?" +#~ msgstr "Voulez-vous quitter Colobot: Édition Gold ?" #~ msgid "Exit film\\Film at the exit of exercises" #~ msgstr "Retour animé\\Retour animé dans les exercices" diff --git a/po/pl.po b/po/pl.po index d9cde920..2995e516 100644 --- a/po/pl.po +++ b/po/pl.po @@ -27,6 +27,10 @@ msgstr "Oczekiwane \" [ \"" msgid "\" ] \" missing" msgstr "Brak \" ] \"" +#, c-format +msgid "%s: %d pts" +msgstr "%s: %d pkt" + msgid "..behind" msgstr "..za" @@ -48,6 +52,18 @@ msgstr "<< Wstecz \\Wraca do poprzedniego ekranu" msgid "<<< Sorry; mission failed >>>" msgstr "<<< Niestety, misja nie powiodła się >>>" +#, c-format +msgid "<<< Team %s finished! >>>" +msgstr "<<< Drużyna %s zakończyła rozgrywkę! >>>" + +#, c-format +msgid "<<< Team %s lost! >>>" +msgstr "<<< Drużyna %s odpadła! >>>" + +#, c-format +msgid "<<< Team %s recieved %d points >>>" +msgstr "<<< Drużyna %s zdobyła %d punktów >>>" + msgid "<<< Well done; mission accomplished >>>" msgstr "<<< Dobra robota, misja wypełniona >>>" @@ -348,6 +364,9 @@ msgstr "Konsola komend\\Pokaż konsolę komend" msgid "Checkpoint" msgstr "Punkt kontrolny" +msgid "Class name expected" +msgstr "" + msgid "Climb\\Increases the power of the jet" msgstr "W górę\\Zwiększa moc silnika" @@ -363,6 +382,9 @@ msgstr "Zamknij" msgid "Closing bracket missing" msgstr "Brak nawiasu zamykającego" +msgid "Code battle" +msgstr "Programobitwa" + msgid "Code battles" msgstr "Programobitwy" @@ -605,6 +627,9 @@ msgstr "Funkcja już istnieje" msgid "Function name missing" msgstr "Brakująca nazwa funkcji" +msgid "Function needs return type \"void\"" +msgstr "" + msgid "Game speed" msgstr "Prędkość gry" @@ -920,6 +945,9 @@ msgstr "Brak uranu do przetworzenia" msgid "No userlevels installed!" msgstr "Brak zainstalowanych poziomów użytkownika!" +msgid "Non-void function needs \"return;\"" +msgstr "" + msgid "Normal size" msgstr "Normalna wielkość" @@ -1229,6 +1257,9 @@ msgstr "Przywracanie stanu CBot" msgid "Restoring saved objects" msgstr "Przywracanie obiektów" +msgid "Results" +msgstr "Wyniki" + msgid "Return to start" msgstr "Powrót do początku" @@ -1451,6 +1482,9 @@ msgstr "Filtrowanie tekstur\\Filtrowanie tekstur" msgid "Textures" msgstr "Tekstury" +msgid "The battle has ended" +msgstr "Bitwa zakończyła się" + msgid "The expression must return a boolean value" msgstr "Wyrażenie musi zwrócić wartość logiczną" @@ -1481,9 +1515,15 @@ msgstr "Taka etykieta nie istnieje" msgid "This menu is for userlevels from mods, but you didn't install any" msgstr "To menu jest przeznaczone na poziomy użytkownika z modyfikacji, ale żadne nie są zainstalowane" +msgid "This object is currently busy" +msgstr "Ten objekt jest obecnie zajęty" + msgid "This object is not a member of a class" msgstr "Ten obiekt nie jest członkiem klasy" +msgid "This parameter needs a default value" +msgstr "" + msgid "This program is read-only, clone it to edit" msgstr "Ten program jest tylko do odczytu, skopiuj go, aby edytować" diff --git a/po/ru.po b/po/ru.po index a6c1a61f..b460b77a 100644 --- a/po/ru.po +++ b/po/ru.po @@ -27,6 +27,10 @@ msgstr "Ожидалось \" [ \"" msgid "\" ] \" missing" msgstr "Отсутствует \"]\" " +#, c-format +msgid "%s: %d pts" +msgstr "" + msgid "..behind" msgstr "Сзади" @@ -48,6 +52,18 @@ msgstr "<< Назад \\Вернуться на предыдущую стран msgid "<<< Sorry; mission failed >>>" msgstr "<<< Миссия провалена >>>" +#, c-format +msgid "<<< Team %s finished! >>>" +msgstr "" + +#, c-format +msgid "<<< Team %s lost! >>>" +msgstr "" + +#, c-format +msgid "<<< Team %s recieved %d points >>>" +msgstr "" + msgid "<<< Well done; mission accomplished >>>" msgstr "<<< Отлично, миссия выполнена >>>" @@ -85,7 +101,7 @@ msgid "Alternative camera mode\\Move sideways instead of rotating (in free camer msgstr "Альтернативный режим камеры\\Движение в стороны вместо поворачивания (в режиме свободной камеры)" msgid "Ambiguous call to overloaded function" -msgstr "" +msgstr "Странный вызов перегруженной функции" msgid "Analysis already performed" msgstr "Анализ уже выполнен" @@ -353,6 +369,9 @@ msgstr "Консоль чит-кодов\\Показать консоль для msgid "Checkpoint" msgstr "Контрольная точка" +msgid "Class name expected" +msgstr "" + msgid "Climb\\Increases the power of the jet" msgstr "Взлет и подъем\\Увеличивает мощность реактивного двигателя" @@ -369,11 +388,15 @@ msgstr "Закрыть" msgid "Closing bracket missing" msgstr "Закрывающая скобка отсутствует" +#, fuzzy +msgid "Code battle" +msgstr "Битвы роботов" + msgid "Code battles" -msgstr "" +msgstr "Битвы роботов" msgid "Code battles\\Program your robot to be the best of them all!" -msgstr "Code battles\\Запрограммируйте собственного робота чтобы быть лучшим среди них!" +msgstr "Битвы роботов\\Запрограммируйте собственного робота чтобы быть лучшим среди них!" msgid "Colobot rules!" msgstr "Правила игры!" @@ -612,6 +635,9 @@ msgstr "Функция уже существует" msgid "Function name missing" msgstr "Имя функции отсутствует" +msgid "Function needs return type \"void\"" +msgstr "" + msgid "Game speed" msgstr "Скорость игры" @@ -817,7 +843,7 @@ msgid "Lunar Roving Vehicle" msgstr "Луноход" msgid "MSAA\\Multisample anti-aliasing" -msgstr "" +msgstr "MSAA\\Улучшенная фильтрация" msgid "Maximize" msgstr "Развернуть" @@ -826,7 +852,7 @@ msgid "Minimize" msgstr "Свернуть" msgid "Mipmap level\\Mipmap level" -msgstr "" +msgstr "Уровень уменьшающей фильтрации\\Уровень уменьшающей фильтрации" msgid "Mission name" msgstr "Название миссии" @@ -929,6 +955,9 @@ msgstr "Нет урана для преобразования" msgid "No userlevels installed!" msgstr "Не установленны пользовательские уровни!" +msgid "Non-void function needs \"return;\"" +msgstr "" + msgid "Normal size" msgstr "Нормальный размер" @@ -1230,17 +1259,20 @@ msgid "Resolution:" msgstr "Разрешение:" msgid "Resources" -msgstr "" +msgstr "Ресурсы" msgid "Restart\\Restart the mission from the beginning" msgstr "Заново\\Начать данную миссию с начала" msgid "Restoring CBot execution state" -msgstr "" +msgstr "Восстановление состояния CBot" msgid "Restoring saved objects" msgstr "Восстановить сохранённые объекты" +msgid "Results" +msgstr "" + msgid "Return to start" msgstr "Вернуться в начало" @@ -1465,6 +1497,9 @@ msgstr "Фильтрация текстур\\Фильтрация текстур msgid "Textures" msgstr "Текстуры" +msgid "The battle has ended" +msgstr "" + msgid "The expression must return a boolean value" msgstr "Выражение должно возвращать логическое значение" @@ -1495,9 +1530,15 @@ msgstr "Эта метка не существует" msgid "This menu is for userlevels from mods, but you didn't install any" msgstr "Это меню для пользовательских уровней из модов, но вы ни одного не уставили" +msgid "This object is currently busy" +msgstr "" + msgid "This object is not a member of a class" msgstr "Этот объект не член класса" +msgid "This parameter needs a default value" +msgstr "" + msgid "This program is read-only, clone it to edit" msgstr "Эта программа только для чтения, для редактирования клонируйте её" diff --git a/src/CBot/CBotCStack.cpp b/src/CBot/CBotCStack.cpp index c8c01346..a2f191d7 100644 --- a/src/CBot/CBotCStack.cpp +++ b/src/CBot/CBotCStack.cpp @@ -350,7 +350,7 @@ CBotTypResult CBotCStack::CompileCall(CBotToken* &p, CBotVar** ppVars, long& nId val = m_prog->GetExternalCalls()->CompileCall(p, nullptr, ppVars, this); if (val.GetType() < 0) { - val = m_prog->GetFunctions()->CompileCall(p->GetString(), ppVars, nIdent); + val = CBotFunction::CompileCall(m_prog->GetFunctions(), p->GetString(), ppVars, nIdent); if ( val.GetType() < 0 ) { // pVar = nullptr; // the error is not on a particular parameter @@ -369,8 +369,7 @@ bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam) if ( m_prog->GetExternalCalls()->CheckCall(name) ) return true; - CBotFunction* pp = m_prog->GetFunctions(); - while ( pp != nullptr ) + for (CBotFunction* pp : m_prog->GetFunctions()) { if ( pToken->GetString() == pp->GetName() ) { @@ -378,7 +377,6 @@ bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam) if ( pp->CheckParam( pParam ) ) return true; } - pp = pp->Next(); } for (CBotFunction* pp : CBotFunction::m_publicFunctions) diff --git a/src/CBot/CBotCallMethode.cpp b/src/CBot/CBotCallMethode.cpp deleted file mode 100644 index 582ce7b5..00000000 --- a/src/CBot/CBotCallMethode.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsitec.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - -#include "CBot/CBotCallMethode.h" - -#include "CBot/CBotUtils.h" -#include "CBot/CBotStack.h" -#include "CBot/CBotCStack.h" - -#include "CBot/CBotVar/CBotVar.h" - -namespace CBot -{ - - -//////////////////////////////////////////////////////////////////////////////// -CBotCallMethode::CBotCallMethode(const std::string& name, - bool rExec(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception, void* user), - CBotTypResult rCompile(CBotVar* pThis, CBotVar*& pVar)) -{ - m_name = name; - m_rExec = rExec; - m_rComp = rCompile; -} - -//////////////////////////////////////////////////////////////////////////////// -CBotCallMethode::~CBotCallMethode() -{ -} - -//////////////////////////////////////////////////////////////////////////////// -CBotTypResult CBotCallMethode::CompileCall(const std::string& name, CBotVar* pThis, CBotVar** ppVar, - CBotCStack* pStack) -{ - CBotCallMethode* pt = this; - - while ( pt != nullptr ) - { - if ( pt->m_name == name ) - { - CBotVar* pVar = MakeListVars(ppVar, true); - CBotVar* pVar2 = pVar; - CBotTypResult r = pt->m_rComp(pThis, pVar2); - int ret = r.GetType(); - if ( ret > 20 ) - { - if (pVar2) pStack->SetError(static_cast(ret), pVar2->GetToken()); - } - delete pVar; - return r; - } - pt = pt->m_next; - } - return CBotTypResult(-1); -} - -//////////////////////////////////////////////////////////////////////////////// -int CBotCallMethode::DoCall(const std::string& name, CBotVar* pThis, CBotVar** ppVars, CBotVar*& pResult, - CBotStack* pStack, CBotToken* pToken) -{ - CBotCallMethode* pt = this; - - // search by name - - while ( pt != nullptr ) - { - if ( pt->m_name == name ) - { - // lists the parameters depending on the contents of the stack (pStackVar) - - CBotVar* pVar = MakeListVars(ppVars, true); - CBotVar* pVarToDelete = pVar; - - int Exception = 0; // TODO: Change this to CBotError - int res = pt->m_rExec(pThis, pVar, pResult, Exception, pStack->GetUserPtr()); - pStack->SetVar(pResult); - - if (res == false) - { - if (Exception!=0) - { -// pStack->SetError(Exception, pVar->GetToken()); - pStack->SetError(static_cast(Exception), pToken); - } - delete pVarToDelete; - return false; - } - delete pVarToDelete; - return true; - } - pt = pt->m_next; - } - - return -1; -} - -} // namespace CBot diff --git a/src/CBot/CBotCallMethode.h b/src/CBot/CBotCallMethode.h deleted file mode 100644 index 4d0e1678..00000000 --- a/src/CBot/CBotCallMethode.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsitec.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - -#pragma once - -#include "CBot/CBotTypResult.h" -#include "CBot/CBotUtils.h" - -namespace CBot -{ - -class CBotVar; -class CBotCStack; -class CBotStack; -class CBotToken; - -/*! - * \brief The CBotCallMethode class Class managing the methods declared by - * AddFunction on a class. - */ -class CBotCallMethode : public CBotLinkedList -{ -public: - - /*! - * \brief CBotCallMethode - * \param name - * \param rExec - * \param rCompile - */ - CBotCallMethode(const std::string& name, - bool rExec(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception, void* user), - CBotTypResult rCompile(CBotVar* pThis, CBotVar*& pVar)); - - /*! - * \brief ~CBotCallMethode - */ - ~CBotCallMethode(); - - /*! - * \brief CompileCall Is acceptable by a call procedure name and given - * parameters. - * \param name - * \param pThis - * \param ppVars - * \param pStack - * \return - */ - CBotTypResult CompileCall(const std::string& name, CBotVar* pThis, CBotVar** ppVars, - CBotCStack* pStack); - - /*! - * \brief DoCall - * \param name - * \param pThis - * \param ppVars - * \param pResult - * \param pStack - * \param pFunc - * \return - */ - int DoCall(const std::string& name, CBotVar* pThis, CBotVar** ppVars, CBotVar*& pResult, - CBotStack* pStack, CBotToken* pFunc); - -private: - std::string m_name; - bool (*m_rExec) (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception, void* user); - CBotTypResult (*m_rComp) (CBotVar* pThis, CBotVar* &pVar); - friend class CBotClass; -}; - -} // namespace CBot diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index 8efc4af8..ef220943 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -37,7 +37,6 @@ #include "CBot/CBotDefParam.h" #include "CBot/CBotUtils.h" #include "CBot/CBotFileUtils.h" -#include "CBot/CBotCallMethode.h" #include @@ -55,8 +54,7 @@ CBotClass::CBotClass(const std::string& name, m_parent = parent; m_name = name; m_pVar = nullptr; - m_pCalls = nullptr; - m_pMethod = nullptr; + m_externalMethods = new CBotExternalCallList(); m_rUpdate = nullptr; m_IsDef = true; m_bIntrinsic= bIntrinsic; @@ -71,8 +69,7 @@ CBotClass::~CBotClass() m_publicClasses.erase(this); delete m_pVar; - delete m_pCalls; - delete m_pMethod; + delete m_externalMethods; } //////////////////////////////////////////////////////////////////////////////// @@ -86,26 +83,24 @@ CBotClass* CBotClass::Create(const std::string& name, //////////////////////////////////////////////////////////////////////////////// void CBotClass::ClearPublic() { - m_publicClasses.clear(); + while ( !m_publicClasses.empty() ) + { + auto it = m_publicClasses.begin(); + delete *it; // calling destructor removes the class from the list + } } //////////////////////////////////////////////////////////////////////////////// void CBotClass::Purge() { - assert ( this != nullptr ); - delete m_pVar; m_pVar = nullptr; - delete m_pCalls; - m_pCalls = nullptr; - delete m_pMethod; - m_pMethod = nullptr; + m_externalMethods->Clear(); + for (CBotFunction* f : m_pMethod) delete f; + m_pMethod.clear(); m_IsDef = false; m_nbVar = m_parent == nullptr ? 0 : m_parent->m_nbVar; - - if (m_next != nullptr) m_next->Purge(); - m_next = nullptr; // no longer belongs to this chain } //////////////////////////////////////////////////////////////////////////////// @@ -205,7 +200,6 @@ std::string CBotClass::GetName() //////////////////////////////////////////////////////////////////////////////// CBotClass* CBotClass::GetParent() { - assert ( this != nullptr ); return m_parent; } @@ -255,6 +249,19 @@ CBotVar* CBotClass::GetItemRef(int nIdent) return nullptr; } +//////////////////////////////////////////////////////////////////////////////// +bool CBotClass::CheckVar(const std::string &name) +{ + CBotVar* p = m_pVar; + + while ( p != nullptr ) + { + if ( p->GetName() == name ) return true; + p = p->GetNext(); + } + return false; +} + //////////////////////////////////////////////////////////////////////////////// bool CBotClass::IsIntrinsic() { @@ -283,29 +290,7 @@ bool CBotClass::AddFunction(const std::string& name, bool rExec(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception, void* user), CBotTypResult rCompile(CBotVar* pThis, CBotVar*& pVar)) { - // stores pointers to the two functions - CBotCallMethode* p = m_pCalls; - CBotCallMethode* pp = nullptr; - - while ( p != nullptr ) - { - if ( name == p->m_name ) - { - if ( pp == nullptr ) m_pCalls = p->m_next; - else pp->m_next = p->m_next; - delete p; - break; - } - pp = p; - p = p->m_next; - } - - p = new CBotCallMethode(name, rExec, rCompile); - - if (m_pCalls == nullptr) m_pCalls = p; - else m_pCalls->AddNext(p); // added to the list - - return true; + return m_externalMethods->AddFunction(name, std::unique_ptr(new CBotExternalCallClass(rExec, rCompile))); } //////////////////////////////////////////////////////////////////////////////// @@ -316,22 +301,22 @@ bool CBotClass::SetUpdateFunc(void rUpdate(CBotVar* thisVar, void* user)) } //////////////////////////////////////////////////////////////////////////////// -CBotTypResult CBotClass::CompileMethode(const std::string& name, +CBotTypResult CBotClass::CompileMethode(CBotToken* name, CBotVar* pThis, CBotVar** ppParams, CBotCStack* pStack, - long& nIdent) + long &nIdent) { nIdent = 0; // forget the previous one if necessary // find the methods declared by AddFunction - CBotTypResult r = m_pCalls->CompileCall(name, pThis, ppParams, pStack); + CBotTypResult r = m_externalMethods->CompileCall(name, pThis, ppParams, pStack); if ( r.GetType() >= 0) return r; // find the methods declared by user - r = m_pMethod->CompileCall(name, ppParams, nIdent); + r = CBotFunction::CompileCall(m_pMethod, name->GetString(), ppParams, nIdent); if ( r.Eq(CBotErrUndefCall) && m_parent != nullptr ) return m_parent->CompileMethode(name, pThis, ppParams, pStack, nIdent); return r; @@ -339,37 +324,39 @@ CBotTypResult CBotClass::CompileMethode(const std::string& name, //////////////////////////////////////////////////////////////////////////////// bool CBotClass::ExecuteMethode(long& nIdent, - const std::string& name, CBotVar* pThis, CBotVar** ppParams, - CBotVar*& pResult, + CBotTypResult pResultType, CBotStack*& pStack, CBotToken* pToken) { - int ret = m_pCalls->DoCall(name, pThis, ppParams, pResult, pStack, pToken); - if (ret>=0) return ret; + int ret = m_externalMethods->DoCall(pToken, pThis, ppParams, pStack, pResultType); + if (ret >= 0) return ret; - ret = m_pMethod->DoCall(nIdent, name, pThis, ppParams, pStack, pToken, this); + ret = CBotFunction::DoCall(m_pMethod, nIdent, pToken->GetString(), pThis, ppParams, pStack, pToken, this); if (ret >= 0) return ret; if (m_parent != nullptr) { - ret = m_parent->ExecuteMethode(nIdent, name, pThis, ppParams, pResult, pStack, pToken); + ret = m_parent->ExecuteMethode(nIdent, pThis, ppParams, pResultType, pStack, pToken); } return ret; } //////////////////////////////////////////////////////////////////////////////// void CBotClass::RestoreMethode(long& nIdent, - const std::string& name, + CBotToken* name, CBotVar* pThis, CBotVar** ppParams, CBotStack*& pStack) { + if (m_externalMethods->RestoreCall(name, pThis, ppParams, pStack)) + return; + CBotClass* pClass = this; while (pClass != nullptr) { - bool ok = pClass->m_pMethod->RestoreCall(nIdent, name, pThis, ppParams, pStack, pClass); + bool ok = CBotFunction::RestoreCall(pClass->m_pMethod, nIdent, name->GetString(), pThis, ppParams, pStack, pClass); if (ok) return; pClass = pClass->m_parent; } @@ -455,8 +442,7 @@ bool CBotClass::CheckCall(CBotProgram* program, CBotDefParam* pParam, CBotToken* if ( program->GetExternalCalls()->CheckCall(name) ) return true; - CBotFunction* pp = m_pMethod; - while ( pp != nullptr ) + for (CBotFunction* pp : m_pMethod) { if ( pToken->GetString() == pp->GetName() ) { @@ -464,7 +450,6 @@ bool CBotClass::CheckCall(CBotProgram* program, CBotDefParam* pParam, CBotToken* if ( pp->CheckParam( pParam ) ) return true; } - pp = pp->Next(); } return false; @@ -483,30 +468,32 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack) std::string name = p->GetString(); - CBotClass* pOld = CBotClass::Find(name); - if ( pOld != nullptr && pOld->m_IsDef ) - { - pStack->SetError( CBotErrRedefClass, p ); - return nullptr; - } - // a name of the class is there? if (IsOfType(p, TokenTypVar)) { + CBotClass* pOld = CBotClass::Find(name); + if ((pOld != nullptr && pOld->m_IsDef) || /* public class exists in different program */ + pStack->GetProgram()->ClassExists(name)) /* class exists in this program */ + { + pStack->SetError(CBotErrRedefClass, p->GetPrev()); + return nullptr; + } + CBotClass* pPapa = nullptr; if ( IsOfType( p, ID_EXTENDS ) ) { std::string name = p->GetString(); pPapa = CBotClass::Find(name); + CBotToken* pp = p; if (!IsOfType(p, TokenTypVar) || pPapa == nullptr ) { - pStack->SetError( CBotErrNotClass, p ); + pStack->SetError(CBotErrNoClassName, pp); return nullptr; } } CBotClass* classe = (pOld == nullptr) ? new CBotClass(name, pPapa) : pOld; - classe->Purge(); // empty the old definitions // TODO: Doesn't this remove all classes of the current program? + classe->Purge(); // empty the old definitions classe->m_IsDef = false; // current definition classe->m_pOpenblk = p; @@ -518,27 +505,29 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack) } int level = 1; - do // skip over the definition + while (level > 0 && p != nullptr) { int type = p->GetType(); p = p->GetNext(); if (type == ID_OPBLK) level++; if (type == ID_CLBLK) level--; } - while (level > 0 && p != nullptr); if (level > 0) pStack->SetError(CBotErrCloseBlock, classe->m_pOpenblk); if (pStack->IsOk()) return classe; } + else + pStack->SetError(CBotErrNoClassName, p); + pStack->SetError(CBotErrNoTerminator, p); return nullptr; } //////////////////////////////////////////////////////////////////////////////// -void CBotClass::DefineClasses(CBotClass* pClass, CBotCStack* pStack) +void CBotClass::DefineClasses(std::list pClassList, CBotCStack* pStack) { - while (pClass != nullptr) + for (CBotClass* pClass : pClassList) { CBotClass* pParent = pClass->m_parent; pClass->m_nbVar = (pParent == nullptr) ? 0 : pParent->m_nbVar; @@ -550,8 +539,6 @@ void CBotClass::DefineClasses(CBotClass* pClass, CBotCStack* pStack) } if (!pStack->IsOk()) return; - - pClass = pClass->GetNext(); } } @@ -587,6 +574,8 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) while (pStack->IsOk()) { CBotTypResult type2 = CBotTypResult(type); // reset type after comma + CBotToken* varToken = p; + std::string pp = p->GetString(); if ( IsOfType(p, ID_NOT) ) { @@ -595,60 +584,30 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) if (IsOfType(p, TokenTypVar)) { - CBotInstr* limites = nullptr; - while ( IsOfType( p, ID_OPBRK ) ) // a table? - { - CBotInstr* i = nullptr; - pStack->SetStartError( p->GetStart() ); - if ( p->GetType() != ID_CLBRK ) - { - i = CBotExpression::Compile( p, pStack ); // expression for the value - if (i == nullptr || pStack->GetType() != CBotTypInt) // must be a number - { - pStack->SetError(CBotErrBadIndex, p->GetStart()); - return false; - } - } - else - i = new CBotEmpty(); // special if not a formula - - type2 = CBotTypResult(CBotTypArrayPointer, type2); - - if (limites == nullptr) limites = i; - else limites->AddNext3(i); - - if (IsOfType(p, ID_CLBRK)) continue; - pStack->SetError(CBotErrCloseIndex, p->GetStart()); - return false; - } - if ( p->GetType() == ID_OPENPAR ) { if ( !bSecond ) { p = pBase; - CBotFunction* f = - CBotFunction::Compile1(p, pStack, this); + CBotFunction* f = CBotFunction::Compile1(p, pStack, this); if ( f == nullptr ) return false; - if (m_pMethod == nullptr) m_pMethod = f; - else m_pMethod->AddNext(f); + m_pMethod.push_back(f); } else { // return a method precompiled in pass 1 - CBotFunction* pf = m_pMethod; - CBotToken* ppp = p; CBotCStack* pStk = pStack->TokenStack(nullptr, true); CBotDefParam* params = CBotDefParam::Compile(p, pStk ); delete pStk; - p = ppp; - while ( pf != nullptr ) // search by name and parameters + std::list::iterator pfIter = std::find_if(m_pMethod.begin(), m_pMethod.end(), [&pp, ¶ms](CBotFunction* x) { - if (pf->GetName() == pp && pf->CheckParam( params )) break; - pf = pf->Next(); - } + return x->GetName() == pp && x->CheckParam( params ); + }); + assert(pfIter != m_pMethod.end()); + CBotFunction* pf = *pfIter; + delete params; bool bConstructor = (pp == GetName()); CBotCStack* pile = pStack->TokenStack(nullptr, true); @@ -706,12 +665,51 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) } // definition of an element - if (type2.Eq(0)) + if (type.Eq(0)) { pStack->SetError(CBotErrNoTerminator, p); return false; } + if (pp[0] == '~' || pp == GetName()) // bad variable name + { + pStack->SetError(CBotErrNoVar, varToken); + return false; + } + + if (!bSecond && CheckVar(pp)) // variable already exists + { + pStack->SetError(CBotErrRedefVar, varToken); + return false; + } + + CBotInstr* limites = nullptr; + while ( IsOfType( p, ID_OPBRK ) ) // an array + { + CBotInstr* i = nullptr; + pStack->SetStartError( p->GetStart() ); + if ( p->GetType() != ID_CLBRK ) + { + i = CBotExpression::Compile( p, pStack ); // expression for the value + if (i == nullptr || pStack->GetType() != CBotTypInt) // must be a number + { + pStack->SetError(CBotErrBadIndex, p->GetStart()); + } + } + else + i = new CBotEmpty(); // special if not a formula + + type2 = CBotTypResult(CBotTypArrayPointer, type2); + + if (limites == nullptr) limites = i; + else limites->AddNext3(i); + + if (pStack->IsOk() && IsOfType(p, ID_CLBRK)) continue; + pStack->SetError(CBotErrCloseIndex, p->GetStart()); + delete limites; + return false; + } + CBotInstr* i = nullptr; if ( IsOfType(p, ID_ASS ) ) { @@ -781,7 +779,10 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) } } else + { delete i; + delete limites; + } if ( IsOfType(p, ID_COMMA) ) continue; if ( IsOfType(p, ID_SEP) ) break; @@ -810,10 +811,11 @@ CBotClass* CBotClass::Compile(CBotToken* &p, CBotCStack* pStack) // TODO: Not sure how correct is that - I have no idea how the precompilation (Compile1 method) works ~krzys_h std::string name = p->GetString(); CBotClass* pPapa = CBotClass::Find(name); + CBotToken* pp = p; if (!IsOfType(p, TokenTypVar) || pPapa == nullptr) { - pStack->SetError( CBotErrNotClass, p ); + pStack->SetError(CBotErrNoClassName, pp); return nullptr; } pOld->m_parent = pPapa; diff --git a/src/CBot/CBotClass.h b/src/CBot/CBotClass.h index 2fc494dc..503c62db 100644 --- a/src/CBot/CBotClass.h +++ b/src/CBot/CBotClass.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace CBot { @@ -37,6 +38,7 @@ class CBotStack; class CBotDefParam; class CBotToken; class CBotCStack; +class CBotExternalCallList; /** * \brief A CBot class definition @@ -102,7 +104,7 @@ class CBotCStack; * float y = var->GetValFloat(); * \endcode */ -class CBotClass : public CBotLinkedList +class CBotClass { public: /*! @@ -133,13 +135,8 @@ public: bool intrinsic = false); /*! - * \brief AddFunction This call allows to add as external new method - * used by the objects of this class. See (**) at end of this file for - * more details. - * \param name - * \param rExec - * \param rCompile - * \return + * \brief Add a function that can be called from CBot + * \see CBotProgram::AddFunction */ bool AddFunction(const std::string& name, bool rExec(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception, void* user), @@ -224,6 +221,13 @@ public: */ CBotVar* GetItemRef(int nIdent); + /*! + * \brief Check whether a variable is already defined in a class + * \param name Name of the variable + * \return True if a variable is defined in the class + */ + bool CheckVar(const std::string &name); + /*! * \brief CompileMethode Compiles a method associated with an instance of * class the method can be declared by the user or AddFunction. @@ -234,11 +238,11 @@ public: * \param nIdent * \return */ - CBotTypResult CompileMethode(const std::string& name, + CBotTypResult CompileMethode(CBotToken* name, CBotVar* pThis, CBotVar** ppParams, CBotCStack* pStack, - long& nIdent); + long &nIdent); /*! * \brief ExecuteMethode Executes a method. @@ -246,18 +250,13 @@ public: * \param name * \param pThis * \param ppParams - * \param pResult + * \param pResultType * \param pStack * \param pToken * \return */ - bool ExecuteMethode(long& nIdent, - const std::string& name, - CBotVar* pThis, - CBotVar** ppParams, - CBotVar*& pResult, - CBotStack*& pStack, - CBotToken* pToken); + bool ExecuteMethode(long &nIdent, CBotVar* pThis, CBotVar** ppParams, CBotTypResult pResultType, + CBotStack*&pStack, CBotToken* pToken); /*! * \brief RestoreMethode Restored the execution stack. @@ -267,11 +266,11 @@ public: * \param ppParams * \param pStack */ - void RestoreMethode(long& nIdent, - const std::string& name, + void RestoreMethode(long &nIdent, + CBotToken* name, CBotVar* pThis, CBotVar** ppParams, - CBotStack*& pStack); + CBotStack*&pStack); /*! * \brief Compile Compiles a class declared by the user. @@ -283,10 +282,15 @@ public: CBotCStack* pStack); /*! - * \brief Compile1 - * \param p - * \param pStack - * \return + * \brief Pre-compile a new class + * \param p[in, out] Pointer to first token of the class, will be updated to point to first token after the class definition + * \param pStack Compile stack + * + * This function is used to find the beginning and end of class definition. + * + * If any errors in the code are detected, this function will set the error on compile stack and return nullptr. + * + * \return Precompiled class, or nullptr in case of error */ static CBotClass* Compile1(CBotToken* &p, CBotCStack* pStack); @@ -294,10 +298,10 @@ public: /*! * \brief DefineClasses Calls CompileDefItem for each class in a list * of classes, defining fields and pre-compiling methods. - * \param pClass List of classes + * \param pClassList List of classes * \param pStack */ - static void DefineClasses(CBotClass* pClass, CBotCStack* pStack); + static void DefineClasses(std::list pClassList, CBotCStack* pStack); /*! * \brief CompileDefItem @@ -388,9 +392,9 @@ private: //! Linked list of all class fields CBotVar* m_pVar; //! Linked list of all class external calls - CBotCallMethode* m_pCalls; - //! Linked list of all class methods - CBotFunction* m_pMethod; + CBotExternalCallList* m_externalMethods; + //! List of all class methods + std::list m_pMethod{}; void (*m_rUpdate)(CBotVar* thisVar, void* user); CBotToken* m_pOpenblk; diff --git a/src/CBot/CBotDebug.cpp b/src/CBot/CBotDebug.cpp index 652bd704..c50bb302 100644 --- a/src/CBot/CBotDebug.cpp +++ b/src/CBot/CBotDebug.cpp @@ -36,12 +36,10 @@ void CBotDebug::DumpCompiledProgram(CBotProgram* program) std::stringstream ss; ss << "digraph {" << std::endl; - CBotFunction* func = program->GetFunctions(); std::map funcIdMap; - while (func != nullptr) + for (CBotFunction* func : program->GetFunctions()) { funcIdMap[func->m_nFuncIdent] = func; - func = func->Next(); } std::set finished; @@ -111,9 +109,8 @@ void CBotDebug::DumpCompiledProgram(CBotProgram* program) { DumpInstr(program->m_entryPoint); } - func = program->GetFunctions(); std::string prev = GetPointerAsString(program->m_entryPoint); - while (func != nullptr) + for (CBotFunction* func : program->GetFunctions()) { if (func != program->m_entryPoint) { @@ -122,8 +119,6 @@ void CBotDebug::DumpCompiledProgram(CBotProgram* program) //ss << prev << " -> " << GetPointerAsString(func) << " [style=invis]" << std::endl; prev = GetPointerAsString(func); } - - func = func->Next(); } ss << "}" << std::endl; diff --git a/src/CBot/CBotDefParam.cpp b/src/CBot/CBotDefParam.cpp index d73c02e0..b99503f7 100644 --- a/src/CBot/CBotDefParam.cpp +++ b/src/CBot/CBotDefParam.cpp @@ -19,6 +19,9 @@ #include "CBot/CBotDefParam.h" +#include "CBot/CBotInstr/CBotInstrUtils.h" +#include "CBot/CBotInstr/CBotParExpr.h" + #include "CBot/CBotUtils.h" #include "CBot/CBotCStack.h" @@ -33,11 +36,13 @@ namespace CBot CBotDefParam::CBotDefParam() { m_nIdent = 0; + m_expr = nullptr; } //////////////////////////////////////////////////////////////////////////////// CBotDefParam::~CBotDefParam() { + delete m_expr; } //////////////////////////////////////////////////////////////////////////////// @@ -51,8 +56,9 @@ CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack) if (IsOfType(p, ID_OPENPAR)) { CBotDefParam* list = nullptr; + bool prevHasDefault = false; - while (!IsOfType(p, ID_CLOSEPAR)) + if (!IsOfType(p, ID_CLOSEPAR)) while (true) { CBotDefParam* param = new CBotDefParam(); if (list == nullptr) list = param; @@ -77,6 +83,26 @@ CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack) break; } + if (IsOfType(p, ID_ASS)) // default value assignment + { + CBotCStack* pStk = pStack->TokenStack(nullptr, true); + if (nullptr != (param->m_expr = CBotParExpr::CompileLitExpr(p, pStk))) + { + CBotTypResult valueType = pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + + if (!TypesCompatibles(type, valueType)) + pStack->SetError(CBotErrBadType1, p->GetPrev()); + + prevHasDefault = true; + } + else pStack->SetError(CBotErrNoExpression, p); + delete pStk; + } + else + if (prevHasDefault) pStack->SetError(CBotErrDefaultValue, p->GetPrev()); + + if (!pStack->IsOk()) break; + if ( type.Eq(CBotTypArrayPointer) ) type.SetType(CBotTypArrayBody); CBotVar* var = CBotVar::Create(pp->GetString(), type); // creates the variable // if ( pClass ) var->SetClass(pClass); @@ -85,10 +111,12 @@ CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack) var->SetUniqNum(param->m_nIdent); pStack->AddVar(var); // place on the stack - if (IsOfType(p, ID_COMMA) || p->GetType() == ID_CLOSEPAR) - continue; + if (IsOfType(p, ID_COMMA)) continue; + if (IsOfType(p, ID_CLOSEPAR)) break; + + pStack->SetError(CBotErrClosePar, p->GetStart()); } - pStack->SetError(CBotErrClosePar, p->GetStart()); + pStack->SetError(CBotErrNoVar, p->GetStart()); } pStack->SetError(CBotErrNoType, p); delete list; @@ -106,40 +134,71 @@ bool CBotDefParam::Execute(CBotVar** ppVars, CBotStack* &pj) int i = 0; CBotDefParam* p = this; + bool useDefault = false; + CBotStack* pile = pj->AddStack(); + while ( p != nullptr ) { + pile = pile->AddStack(); + if (pile->GetState() == 1) // already done? + { + if (ppVars != nullptr && ppVars[i] != nullptr) ++i; + p = p->m_next; + continue; // next param + } + + CBotVar* pVar = nullptr; + + if (useDefault || (ppVars == nullptr || ppVars[i] == nullptr)) + { + assert(p->m_expr != nullptr); + + useDefault = true; + + if (!p->m_expr->Execute(pile)) return false; // interupt here + + pVar = pile->GetVar(); + } + else + pVar = ppVars[i]; + + pile->SetState(1); // mark this param done + // creates a local variable on the stack CBotVar* newvar = CBotVar::Create(p->m_token.GetString(), p->m_type); // serves to make the transformation of types: - if ( ppVars != nullptr && ppVars[i] != nullptr ) + if ((useDefault && pVar != nullptr) || + (ppVars != nullptr && pVar != nullptr)) { switch (p->m_type.GetType()) { case CBotTypInt: - newvar->SetValInt(ppVars[i]->GetValInt()); + newvar->SetValInt(pVar->GetValInt()); + newvar->SetInit(pVar->GetInit()); // copy nan break; case CBotTypFloat: - newvar->SetValFloat(ppVars[i]->GetValFloat()); + newvar->SetValFloat(pVar->GetValFloat()); + newvar->SetInit(pVar->GetInit()); // copy nan break; case CBotTypString: - newvar->SetValString(ppVars[i]->GetValString()); + newvar->SetValString(pVar->GetValString()); break; case CBotTypBoolean: - newvar->SetValInt(ppVars[i]->GetValInt()); + newvar->SetValInt(pVar->GetValInt()); break; case CBotTypIntrinsic: - (static_cast(newvar))->Copy(ppVars[i], false); + (static_cast(newvar))->Copy(pVar, false); break; case CBotTypPointer: { - newvar->SetPointer(ppVars[i]->GetPointer()); + newvar->SetPointer(pVar->GetPointer()); newvar->SetType(p->m_type); // keep pointer type } break; case CBotTypArrayPointer: { - newvar->SetPointer(ppVars[i]->GetPointer()); + newvar->SetPointer(pVar->GetPointer()); } break; default: @@ -149,23 +208,41 @@ bool CBotDefParam::Execute(CBotVar** ppVars, CBotStack* &pj) newvar->SetUniqNum(p->m_nIdent); pj->AddVar(newvar); // add a variable p = p->m_next; - i++; + if (!useDefault) i++; } return true; } +//////////////////////////////////////////////////////////////////////////////// +bool CBotDefParam::HasDefault() +{ + return (m_expr != nullptr); +} + //////////////////////////////////////////////////////////////////////////////// void CBotDefParam::RestoreState(CBotStack* &pj, bool bMain) { -// int i = 0; CBotDefParam* p = this; + CBotStack* pile = nullptr; + if (bMain) pile = pj->RestoreStack(); + while ( p != nullptr ) { + if (bMain && pile != nullptr) + { + pile = pile->RestoreStack(); + if (pile != nullptr && pile->GetState() == 0) + { + assert(p->m_expr != nullptr); + p->m_expr->RestoreState(pile, true); + return; + } + } // creates a local variable on the stack CBotVar* var = pj->FindVar(p->m_token.GetString()); - var->SetUniqNum(p->m_nIdent); + if (var != nullptr) var->SetUniqNum(p->m_nIdent); p = p->m_next; } } diff --git a/src/CBot/CBotDefParam.h b/src/CBot/CBotDefParam.h index c23855db..bc9d9d0a 100644 --- a/src/CBot/CBotDefParam.h +++ b/src/CBot/CBotDefParam.h @@ -63,6 +63,12 @@ public: */ bool Execute(CBotVar** ppVars, CBotStack* &pj); + /*! + * \brief Check if this parameter has a default value expression. + * \return true if the parameter was compiled with a default value. + */ + bool HasDefault(); + /*! * \brief RestoreState * \param pj @@ -96,6 +102,9 @@ private: //! Type of paramteter. CBotTypResult m_type; long m_nIdent; + + //! Default value expression for the parameter. + CBotInstr* m_expr; }; } // namespace CBot diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index da0dbea7..f98d4e96 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -237,6 +237,10 @@ enum CBotError : int CBotErrNoPublic = 5042, //!< missing word "public" CBotErrNoExpression = 5043, //!< expression expected after = CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function + CBotErrFuncNotVoid = 5045, //!< function needs return type "void" + CBotErrNoClassName = 5046, //!< class name expected + CBotErrNoReturn = 5047, //!< non-void function needs "return;" + CBotErrDefaultValue = 5048, //!< this parameter needs a default value // Runtime errors CBotErrZeroDiv = 6000, //!< division by zero diff --git a/src/CBot/CBotExternalCall.cpp b/src/CBot/CBotExternalCall.cpp index dcf7222b..be7aa676 100644 --- a/src/CBot/CBotExternalCall.cpp +++ b/src/CBot/CBotExternalCall.cpp @@ -168,4 +168,48 @@ bool CBotExternalCallDefault::Run(CBotVar* thisVar, CBotStack* pStack) return true; } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +CBotExternalCallClass::CBotExternalCallClass(RuntimeFunc rExec, CompileFunc rCompile) +{ + m_rExec = rExec; + m_rComp = rCompile; +} + +CBotExternalCallClass::~CBotExternalCallClass() +{ +} + +CBotTypResult CBotExternalCallClass::Compile(CBotVar* thisVar, CBotVar* args, void* user) +{ + return m_rComp(thisVar, args); +} + +bool CBotExternalCallClass::Run(CBotVar* thisVar, CBotStack* pStack) +{ + if (pStack->IsCallFinished()) return true; + CBotStack* pile = pStack->AddStackExternalCall(this); + CBotVar* args = pile->GetVar(); + + CBotStack* pile2 = pile->AddStack(); + + CBotVar* result = pile2->GetVar(); + + int exception = CBotNoErr; // TODO: Change to CBotError + bool res = m_rExec(thisVar, args, result, exception, pStack->GetUserPtr()); + + if (!res) + { + if (exception != CBotNoErr) + { + pStack->SetError(static_cast(exception)); + } + return false; + } + + if (result != nullptr) pStack->SetCopyVar(result); + + return true; +} + } diff --git a/src/CBot/CBotExternalCall.h b/src/CBot/CBotExternalCall.h index 5bef80ab..5432aad1 100644 --- a/src/CBot/CBotExternalCall.h +++ b/src/CBot/CBotExternalCall.h @@ -105,6 +105,36 @@ private: CompileFunc m_rComp; }; +/** + * \brief Implementation of CBot external call for class methods, using compilation and runtime functions + */ +class CBotExternalCallClass : public CBotExternalCall +{ +public: + typedef bool (*RuntimeFunc)(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception, void* user); + typedef CBotTypResult (*CompileFunc)(CBotVar* pThis, CBotVar*& pVar); + + /** + * \brief Constructor + * \param rExec Runtime function + * \param rCompile Compilation function + * \see CBotProgram::AddFunction() + */ + CBotExternalCallClass(RuntimeFunc rExec, CompileFunc rCompile); + + /** + * \brief Destructor + */ + virtual ~CBotExternalCallClass(); + + virtual CBotTypResult Compile(CBotVar* thisVar, CBotVar* args, void* user) override; + virtual bool Run(CBotVar* thisVar, CBotStack* pStack) override; + +private: + RuntimeFunc m_rExec; + CompileFunc m_rComp; +}; + /** * \brief Class for mangaging CBot external calls diff --git a/src/CBot/CBotInstr/CBotDefClass.cpp b/src/CBot/CBotInstr/CBotDefClass.cpp index cd73d9cc..bd885e10 100644 --- a/src/CBot/CBotInstr/CBotDefClass.cpp +++ b/src/CBot/CBotInstr/CBotDefClass.cpp @@ -51,6 +51,9 @@ CBotDefClass::CBotDefClass() //////////////////////////////////////////////////////////////////////////////// CBotDefClass::~CBotDefClass() { + delete m_parameters; + delete m_exprRetVar; + delete m_expr; delete m_var; } @@ -131,7 +134,7 @@ CBotInstr* CBotDefClass::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* p { // the constructor is there? // std::string noname; - CBotTypResult r = pClass->CompileMethode(pClass->GetName(), var, ppVars, pStk, inst->m_nMethodeIdent); + CBotTypResult r = pClass->CompileMethode(&token, var, ppVars, pStk, inst->m_nMethodeIdent); delete pStk->TokenStack(); // releases the supplement stack int typ = r.GetType(); @@ -246,9 +249,9 @@ bool CBotDefClass::Execute(CBotStack* &pj) if (m_exprRetVar != nullptr) // Class c().method(); { - if (pile->IfStep()) return false; if (pile->GetState() == 4) { + if (pile->IfStep()) return false; CBotStack* pile3 = pile->AddStack(); if (!m_exprRetVar->Execute(pile3)) return false; pile3->SetVar(nullptr); @@ -369,11 +372,7 @@ bool CBotDefClass::Execute(CBotStack* &pj) ppVars[i] = nullptr; // creates a variable for the result - CBotVar* pResult = nullptr; // constructor still void - - if ( !pClass->ExecuteMethode(m_nMethodeIdent, pClass->GetName(), - pThis, ppVars, - pResult, pile2, GetToken())) return false; // interrupt + if ( !pClass->ExecuteMethode(m_nMethodeIdent, pThis, ppVars, CBotTypResult(CBotTypVoid), pile2, GetToken())) return false; // interrupt pThis->SetInit(CBotVar::InitType::DEF); pThis->ConstructorSet(); // indicates that the constructor has been called @@ -483,9 +482,7 @@ void CBotDefClass::RestoreState(CBotStack* &pj, bool bMain) ppVars[i] = nullptr; // creates a variable for the result -// CBotVar* pResult = nullptr; // constructor still void - - pClass->RestoreMethode(m_nMethodeIdent, pClass->GetName(), pThis, ppVars, pile2); + pClass->RestoreMethode(m_nMethodeIdent, pt, pThis, ppVars, pile2); return; } } diff --git a/src/CBot/CBotInstr/CBotExprUnaire.cpp b/src/CBot/CBotInstr/CBotExprUnaire.cpp index 0080a468..e4d1ec3f 100644 --- a/src/CBot/CBotInstr/CBotExprUnaire.cpp +++ b/src/CBot/CBotInstr/CBotExprUnaire.cpp @@ -41,7 +41,7 @@ CBotExprUnaire::~CBotExprUnaire() } //////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack) +CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral) { int op = p->GetType(); CBotToken* pp = p; @@ -52,7 +52,10 @@ CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack) CBotExprUnaire* inst = new CBotExprUnaire(); inst->SetToken(pp); - if (nullptr != (inst->m_expr = CBotParExpr::Compile(p, pStk ))) + if (!bLiteral) inst->m_expr = CBotParExpr::Compile(p, pStk); + else inst->m_expr = CBotParExpr::CompileLitExpr(p, pStk); + + if (inst->m_expr != nullptr) { if (op == ID_ADD && pStk->GetType() < CBotTypBoolean) // only with the number return pStack->Return(inst, pStk); diff --git a/src/CBot/CBotInstr/CBotExprUnaire.h b/src/CBot/CBotInstr/CBotExprUnaire.h index e7e58c74..b15d2552 100644 --- a/src/CBot/CBotInstr/CBotExprUnaire.h +++ b/src/CBot/CBotInstr/CBotExprUnaire.h @@ -34,12 +34,13 @@ public: ~CBotExprUnaire(); /*! - * \brief Compile - * \param p - * \param pStack - * \return + * \brief Compile an expression with a unary operator + * \param p[in, out] Pointer to first token of the expression, will be updated to point to first token after the expression + * \param pStack Current compilation stack frame + * \param bLiteral If true, compiles only literal expressions Ex: ~11, -4.0, !false, not true + * \return The compiled instruction or nullptr */ - static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral = false); /*! * \brief Execute diff --git a/src/CBot/CBotInstr/CBotExprVar.cpp b/src/CBot/CBotInstr/CBotExprVar.cpp index a15c9c9a..ed77d8ac 100644 --- a/src/CBot/CBotInstr/CBotExprVar.cpp +++ b/src/CBot/CBotInstr/CBotExprVar.cpp @@ -44,7 +44,7 @@ CBotExprVar::~CBotExprVar() } //////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::ProtectionLevel privat) +CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bCheckReadOnly) { // CBotToken* pDebut = p; CBotCStack* pStk = pStack->TokenStack(); @@ -67,7 +67,7 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot if (ident > 0 && ident < 9000) { - if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, privat)) + if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, bCheckReadOnly)) { pStk->SetError(CBotErrPrivate, p); goto err; @@ -122,6 +122,8 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot { if (p->GetNext()->GetType() == ID_OPENPAR) // a method call? { + if (bCheckReadOnly) goto err; // don't allow increment a method call "++" + CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var); if (!pStk->IsOk()) goto err; inst->AddNext3(i); // added after @@ -137,7 +139,7 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot if (var != nullptr) { i->SetUniqNum(var->GetUniqNum()); - if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, privat)) + if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, bCheckReadOnly)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotInstr/CBotExprVar.h b/src/CBot/CBotInstr/CBotExprVar.h index 9cf68991..d9b857cb 100644 --- a/src/CBot/CBotInstr/CBotExprVar.h +++ b/src/CBot/CBotInstr/CBotExprVar.h @@ -40,14 +40,13 @@ public: ~CBotExprVar(); /*! - * \brief Compile - * \param p - * \param pStack - * \param privat + * \brief Compile an expression of a variable, possibly chained with index operators and/or dot operators + * \param p[in, out] Pointer to first token of the expression, will be updated to point to first token after the expression + * \param pStack Current compilation stack frame + * \param bCheckReadOnly True for operations that would modify the value of the variable * \return */ - static CBotInstr* Compile(CBotToken*& p, CBotCStack* pStack, - CBotVar::ProtectionLevel privat = CBotVar::ProtectionLevel::Protected); + static CBotInstr* Compile(CBotToken*& p, CBotCStack* pStack, bool bCheckReadOnly = false); /*! * \brief CompileMethode @@ -81,6 +80,8 @@ public: */ bool ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, bool bStep); + using CBotInstr::ExecuteVar; + /*! * \brief RestoreStateVar Fetch variable at runtime. * \param pj diff --git a/src/CBot/CBotInstr/CBotFieldExpr.cpp b/src/CBot/CBotInstr/CBotFieldExpr.cpp index 09ac5f8e..04635a1a 100644 --- a/src/CBot/CBotInstr/CBotFieldExpr.cpp +++ b/src/CBot/CBotInstr/CBotFieldExpr.cpp @@ -135,12 +135,11 @@ std::string CBotFieldExpr::GetDebugData() } //////////////////////////////////////////////////////////////////////////////// -bool CBotFieldExpr::CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, - CBotVar::ProtectionLevel privat) +bool CBotFieldExpr::CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, bool bCheckReadOnly) { CBotVar::ProtectionLevel varPriv = pVar->GetPrivate(); - if (privat == CBotVar::ProtectionLevel::ReadOnly && varPriv == privat) + if (bCheckReadOnly && varPriv == CBotVar::ProtectionLevel::ReadOnly) return true; if (varPriv == CBotVar::ProtectionLevel::Public) return false; diff --git a/src/CBot/CBotInstr/CBotFieldExpr.h b/src/CBot/CBotInstr/CBotFieldExpr.h index 67ab2f05..5f093bd7 100644 --- a/src/CBot/CBotInstr/CBotFieldExpr.h +++ b/src/CBot/CBotInstr/CBotFieldExpr.h @@ -72,13 +72,12 @@ public: * This function doesn't set the error flag itself. * * \param pStack Current compilation stack frame - * \param pPrev Class instance which variable to check is part of, or nullptr if not part of a class + * \param pPrev Class instance which variable to check is part of, or nullptr when compiler inserts 'this.' before * \param pVar Variable to check - * \param privat CBotVar::ProtectionLevel::ReadOnly if requesting read-only access, anything else otherwise + * \param bCheckReadOnly True for operations that would modify the value of the variable * \return true if pVar is inaccessible in the current context, false if access should be allowed */ - static bool CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, - CBotVar::ProtectionLevel privat = CBotVar::ProtectionLevel::Protected); + static bool CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, bool bCheckReadOnly = false); protected: virtual const std::string GetDebugName() override { return "CBotFieldExpr"; } diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp index 0b1e05b5..c24f9861 100644 --- a/src/CBot/CBotInstr/CBotFunction.cpp +++ b/src/CBot/CBotInstr/CBotFunction.cpp @@ -46,7 +46,6 @@ CBotFunction::CBotFunction() { m_param = nullptr; // empty parameter list m_block = nullptr; // the instruction block - m_next = nullptr; // functions can be chained m_bPublic = false; // function not public m_bExtern = false; // function not extern m_pProg = nullptr; @@ -63,7 +62,6 @@ CBotFunction::~CBotFunction() { delete m_param; // empty parameter list delete m_block; // the instruction block - delete m_next; // remove public list if there is if (m_bPublic) @@ -167,6 +165,7 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct if ( IsOfType(p, ID_NOT) ) { CBotToken d(std::string("~") + p->GetString()); + d.SetPos(pp->GetStart(), p->GetEnd()); func->m_token = d; } @@ -178,7 +177,11 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct func->m_MasterClass = pp->GetString(); func->m_classToken = *pp; CBotClass* pClass = CBotClass::Find(pp); - if ( pClass == nullptr ) goto bad; + if ( pClass == nullptr ) + { + pStk->SetError(CBotErrNoClassName, pp); + goto bad; + } // pp = p; func->m_token = *p; @@ -186,6 +189,7 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct } func->m_openpar = *p; + delete func->m_param; func->m_param = CBotDefParam::Compile(p, pStk ); func->m_closepar = *(p->GetPrev()); if (pStk->IsOk()) @@ -224,6 +228,12 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct func->m_closeblk = (p != nullptr && p->GetPrev() != nullptr) ? *(p->GetPrev()) : CBotToken(); if ( pStk->IsOk() ) { + if (!func->m_retTyp.Eq(CBotTypVoid) && !func->HasReturn()) + { + int errPos = func->m_closeblk.GetStart(); + pStk->ResetError(CBotErrNoReturn, errPos, errPos); + goto bad; + } return pStack->ReturnFunc(func, pStk); } } @@ -270,6 +280,7 @@ CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClas if ( IsOfType(p, ID_NOT) ) { CBotToken d(std::string("~") + p->GetString()); + d.SetPos(pp->GetStart(), p->GetEnd()); func->m_token = d; } @@ -279,22 +290,47 @@ CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClas if ( IsOfType( p, ID_DBLDOTS ) ) // method for a class { func->m_MasterClass = pp->GetString(); - CBotClass* pClass = CBotClass::Find(pp); - if ( pClass == nullptr ) - { - pStk->SetError(CBotErrNotClass, pp); - goto bad; - } - + // existence of the class is checked + // later in CBotFunction::Compile() pp = p; func->m_token = *p; if (!IsOfType(p, TokenTypVar)) goto bad; } - func->m_param = CBotDefParam::Compile(p, pStk ); + + CBotToken* openPar = p; + func->m_param = CBotDefParam::Compile(p, pStk); // compile parameters + + if (pStk->IsOk() && pClass != nullptr) // method in a class + { + // check if a constructor has return type void + if (func->GetName() == pClass->GetName() && !func->m_retTyp.Eq(CBotTypVoid)) + { + pp = &(func->m_retToken); + pStk->SetError(CBotErrFuncNotVoid, pp); + } + + if (pStk->IsOk() && pp->GetString() == "~") // destructor + { + // check destructor name + if (func->GetName() != ("~" + pClass->GetName())) + pStk->SetError(CBotErrNoFunc, pp); + // confirm no parameters + if (pStk->IsOk() && func->m_param != nullptr) + pStk->SetError(CBotErrClosePar, openPar->GetNext()); + // must return void + if (pStk->IsOk() && !func->m_retTyp.Eq(CBotTypVoid)) + { + pp = &(func->m_retToken); + pStk->SetError(CBotErrFuncNotVoid, pp); + } + } + } + if (pStk->IsOk()) { // looks if the function exists elsewhere + pp = &(func->m_token); if (( pClass != nullptr || !pStack->CheckCall(pp, func->m_param)) && ( pClass == nullptr || !pClass->CheckCall(pStack->GetProgram(), func->m_param, pp)) ) { @@ -334,9 +370,19 @@ bool CBotFunction::Execute(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInstance) pile->SetProgram(m_pProg); // bases for routines + if ( pile->IfStep() ) return false; + if ( pile->GetState() == 0 ) { - if ( !m_param->Execute(ppVars, pile) ) return false; // define parameters + if (m_param != nullptr) + { + // stack for parameters and default args + CBotStack* pile3b = pile->AddStack(); + pile3b->SetState(1); + + if ( !m_param->Execute(ppVars, pile) ) return false; // define parameters + pile3b->Delete(); // done with param stack + } pile->IncState(); } @@ -369,8 +415,6 @@ bool CBotFunction::Execute(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInstance) pile->IncState(); } - if ( pile->IfStep() ) return false; - if ( !m_block->Execute(pile) ) { if ( pile->GetError() < 0 ) @@ -399,7 +443,22 @@ void CBotFunction::RestoreState(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInst pile2->Delete(); } - m_param->RestoreState(pile2, true); // parameters + if ( pile->GetState() == 0 ) + { + if (m_param != nullptr) + { + CBotStack* pile3b = pile2->RestoreStack(); + + if (pile3b != nullptr && pile3b->GetState() == 1) + m_param->RestoreState(pile2, true); // restore executing default arguments + else + m_param->RestoreState(pile2, false); // restore parameter IDs + } + return; + } + + if (m_param != nullptr) + m_param->RestoreState(pile2, false); // restore parameter IDs if ( !m_MasterClass.empty() ) { @@ -412,37 +471,28 @@ void CBotFunction::RestoreState(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInst } //////////////////////////////////////////////////////////////////////////////// -void CBotFunction::AddNext(CBotFunction* p) +CBotTypResult CBotFunction::CompileCall(const std::list& localFunctionList, const std::string &name, CBotVar** ppVars, long &nIdent) { - CBotFunction* pp = this; - while (pp->m_next != nullptr) pp = pp->m_next; - - pp->m_next = p; -} - -//////////////////////////////////////////////////////////////////////////////// -CBotTypResult CBotFunction::CompileCall(const std::string& name, CBotVar** ppVars, long& nIdent) -{ - nIdent = 0; - CBotTypResult type; - -// CBotFunction* pt = FindLocalOrPublic(nIdent, name, ppVars, type); - FindLocalOrPublic(nIdent, name, ppVars, type); + CBotTypResult type; + if (!FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type)) + { + // Reset the identifier to "not found" value + nIdent = 0; + } return type; } //////////////////////////////////////////////////////////////////////////////// -CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& name, CBotVar** ppVars, - CBotTypResult& TypeOrError, bool bPublic) +CBotFunction* CBotFunction::FindLocalOrPublic(const std::list& localFunctionList, long &nIdent, const std::string &name, + CBotVar** ppVars, CBotTypResult &TypeOrError, bool bPublic) { TypeOrError.SetType(CBotErrUndefCall); // no routine of the name - CBotFunction* pt; if ( nIdent ) { - if ( this != nullptr ) for ( pt = this ; pt != nullptr ; pt = pt->m_next ) + for (CBotFunction* pt : localFunctionList) { - if ( pt->m_nFuncIdent == nIdent ) + if (pt->m_nFuncIdent == nIdent) { TypeOrError = pt->m_retTyp; return pt; @@ -464,62 +514,64 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n std::map funcMap; - if ( this != nullptr ) + for (CBotFunction* pt : localFunctionList) { - for ( pt = this ; pt != nullptr ; pt = pt->m_next ) + if ( pt->m_token.GetString() == name ) { - if ( pt->m_token.GetString() == name ) + int i = 0; + int alpha = 0; // signature of parameters + // parameters are compatible? + CBotDefParam* pv = pt->m_param; // expected list of parameters + CBotVar* pw = ppVars[i++]; // provided list parameter + while ( pv != nullptr && (pw != nullptr || pv->HasDefault()) ) { - int i = 0; - int alpha = 0; // signature of parameters - // parameters are compatible? - CBotDefParam* pv = pt->m_param; // expected list of parameters - CBotVar* pw = ppVars[i++]; // provided list parameter - while ( pv != nullptr && pw != nullptr) + if (pw == nullptr) // end of arguments { - CBotTypResult paramType = pv->GetTypResult(); - CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); - - if (!TypesCompatibles(paramType, argType)) - { - if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam); - break; - } - - if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer)) - { - CBotClass* c1 = paramType.GetClass(); - CBotClass* c2 = argType.GetClass(); - while (c2 != c1 && c2 != nullptr) // implicit cast - { - alpha += 10; - c2 = c2->GetParent(); - } - } - else - { - int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); - alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! - } pv = pv->GetNext(); - pw = ppVars[i++]; + continue; // skip params with default values } - if ( pw != nullptr ) + CBotTypResult paramType = pv->GetTypResult(); + CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + + if (!TypesCompatibles(paramType, argType)) { - if ( !funcMap.empty() ) continue; - if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam); - if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam); - continue; // too many parameters + if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam); + break; } - if ( pv != nullptr ) + + if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer)) { - if ( !funcMap.empty() ) continue; - if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam); - if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam); - continue; // not enough parameters + CBotClass* c1 = paramType.GetClass(); + CBotClass* c2 = argType.GetClass(); + while (c2 != c1 && c2 != nullptr) // implicit cast + { + alpha += 10; + c2 = c2->GetParent(); + } } - funcMap.insert( std::pair(pt, alpha) ); + else + { + int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! + } + pv = pv->GetNext(); + pw = ppVars[i++]; } + if ( pw != nullptr ) + { + if ( !funcMap.empty() ) continue; + if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam); + if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam); + continue; // too many parameters + } + if ( pv != nullptr ) + { + if ( !funcMap.empty() ) continue; + if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam); + if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam); + continue; // not enough parameters + } + funcMap.insert( std::pair(pt, alpha) ); } } @@ -534,8 +586,13 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n // parameters sont-ils compatibles ? CBotDefParam* pv = pt->m_param; // list of expected parameters CBotVar* pw = ppVars[i++]; // list of provided parameters - while ( pv != nullptr && pw != nullptr) + while ( pv != nullptr && (pw != nullptr || pv->HasDefault()) ) { + if (pw == nullptr) // end of arguments + { + pv = pv->GetNext(); + continue; // skip params with default values + } CBotTypResult paramType = pv->GetTypResult(); CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); @@ -610,12 +667,13 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n } //////////////////////////////////////////////////////////////////////////////// -int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken) +int CBotFunction::DoCall(CBotProgram* program, const std::list& localFunctionList, long &nIdent, const std::string &name, + CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken) { CBotTypResult type; CBotFunction* pt = nullptr; - pt = FindLocalOrPublic(nIdent, name, ppVars, type); + pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type); if ( pt != nullptr ) { @@ -632,9 +690,12 @@ int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar** ppVars if ( pStk1->GetState() == 0 ) { - if ( !pt->m_MasterClass.empty() ) + // stack for parameters and default args + CBotStack* pStk3b = pStk3->AddStack(); + + if (pStk3b->GetState() == 0 && !pt->m_MasterClass.empty()) { - CBotVar* pInstance = m_pProg->m_thisVar; + CBotVar* pInstance = program->m_thisVar; // make "this" known CBotVar* pThis ; if ( pInstance == nullptr ) @@ -658,10 +719,21 @@ int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar** ppVars pThis->SetUniqNum(-2); pStk1->AddVar(pThis); } + pStk3b->SetState(1); // set 'this' was created // initializes the variables as parameters - pt->m_param->Execute(ppVars, pStk3); // cannot be interrupted - + if (pt->m_param != nullptr) + { + if (!pt->m_param->Execute(ppVars, pStk3)) // interupt here + { + if (!pStk3->IsOk() && pt->m_pProg != program) + { + pStk3->SetPosError(pToken); // indicates the error on the procedure call + } + return false; + } + } + pStk3b->Delete(); // done with param stack pStk1->IncState(); } @@ -670,7 +742,7 @@ int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar** ppVars if ( !pStk3->GetRetVar( // puts the result on the stack pt->m_block->Execute(pStk3) )) // GetRetVar said if it is interrupted { - if ( !pStk3->IsOk() && pt->m_pProg != m_pProg ) + if ( !pStk3->IsOk() && pt->m_pProg != program ) { pStk3->SetPosError(pToken); // indicates the error on the procedure call } @@ -683,7 +755,8 @@ int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar** ppVars } //////////////////////////////////////////////////////////////////////////////// -void CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar** ppVars, CBotStack* pStack) +void CBotFunction::RestoreCall(const std::list& localFunctionList, + long &nIdent, const std::string &name, CBotVar** ppVars, CBotStack* pStack) { CBotTypResult type; CBotFunction* pt = nullptr; @@ -692,7 +765,7 @@ void CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar** // search function to return the ok identifier - pt = FindLocalOrPublic(nIdent, name, ppVars, type); + pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type); if ( pt != nullptr ) { @@ -729,24 +802,33 @@ void CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar** if ( pStk1->GetState() == 0 ) { - pt->m_param->RestoreState(pStk3, true); + if (pt->m_param != nullptr) + { + CBotStack* pStk3b = pStk3->RestoreStack(); + + if (pStk3b != nullptr && pStk3b->GetState() == 1) + pt->m_param->RestoreState(pStk3, true); // restore executing default arguments + else + pt->m_param->RestoreState(pStk3, false); // restore parameter IDs + } return; } // initializes the variables as parameters - pt->m_param->RestoreState(pStk3, false); + if (pt->m_param != nullptr) + pt->m_param->RestoreState(pStk3, false); // restore parameter IDs pt->m_block->RestoreState(pStk3, true); } } //////////////////////////////////////////////////////////////////////////////// -int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar* pThis, CBotVar** ppVars, CBotStack* pStack, - CBotToken* pToken, CBotClass* pClass) +int CBotFunction::DoCall(const std::list& localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis, + CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken, CBotClass* pClass) { CBotTypResult type; CBotProgram* pProgCurrent = pStack->GetProgram(); - CBotFunction* pt = FindLocalOrPublic(nIdent, name, ppVars, type, false); + CBotFunction* pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type, false); if ( pt != nullptr ) { @@ -762,23 +844,42 @@ int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar* pThis, if ( pStk->GetState() == 0 ) { - // sets the variable "this" on the stack - CBotVar* pthis = CBotVar::Create("this", CBotTypNullPointer); - pthis->Copy(pThis, false); - pthis->SetUniqNum(-2); // special value - pStk->AddVar(pthis); + // stack for parameters and default args + CBotStack* pStk3b = pStk3->AddStack(); - CBotClass* pClass = pThis->GetClass()->GetParent(); - if ( pClass ) + if (pStk3b->GetState() == 0) { - // sets the variable "super" on the stack - CBotVar* psuper = CBotVar::Create("super", CBotTypNullPointer); - psuper->Copy(pThis, false); // in fact identical to "this" - psuper->SetUniqNum(-3); // special value - pStk->AddVar(psuper); + // sets the variable "this" on the stack + CBotVar* pthis = CBotVar::Create("this", CBotTypNullPointer); + pthis->Copy(pThis, false); + pthis->SetUniqNum(-2); // special value + pStk->AddVar(pthis); + + CBotClass* pClass = pThis->GetClass()->GetParent(); + if ( pClass ) + { + // sets the variable "super" on the stack + CBotVar* psuper = CBotVar::Create("super", CBotTypNullPointer); + psuper->Copy(pThis, false); // in fact identical to "this" + psuper->SetUniqNum(-3); // special value + pStk->AddVar(psuper); + } } + pStk3b->SetState(1); // set 'this' was created + // initializes the variables as parameters - pt->m_param->Execute(ppVars, pStk3); // cannot be interrupted + if (pt->m_param != nullptr) + { + if (!pt->m_param->Execute(ppVars, pStk3)) // interupt here + { + if (!pStk3->IsOk() && pt->m_pProg != pProgCurrent) + { + pStk3->SetPosError(pToken); // indicates the error on the procedure call + } + return false; + } + } + pStk3b->Delete(); // done with param stack pStk->IncState(); } @@ -822,11 +923,11 @@ int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar* pThis, } //////////////////////////////////////////////////////////////////////////////// -bool CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* pThis, CBotVar** ppVars, - CBotStack* pStack, CBotClass* pClass) +bool CBotFunction::RestoreCall(const std::list& localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis, + CBotVar** ppVars, CBotStack* pStack, CBotClass* pClass) { CBotTypResult type; - CBotFunction* pt = FindLocalOrPublic(nIdent, name, ppVars, type); + CBotFunction* pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type); if ( pt != nullptr ) { @@ -846,7 +947,21 @@ bool CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* p CBotStack* pStk3 = pStk->RestoreStack(nullptr); // to set parameters passed if ( pStk3 == nullptr ) return true; - pt->m_param->RestoreState(pStk3, true); // parameters + if ( pStk->GetState() == 0 ) + { + if (pt->m_param != nullptr) + { + CBotStack* pStk3b = pStk3->RestoreStack(); + if (pStk3b != nullptr && pStk3b->GetState() == 1) + pt->m_param->RestoreState(pStk3, true); // restore executing default arguments + else + pt->m_param->RestoreState(pStk3, false); // restore parameter IDs + } + return true; + } + + if (pt->m_param != nullptr) + pt->m_param->RestoreState(pStk3, false); // restore parameter IDs if ( pStk->GetState() > 1 && // latching is effective? pt->m_bSynchro ) @@ -903,18 +1018,18 @@ std::string CBotFunction::GetParams() return params; } -//////////////////////////////////////////////////////////////////////////////// -CBotFunction* CBotFunction::Next() -{ - return m_next; -} - //////////////////////////////////////////////////////////////////////////////// void CBotFunction::AddPublic(CBotFunction* func) { m_publicFunctions.insert(func); } +bool CBotFunction::HasReturn() +{ + if (m_block != nullptr) return m_block->HasReturn(); + return false; +} + std::string CBotFunction::GetDebugData() { std::stringstream ss; diff --git a/src/CBot/CBotInstr/CBotFunction.h b/src/CBot/CBotInstr/CBotFunction.h index 76a02bc0..1f25474f 100644 --- a/src/CBot/CBotInstr/CBotFunction.h +++ b/src/CBot/CBotInstr/CBotFunction.h @@ -60,11 +60,16 @@ public: bool bLocal = true); /*! - * \brief Compile1 Pre-compile a new function. - * \param p - * \param pStack - * \param pClass - * \return + * \brief Pre-compile a new function + * \param p[in, out] Pointer to first token of the function, will be updated to point to first token after the function definition + * \param pStack Compile stack + * \param pClass If this is a class method, pointer to class this function is part of, otherwise nullptr + * + * This function is used to find the beginning and end of function definition. + * + * If any errors in the code are detected, this function will set the error on compile stack and return nullptr. + * + * \return Precompiled function, or nullptr in case of error */ static CBotFunction* Compile1(CBotToken* &p, CBotCStack* pStack, @@ -81,6 +86,8 @@ public: CBotStack* &pj, CBotVar* pInstance = nullptr); + using CBotInstr::Execute; + /*! * \brief RestoreState * \param ppVars @@ -91,40 +98,46 @@ public: CBotStack* &pj, CBotVar* pInstance = nullptr); - /*! - * \brief AddNext - * \param p - */ - void AddNext(CBotFunction* p); + using CBotInstr::RestoreState; /*! - * \brief CompileCall - * \param name - * \param ppVars - * \param nIdent - * \return + * \brief Compile a function call + * + * See FindLocalOrPublic for more detailed explanation + * + * \param localFunctionList Linked list of local functions to search in, can be null + * \param name Name of the function + * \param ppVars List of function arguments + * \param nIdent[in, out] Unique identifier of the function + * \return Type returned by the function or error code + * \see FindLocalOrPublic */ - CBotTypResult CompileCall(const std::string& name, - CBotVar** ppVars, - long& nIdent); + static CBotTypResult CompileCall(const std::list& localFunctionList, + const std::string &name, CBotVar** ppVars, long &nIdent); /*! - * \brief FindLocalOrPublic Is a function according to its unique identifier - * if the identifier is not found, looking by name and parameters. - * \param nIdent - * \param name - * \param ppVars - * \param TypeOrError - * \param bPublic - * \return + * \brief Finds a local or public function + * + *

Finds a local or (if bPublic is true) public function to call + * + *

First, it looks for a function according to its unique identifier.
+ * If the identifier is not found, looks by name and parameters. + * + * \param localFunctionList Linked list of local functions to search in, can be null + * \param nIdent[in, out] Unique identifier of the function + * \param name Name of the function + * \param ppVars List of function arguments + * \param TypeOrError Type returned by the function or error code + * \param bPublic Whether to look in public functions or not + * \return Pointer to found CBotFunction instance, or nullptr in case of no match or ambiguity (see TypeOrError for error code) */ - CBotFunction* FindLocalOrPublic(long& nIdent, const std::string& name, - CBotVar** ppVars, - CBotTypResult& TypeOrError, - bool bPublic = true); + static CBotFunction* FindLocalOrPublic(const std::list& localFunctionList, long &nIdent, const std::string &name, + CBotVar** ppVars, CBotTypResult &TypeOrError, bool bPublic = true); /*! * \brief DoCall Fait un appel à une fonction. + * \param program + * \param localFunctionList * \param nIdent * \param name * \param ppVars @@ -133,27 +146,24 @@ public: * \return */ - int DoCall(long& nIdent, - const std::string& name, - CBotVar** ppVars, - CBotStack* pStack, - CBotToken* pToken); + static int DoCall(CBotProgram* program, const std::list& localFunctionList, long &nIdent, const std::string &name, + CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken); /*! * \brief RestoreCall + * \param localFunctionList * \param nIdent * \param name * \param ppVars * \param pStack */ - void RestoreCall(long& nIdent, - const std::string& name, - CBotVar** ppVars, - CBotStack* pStack); + static void RestoreCall(const std::list& localFunctionList, + long &nIdent, const std::string &name, CBotVar** ppVars, CBotStack* pStack); /*! - * \brief DoCall Makes call of a method note: this is already on the stack, - * the pointer pThis is just to simplify. + * \brief DoCall Makes call of a method + * note: this is already on the stack, the pointer pThis is just to simplify. + * \param localFunctionList * \param nIdent * \param name * \param pThis @@ -163,16 +173,12 @@ public: * \param pClass * \return */ - int DoCall(long& nIdent, - const std::string& name, - CBotVar* pThis, - CBotVar** ppVars, - CBotStack* pStack, - CBotToken* pToken, - CBotClass* pClass); + static int DoCall(const std::list& localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis, + CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken, CBotClass* pClass); /*! * \brief RestoreCall + * \param localFunctionList * \param nIdent * \param name * \param pThis @@ -181,12 +187,8 @@ public: * \param pClass * \return Returns true if the method call was restored. */ - bool RestoreCall(long& nIdent, - const std::string& name, - CBotVar* pThis, - CBotVar** ppVars, - CBotStack* pStack, - CBotClass* pClass); + static bool RestoreCall(const std::list& localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis, + CBotVar** ppVars, CBotStack* pStack, CBotClass* pClass); /*! * \brief CheckParam See if the "signature" of parameters is identical. @@ -225,12 +227,6 @@ public: */ bool IsExtern(); - /*! - * \brief Next - * \return - */ - CBotFunction* Next(); - /*! * \brief GetPosition * \param start @@ -243,6 +239,12 @@ public: CBotGet modestart, CBotGet modestop); + /*! + * \brief Check if the function has a return statment that will execute. + * \return true if a return statment was found. + */ + bool HasReturn() override; + protected: virtual const std::string GetDebugName() override { return "CBotFunction"; } virtual std::string GetDebugData() override; @@ -258,7 +260,6 @@ private: CBotDefParam* m_param; //! The instruction block. CBotInstr* m_block; - CBotFunction* m_next; //! If returns CBotTypClass. CBotToken m_retToken; //! Complete type of the result. diff --git a/src/CBot/CBotInstr/CBotIf.cpp b/src/CBot/CBotInstr/CBotIf.cpp index c0a1d1ee..92da9b48 100644 --- a/src/CBot/CBotInstr/CBotIf.cpp +++ b/src/CBot/CBotInstr/CBotIf.cpp @@ -163,6 +163,15 @@ void CBotIf :: RestoreState(CBotStack* &pj, bool bMain) } } +bool CBotIf::HasReturn() +{ + if (m_block != nullptr && m_blockElse != nullptr) + { + if (m_block->HasReturn() && m_blockElse->HasReturn()) return true; + } + return CBotInstr::HasReturn(); // check next block or instruction +} + std::map CBotIf::GetDebugLinks() { auto links = CBotInstr::GetDebugLinks(); diff --git a/src/CBot/CBotInstr/CBotIf.h b/src/CBot/CBotInstr/CBotIf.h index 94b2d257..3df400e1 100644 --- a/src/CBot/CBotInstr/CBotIf.h +++ b/src/CBot/CBotInstr/CBotIf.h @@ -56,6 +56,14 @@ public: */ void RestoreState(CBotStack* &pj, bool bMain) override; + /** + * \brief Check 'if' and 'else' for return statements. + * Returns true when 'if' and 'else' have return statements, + * if not, the next block or instruction is checked. + * \return true if a return statement is found. + */ + bool HasReturn() override; + protected: virtual const std::string GetDebugName() override { return "CBotIf"; } virtual std::map GetDebugLinks() override; diff --git a/src/CBot/CBotInstr/CBotInstr.cpp b/src/CBot/CBotInstr/CBotInstr.cpp index 2ae04233..69bb9025 100644 --- a/src/CBot/CBotInstr/CBotInstr.cpp +++ b/src/CBot/CBotInstr/CBotInstr.cpp @@ -359,6 +359,12 @@ CBotInstr* CBotInstr::CompileArray(CBotToken* &p, CBotCStack* pStack, CBotTypRes return nullptr; } +bool CBotInstr::HasReturn() +{ + if (m_next != nullptr) return m_next->HasReturn(); + return false; // end of the list +} + std::map CBotInstr::GetDebugLinks() { return { diff --git a/src/CBot/CBotInstr/CBotInstr.h b/src/CBot/CBotInstr/CBotInstr.h index ab991366..a96550db 100644 --- a/src/CBot/CBotInstr/CBotInstr.h +++ b/src/CBot/CBotInstr/CBotInstr.h @@ -281,6 +281,12 @@ public: */ static bool ChkLvl(const std::string& label, int type); + /** + * \brief Check a list of instructions for a return statement. + * \return true if a return statement was found. + */ + virtual bool HasReturn(); + protected: friend class CBotDebug; /** diff --git a/src/CBot/CBotInstr/CBotInstrCall.cpp b/src/CBot/CBotInstr/CBotInstrCall.cpp index 6b45bb52..86e37f72 100644 --- a/src/CBot/CBotInstr/CBotInstrCall.cpp +++ b/src/CBot/CBotInstr/CBotInstrCall.cpp @@ -36,6 +36,7 @@ namespace CBot CBotInstrCall::CBotInstrCall() { m_parameters = nullptr; + m_exprRetVar = nullptr; m_nFuncIdent = 0; } @@ -43,6 +44,7 @@ CBotInstrCall::CBotInstrCall() CBotInstrCall::~CBotInstrCall() { delete m_parameters; + delete m_exprRetVar; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/CBot/CBotInstr/CBotInstrMethode.cpp b/src/CBot/CBotInstr/CBotInstrMethode.cpp index bf4ec41e..f4cda036 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.cpp +++ b/src/CBot/CBotInstr/CBotInstrMethode.cpp @@ -36,6 +36,7 @@ namespace CBot CBotInstrMethode::CBotInstrMethode() { m_parameters = nullptr; + m_exprRetVar = nullptr; m_MethodeIdent = 0; } @@ -43,6 +44,7 @@ CBotInstrMethode::CBotInstrMethode() CBotInstrMethode::~CBotInstrMethode() { delete m_parameters; + delete m_exprRetVar; } //////////////////////////////////////////////////////////////////////////////// @@ -67,8 +69,7 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* inst->m_thisIdent = var->GetUniqNum(); CBotClass* pClass = var->GetClass(); // pointer to the class inst->m_className = pClass->GetName(); // name of the class - CBotTypResult r = pClass->CompileMethode(inst->m_methodName, var, ppVars, - pStack, inst->m_MethodeIdent); + CBotTypResult r = pClass->CompileMethode(pp, var, ppVars, pStack, inst->m_MethodeIdent); delete pStack->TokenStack(); // release parameters on the stack inst->m_typRes = r; @@ -176,18 +177,7 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre else pClass = pThis->GetClass(); - CBotVar* pResult = nullptr; - if (m_typRes.GetType() > 0) pResult = CBotVar::Create("", m_typRes); - if (m_typRes.Eq(CBotTypClass)) - { - pResult->SetClass(m_typRes.GetClass()); - } - CBotVar* pRes = pResult; - - if ( !pClass->ExecuteMethode(m_MethodeIdent, m_methodName, - pThis, ppVars, - pResult, pile2, GetToken())) return false; - if (pRes != pResult) delete pRes; + if ( !pClass->ExecuteMethode(m_MethodeIdent, pThis, ppVars, m_typRes, pile2, GetToken())) return false; if (m_exprRetVar != nullptr) // .func().member { @@ -264,8 +254,7 @@ void CBotInstrMethode::RestoreStateVar(CBotStack* &pile, bool bMain) // CBotVar* pRes = pResult; - pClass->RestoreMethode(m_MethodeIdent, m_methodName, - pThis, ppVars, pile2); + pClass->RestoreMethode(m_MethodeIdent, &m_token, pThis, ppVars, pile2); } //////////////////////////////////////////////////////////////////////////////// @@ -316,24 +305,12 @@ bool CBotInstrMethode::Execute(CBotStack* &pj) else pClass = pThis->GetClass(); - CBotVar* pResult = nullptr; - if (m_typRes.GetType()>0) pResult = CBotVar::Create("", m_typRes); - if (m_typRes.Eq(CBotTypClass)) - { - pResult->SetClass(m_typRes.GetClass()); - } - CBotVar* pRes = pResult; - - if ( !pClass->ExecuteMethode(m_MethodeIdent, m_methodName, - pThis, ppVars, - pResult, pile2, GetToken())) return false; // interupted + if ( !pClass->ExecuteMethode(m_MethodeIdent, pThis, ppVars, m_typRes, pile2, GetToken())) return false; // interupted // set the new value of this in place of the old variable CBotVar* old = pile1->FindVar(m_token, false); old->Copy(pThis, false); - if (pRes != pResult) delete pRes; - return pj->Return(pile2); // release the entire stack } diff --git a/src/CBot/CBotInstr/CBotInstrUtils.h b/src/CBot/CBotInstr/CBotInstrUtils.h index b5302883..0177e09c 100644 --- a/src/CBot/CBotInstr/CBotInstrUtils.h +++ b/src/CBot/CBotInstr/CBotInstrUtils.h @@ -45,7 +45,7 @@ CBotInstr* CompileParams(CBotToken* &p, CBotCStack* pStack, CBotVar** ppVars); * For assignment or compound assignment operations (it's reversed): * see CBotReturn::Compile & CBotExpression::Compile - * TypeCompatible( valueType, varType, opType ) + * TypeCompatible( valueType, varType, opType ) * \param type1 * \param type2 * \param op diff --git a/src/CBot/CBotInstr/CBotLeftExpr.cpp b/src/CBot/CBotInstr/CBotLeftExpr.cpp index 4678ff8e..4c4400a9 100644 --- a/src/CBot/CBotInstr/CBotLeftExpr.cpp +++ b/src/CBot/CBotInstr/CBotLeftExpr.cpp @@ -64,7 +64,7 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) inst->m_nIdent = var->GetUniqNum(); if (inst->m_nIdent > 0 && inst->m_nIdent < 9000) { - if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, CBotVar::ProtectionLevel::ReadOnly)) + if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, true)) { pStk->SetError(CBotErrPrivate, p); goto err; @@ -128,8 +128,7 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) var = var->GetItem(p->GetString()); // get item correspondent if (var != nullptr) { - if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, - CBotVar::ProtectionLevel::ReadOnly)) + if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, true)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotInstr/CBotLeftExpr.h b/src/CBot/CBotInstr/CBotLeftExpr.h index 7acea068..4cffe46e 100644 --- a/src/CBot/CBotInstr/CBotLeftExpr.h +++ b/src/CBot/CBotInstr/CBotLeftExpr.h @@ -61,6 +61,8 @@ public: */ bool Execute(CBotStack* &pStack, CBotStack* array); + using CBotInstr::Execute; + /*! * \brief ExecuteVar Fetch a variable during compilation. * \param pVar @@ -69,6 +71,8 @@ public: */ bool ExecuteVar(CBotVar* &pVar, CBotCStack* &pile) override; + using CBotInstr::ExecuteVar; + /*! * \brief ExecuteVar Fetch the variable at runtume. * \param pVar diff --git a/src/CBot/CBotInstr/CBotListInstr.cpp b/src/CBot/CBotInstr/CBotListInstr.cpp index 58413a42..bcbd3bba 100644 --- a/src/CBot/CBotInstr/CBotListInstr.cpp +++ b/src/CBot/CBotInstr/CBotListInstr.cpp @@ -52,7 +52,7 @@ CBotInstr* CBotListInstr::Compile(CBotToken* &p, CBotCStack* pStack, bool bLocal if (IsOfType(p, ID_SEP)) continue; // empty statement ignored if (p->GetType() == ID_CLBLK) break; - if (IsOfType(p, 0)) + if (p->GetType() == TokenTypNone) { pStack->SetError(CBotErrCloseBlock, p->GetStart()); delete inst; @@ -117,6 +117,12 @@ void CBotListInstr::RestoreState(CBotStack* &pj, bool bMain) if (p != nullptr) p->RestoreState(pile, true); } +bool CBotListInstr::HasReturn() +{ + if (m_instr != nullptr && m_instr->HasReturn()) return true; + return CBotInstr::HasReturn(); // check next block or instruction +} + std::map CBotListInstr::GetDebugLinks() { auto links = CBotInstr::GetDebugLinks(); diff --git a/src/CBot/CBotInstr/CBotListInstr.h b/src/CBot/CBotInstr/CBotListInstr.h index e0923e04..659784f6 100644 --- a/src/CBot/CBotInstr/CBotListInstr.h +++ b/src/CBot/CBotInstr/CBotListInstr.h @@ -56,6 +56,13 @@ public: */ void RestoreState(CBotStack* &pj, bool bMain) override; + /** + * \brief Check this block of instructions for a return statement. + * If not found, the next block or instruction is checked. + * \return true if a return statement was found. + */ + bool HasReturn() override; + protected: virtual const std::string GetDebugName() override { return "CBotListInstr"; } virtual std::map GetDebugLinks() override; diff --git a/src/CBot/CBotInstr/CBotNew.cpp b/src/CBot/CBotInstr/CBotNew.cpp index e7d973c9..1c84dc53 100644 --- a/src/CBot/CBotInstr/CBotNew.cpp +++ b/src/CBot/CBotInstr/CBotNew.cpp @@ -36,12 +36,15 @@ namespace CBot CBotNew::CBotNew() { m_parameters = nullptr; + m_exprRetVar = nullptr; m_nMethodeIdent = 0; } //////////////////////////////////////////////////////////////////////////////// CBotNew::~CBotNew() { + delete m_parameters; + delete m_exprRetVar; } //////////////////////////////////////////////////////////////////////////////// @@ -83,7 +86,7 @@ CBotInstr* CBotNew::Compile(CBotToken* &p, CBotCStack* pStack) if (!pStk->IsOk()) goto error; // constructor exist? - CBotTypResult r = pClass->CompileMethode(pClass->GetName(), pVar, ppVars, pStk, inst->m_nMethodeIdent); + CBotTypResult r = pClass->CompileMethode(&inst->m_vartoken, pVar, ppVars, pStk, inst->m_nMethodeIdent); delete pStk->TokenStack(); // release extra stack int typ = r.GetType(); @@ -197,12 +200,7 @@ bool CBotNew::Execute(CBotStack* &pj) } ppVars[i] = nullptr; - // create a variable for the result - CBotVar* pResult = nullptr; // constructos still void - - if ( !pClass->ExecuteMethode(m_nMethodeIdent, pClass->GetName(), - pThis, ppVars, - pResult, pile2, GetToken())) return false; // interrupt + if ( !pClass->ExecuteMethode(m_nMethodeIdent, pThis, ppVars, CBotTypResult(CBotTypVoid), pile2, &m_vartoken)) return false; // interrupt pThis->ConstructorSet(); // indicates that the constructor has been called } @@ -284,8 +282,7 @@ void CBotNew::RestoreState(CBotStack* &pj, bool bMain) } ppVars[i] = nullptr; - pClass->RestoreMethode(m_nMethodeIdent, m_vartoken.GetString(), pThis, - ppVars, pile2) ; // interrupt here! + pClass->RestoreMethode(m_nMethodeIdent, &m_vartoken, pThis, ppVars, pile2); // interrupt here! } } diff --git a/src/CBot/CBotInstr/CBotParExpr.cpp b/src/CBot/CBotInstr/CBotParExpr.cpp index 714e68e4..0a608c32 100644 --- a/src/CBot/CBotInstr/CBotParExpr.cpp +++ b/src/CBot/CBotInstr/CBotParExpr.cpp @@ -90,17 +90,16 @@ CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack) // post incremented or decremented? if (IsOfType(p, ID_INC, ID_DEC)) { + // recompile the variable for read-only + delete inst; + p = pvar; + inst = CBotExprVar::Compile(p, pStk, true); if (pStk->GetType() >= CBotTypBoolean) { pStk->SetError(CBotErrBadType1, pp); delete inst; return pStack->Return(nullptr, pStk); } - - // recompile the variable for read-only - delete inst; - p = pvar; - inst = CBotExprVar::Compile(p, pStk, CBotVar::ProtectionLevel::ReadOnly); p = p->GetNext(); CBotPostIncExpr* i = new CBotPostIncExpr(); @@ -115,26 +114,39 @@ CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack) CBotToken* pp = p; if (IsOfType(p, ID_INC, ID_DEC)) { - CBotPreIncExpr* i = new CBotPreIncExpr(); - i->SetToken(pp); - if (p->GetType() == TokenTypVar) { - if (nullptr != (i->m_instr = CBotExprVar::Compile(p, pStk, CBotVar::ProtectionLevel::ReadOnly))) + if (nullptr != (inst = CBotExprVar::Compile(p, pStk, true))) { - if (pStk->GetType() >= CBotTypBoolean) + if (pStk->GetType() < CBotTypBoolean) // a number ? { - pStk->SetError(CBotErrBadType1, pp); - delete inst; - return pStack->Return(nullptr, pStk); + CBotPreIncExpr* i = new CBotPreIncExpr(); + i->SetToken(pp); + i->m_instr = inst; + return pStack->Return(i, pStk); } - return pStack->Return(i, pStk); + delete inst; } - delete i; - return pStack->Return(nullptr, pStk); } + pStk->SetError(CBotErrBadType1, pp); + return pStack->Return(nullptr, pStk); } + return CBotParExpr::CompileLitExpr(p, pStack); +} + +//////////////////////////////////////////////////////////////////////////////// +CBotInstr* CBotParExpr::CompileLitExpr(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); + + CBotToken* pp = p; + + // is this a unary operation? + CBotInstr* inst = CBotExprUnaire::Compile(p, pStk, true); + if (inst != nullptr || !pStk->IsOk()) + return pStack->Return(inst, pStk); + // is it a number or DefineNum? if (p->GetType() == TokenTypNum || p->GetType() == TokenTypDef ) diff --git a/src/CBot/CBotInstr/CBotParExpr.h b/src/CBot/CBotInstr/CBotParExpr.h index cd92467b..0bcdd0e1 100644 --- a/src/CBot/CBotInstr/CBotParExpr.h +++ b/src/CBot/CBotInstr/CBotParExpr.h @@ -54,6 +54,14 @@ public: */ static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + /*! + * \brief Compile a literal expression ("string", number, true, false, null, nan, new) + * \param p[in, out] Pointer to first token of the expression, will be updated to point to first token after the expression + * \param pStack Current compilation stack frame + * \return The compiled instruction or nullptr on error + */ + static CBotInstr* CompileLitExpr(CBotToken* &p, CBotCStack* pStack); + private: CBotParExpr() = delete; CBotParExpr(const CBotParExpr&) = delete; diff --git a/src/CBot/CBotInstr/CBotReturn.cpp b/src/CBot/CBotInstr/CBotReturn.cpp index acbd3d8e..5979989e 100644 --- a/src/CBot/CBotInstr/CBotReturn.cpp +++ b/src/CBot/CBotInstr/CBotReturn.cpp @@ -111,6 +111,11 @@ void CBotReturn::RestoreState(CBotStack* &pj, bool bMain) } } +bool CBotReturn::HasReturn() +{ + return true; +} + std::map CBotReturn::GetDebugLinks() { auto links = CBotInstr::GetDebugLinks(); diff --git a/src/CBot/CBotInstr/CBotReturn.h b/src/CBot/CBotInstr/CBotReturn.h index 237571a3..9d14a3e9 100644 --- a/src/CBot/CBotInstr/CBotReturn.h +++ b/src/CBot/CBotInstr/CBotReturn.h @@ -55,6 +55,12 @@ public: */ void RestoreState(CBotStack* &pj, bool bMain) override; + /*! + * \brief Always returns true. + * \return true to signal a return statment has been found. + */ + bool HasReturn() override; + protected: virtual const std::string GetDebugName() override { return "CBotReturn"; } virtual std::map GetDebugLinks() override; diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index 17c5ec0a..a3a216a2 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -30,6 +30,8 @@ #include "CBot/stdlib/stdlib.h" +#include + namespace CBot { @@ -47,27 +49,30 @@ CBotProgram::CBotProgram(CBotVar* thisVar) CBotProgram::~CBotProgram() { // delete m_classes; - if (m_classes != nullptr) m_classes->Purge(); - m_classes = nullptr; + for (CBotClass* c : m_classes) + c->Purge(); + m_classes.clear(); CBotClass::FreeLock(this); - delete m_functions; - if (m_stack != nullptr) m_stack->Delete(); + for (CBotFunction* f : m_functions) delete f; + m_functions.clear(); } -bool CBotProgram::Compile(const std::string& program, std::vector& functions, void* pUser) +bool CBotProgram::Compile(const std::string& program, std::vector& externFunctions, void* pUser) { // Cleanup the previously compiled program Stop(); -// delete m_classes; - if (m_classes != nullptr) m_classes->Purge(); // purge the old definitions of classes - // but without destroying the object - m_classes = nullptr; - delete m_functions; m_functions = nullptr; + for (CBotClass* c : m_classes) + c->Purge(); // purge the old definitions of classes + // but without destroying the object - functions.clear(); + m_classes.clear(); + for (CBotFunction* f : m_functions) delete f; + m_functions.clear(); + + externFunctions.clear(); m_error = CBotNoErr; // Step 1. Process the code into tokens @@ -88,15 +93,15 @@ bool CBotProgram::Compile(const std::string& program, std::vector& if ( p->GetType() == ID_CLASS || ( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS )) { - CBotClass* nxt = CBotClass::Compile1(p, pStack.get()); - if (m_classes == nullptr ) m_classes = nxt; - else m_classes->AddNext(nxt); + CBotClass* newclass = CBotClass::Compile1(p, pStack.get()); + if (newclass != nullptr) + m_classes.push_back(newclass); } else { - CBotFunction* next = CBotFunction::Compile1(p, pStack.get(), nullptr); - if (m_functions == nullptr ) m_functions = next; - else m_functions->AddNext(next); + CBotFunction* newfunc = CBotFunction::Compile1(p, pStack.get(), nullptr); + if (newfunc != nullptr) + m_functions.push_back(newfunc); } } @@ -106,17 +111,14 @@ bool CBotProgram::Compile(const std::string& program, std::vector& if ( !pStack->IsOk() ) { m_error = pStack->GetError(m_errorStart, m_errorEnd); - delete m_functions; - m_functions = nullptr; + for (CBotFunction* f : m_functions) delete f; + m_functions.clear(); return false; } // Step 3. Real compilation -// CBotFunction* temp = nullptr; - CBotFunction* next = m_functions; // rewind the list - + std::list::iterator next = m_functions.begin(); p = tokens.get()->GetNext(); // returns to the beginning - while ( pStack->IsOk() && p != nullptr && p->GetType() != 0 ) { if ( IsOfType(p, ID_SEP) ) continue; // semicolons lurking @@ -128,43 +130,35 @@ bool CBotProgram::Compile(const std::string& program, std::vector& } else { - CBotFunction::Compile(p, pStack.get(), next); - if (next->IsExtern()) functions.push_back(next->GetName()/* + next->GetParams()*/); - if (next->IsPublic()) CBotFunction::AddPublic(next); - next->m_pProg = this; // keeps pointers to the module - next = next->Next(); + CBotFunction::Compile(p, pStack.get(), *next); + if ((*next)->IsExtern()) externFunctions.push_back((*next)->GetName()/* + next->GetParams()*/); + if ((*next)->IsPublic()) CBotFunction::AddPublic(*next); + (*next)->m_pProg = this; // keeps pointers to the module + ++next; } } -// delete m_Prog; // the list of first pass -// m_Prog = temp; // list of the second pass - if ( !pStack->IsOk() ) { m_error = pStack->GetError(m_errorStart, m_errorEnd); - delete m_functions; - m_functions = nullptr; + for (CBotFunction* f : m_functions) delete f; + m_functions.clear(); } - return (m_functions != nullptr); + return !m_functions.empty(); } bool CBotProgram::Start(const std::string& name) { Stop(); - m_entryPoint = m_functions; - while (m_entryPoint != nullptr) - { - if (m_entryPoint->GetName() == name ) break; - m_entryPoint = m_entryPoint->m_next; - } - - if (m_entryPoint == nullptr) + auto it = std::find_if(m_functions.begin(), m_functions.end(), [&name](CBotFunction* x) { return x->GetName() == name; }); + if (it == m_functions.end()) { m_error = CBotErrNoRun; return false; } + m_entryPoint = *it; m_stack = CBotStack::AllocateStack(); m_stack->SetProgram(this); @@ -174,16 +168,10 @@ bool CBotProgram::Start(const std::string& name) bool CBotProgram::GetPosition(const std::string& name, int& start, int& stop, CBotGet modestart, CBotGet modestop) { - CBotFunction* p = m_functions; - while (p != nullptr) - { - if ( p->GetName() == name ) break; - p = p->m_next; - } + auto it = std::find_if(m_functions.begin(), m_functions.end(), [&name](CBotFunction* x) { return x->GetName() == name; }); + if (it == m_functions.end()) return false; - if ( p == nullptr ) return false; - - p->GetPosition(start, stop, modestart, modestop); + (*it)->GetPosition(start, stop, modestart, modestop); return true; } @@ -212,16 +200,16 @@ bool CBotProgram::Run(void* pUser, int timer) } // completed on a mistake? - if (!ok && !m_stack->IsOk()) + if (ok || !m_stack->IsOk()) { m_error = m_stack->GetError(m_errorStart, m_errorEnd); m_stack->Delete(); m_stack = nullptr; CBotClass::FreeLock(this); + m_entryPoint = nullptr; return true; // execution is finished! } - if ( ok ) m_entryPoint = nullptr; // more function in execution return ok; } @@ -288,11 +276,21 @@ bool CBotProgram::GetError(CBotError& code, int& start, int& end, CBotProgram*& } //////////////////////////////////////////////////////////////////////////////// -CBotFunction* CBotProgram::GetFunctions() +const std::list& CBotProgram::GetFunctions() { return m_functions; } +bool CBotProgram::ClassExists(std::string name) +{ + for (CBotClass* p : m_classes) + { + if ( p->GetName() == name ) return true; + } + + return false; +} + //////////////////////////////////////////////////////////////////////////////// CBotTypResult cSizeOf( CBotVar* &pVar, void* pUser ) { @@ -304,7 +302,7 @@ CBotTypResult cSizeOf( CBotVar* &pVar, void* pUser ) bool rSizeOf( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) { - if ( pVar == nullptr ) return CBotErrLowParam; + if ( pVar == nullptr ) { ex = CBotErrLowParam; return true; } int i = 0; pVar = pVar->GetItemList(); @@ -375,8 +373,7 @@ bool CBotProgram::RestoreState(FILE* pf) } // retrieves the stack from the memory - // uses a nullptr pointer (m_stack) but it's ok like that - // TODO: no it's not okay like that! but it looks like it doesn't get optimized out at least ~krzys_h + m_stack = CBotStack::AllocateStack(); if (!m_stack->RestoreState(pf, m_stack)) return false; m_stack->SetProgram(this); // bases for routines diff --git a/src/CBot/CBotProgram.h b/src/CBot/CBotProgram.h index 165defc1..4a64b562 100644 --- a/src/CBot/CBotProgram.h +++ b/src/CBot/CBotProgram.h @@ -23,6 +23,7 @@ #include "CBot/CBotEnums.h" #include +#include namespace CBot { @@ -124,12 +125,12 @@ public: * 3. Second pass - compiling definitions of all functions and classes * * \param program Code to compile - * \param[out] functions Returns the names of functions declared as extern + * \param[out] externFunctions Returns the names of functions declared as extern * \param pUser Optional pointer to be passed to compile function (see AddFunction()) * \return true if compilation is successful, false if an compilation error occurs * \see GetError() to retrieve the error */ - bool Compile(const std::string& program, std::vector& functions, void* pUser = nullptr); + bool Compile(const std::string& program, std::vector& externFunctions, void* pUser = nullptr); /** * \brief Returns the last error @@ -328,9 +329,15 @@ public: * * This list includes all the functions (not only extern) * - * \return Linked list of CBotFunction instances + * \return List of CBotFunction instances */ - CBotFunction* GetFunctions(); + const std::list& GetFunctions(); + + /** + * \brief Check if class with that name was created in this program + * \return True if class was defined in this program, otherwise, false + */ + bool ClassExists(std::string name); /** * \brief Returns static list of all registered external calls @@ -341,11 +348,11 @@ private: //! All external calls static CBotExternalCallList* m_externalCalls; //! All user-defined functions - CBotFunction* m_functions = nullptr; + std::list m_functions{}; //! The entry point function CBotFunction* m_entryPoint = nullptr; //! Classes defined in this program - CBotClass* m_classes = nullptr; + std::list m_classes{}; //! Execution stack CBotStack* m_stack = nullptr; //! "this" variable diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp index 94697b4a..57aa4290 100644 --- a/src/CBot/CBotStack.cpp +++ b/src/CBot/CBotStack.cpp @@ -82,8 +82,6 @@ CBotStack* CBotStack::AllocateStack() //////////////////////////////////////////////////////////////////////////////// void CBotStack::Delete() { - assert ( this != nullptr ); - if (m_next != nullptr) m_next->Delete(); if (m_next2 != nullptr) m_next2->Delete(); @@ -192,8 +190,18 @@ bool CBotStack::Return(CBotStack* pfils) m_var = pfils->m_var; // result transmitted pfils->m_var = nullptr; // not to destroy the variable - if (m_next != nullptr) m_next->Delete();m_next = nullptr; // releases the stack above - if (m_next2 != nullptr) m_next2->Delete();m_next2 = nullptr; // also the second stack (catch) + if (m_next != nullptr) + { + // releases the stack above + m_next->Delete(); + m_next = nullptr; + } + if (m_next2 != nullptr) + { + // also the second stack (catch) + m_next2->Delete(); + m_next2 = nullptr; + } return IsOk(); // interrupted if error } @@ -260,7 +268,7 @@ bool CBotStack::IfStep() bool CBotStack::BreakReturn(CBotStack* pfils, const std::string& name) { if ( m_error>=0 ) return false; // normal output - if ( m_error==-3 ) return false; // normal output (return current) + if ( m_error==CBotError(-3) ) return false; // normal output (return current) if (!m_labelBreak.empty() && (name.empty() || m_labelBreak != name)) return false; // it's not for me @@ -273,7 +281,7 @@ bool CBotStack::BreakReturn(CBotStack* pfils, const std::string& name) //////////////////////////////////////////////////////////////////////////////// bool CBotStack::IfContinue(int state, const std::string& name) { - if ( m_error != -2 ) return false; + if ( m_error != CBotError(-2) ) return false; if (!m_labelBreak.empty() && (name.empty() || m_labelBreak != name)) return false; // it's not for me @@ -301,7 +309,7 @@ void CBotStack::SetBreak(int val, const std::string& name) //////////////////////////////////////////////////////////////////////////////// bool CBotStack::GetRetVar(bool bRet) { - if (m_error == -3) + if (m_error == CBotError(-3)) { if ( m_var ) delete m_var; m_var = m_retvar; @@ -568,7 +576,7 @@ bool CBotStack::ExecuteCall(long& nIdent, CBotToken* token, CBotVar** ppVar, con res = m_prog->GetExternalCalls()->DoCall(nullptr, nullptr, ppVar, this, rettype); if (res >= 0) return res; - res = m_prog->GetFunctions()->DoCall(nIdent, "", ppVar, this, token ); + res = CBotFunction::DoCall(m_prog, m_prog->GetFunctions(), nIdent, "", ppVar, this, token); if (res >= 0) return res; // if not found (recompile?) seeks by name @@ -577,7 +585,7 @@ bool CBotStack::ExecuteCall(long& nIdent, CBotToken* token, CBotVar** ppVar, con res = m_prog->GetExternalCalls()->DoCall(token, nullptr, ppVar, this, rettype); if (res >= 0) return res; - res = m_prog->GetFunctions()->DoCall(nIdent, token->GetString(), ppVar, this, token ); + res = CBotFunction::DoCall(m_prog, m_prog->GetFunctions(), nIdent, token->GetString(), ppVar, this, token); if (res >= 0) return res; SetError(CBotErrUndefFunc, token); @@ -592,7 +600,7 @@ void CBotStack::RestoreCall(long& nIdent, CBotToken* token, CBotVar** ppVar) if (m_prog->GetExternalCalls()->RestoreCall(token, nullptr, ppVar, this)) return; - m_prog->GetFunctions()->RestoreCall(nIdent, token->GetString(), ppVar, this); + CBotFunction::RestoreCall(m_prog->GetFunctions(), nIdent, token->GetString(), ppVar, this); } //////////////////////////////////////////////////////////////////////////////// @@ -727,12 +735,11 @@ bool CBotStack::RestoreState(FILE* pf, CBotStack* &pStack) { unsigned short w; - pStack = nullptr; + if (pStack != this) pStack = nullptr; if (!ReadWord(pf, w)) return false; if ( w == 0 ) return true; // 0 - terminator - if ( this == nullptr ) pStack = AllocateStack(); - else pStack = AddStack(); + if (pStack == nullptr) pStack = AddStack(); if ( w == 2 ) // 2 - m_next2 { diff --git a/src/CBot/CBotToken.cpp b/src/CBot/CBotToken.cpp index 890c038a..d9e33ffd 100644 --- a/src/CBot/CBotToken.cpp +++ b/src/CBot/CBotToken.cpp @@ -199,7 +199,6 @@ const CBotToken& CBotToken::operator=(const CBotToken& src) //////////////////////////////////////////////////////////////////////////////// int CBotToken::GetType() { - if (this == nullptr) return 0; if (m_type == TokenTypKeyWord) return m_keywordId; return m_type; } @@ -225,14 +224,12 @@ void CBotToken::SetString(const std::string& name) //////////////////////////////////////////////////////////////////////////////// int CBotToken::GetStart() { - if (this == nullptr) return -1; return m_start; } //////////////////////////////////////////////////////////////////////////////// int CBotToken::GetEnd() { - if (this == nullptr) return -1; return m_end; } @@ -439,6 +436,13 @@ std::unique_ptr CBotToken::CompileTokens(const std::string& program) pp = p; } + // terminator token + nxt = new CBotToken(); + nxt->m_type = TokenTypNone; + nxt->m_end = nxt->m_start = pos; + prv->m_next = nxt; + nxt->m_prev = prv; + return std::unique_ptr(tokenbase); } diff --git a/src/CBot/CBotTypResult.cpp b/src/CBot/CBotTypResult.cpp index 46d42eef..d7005c2a 100644 --- a/src/CBot/CBotTypResult.cpp +++ b/src/CBot/CBotTypResult.cpp @@ -167,11 +167,16 @@ CBotTypResult& CBotTypResult::operator=(const CBotTypResult& src) m_type = src.m_type; m_limite = src.m_limite; m_class = src.m_class; - m_next = nullptr; if (src.m_next != nullptr ) { + delete m_next; m_next = new CBotTypResult(*src.m_next); } + else + { + delete m_next; + m_next = nullptr; + } return *this; } diff --git a/src/CBot/CBotVar/CBotVar.cpp b/src/CBot/CBotVar/CBotVar.cpp index 4e88cd69..7ea659f5 100644 --- a/src/CBot/CBotVar/CBotVar.cpp +++ b/src/CBot/CBotVar/CBotVar.cpp @@ -22,6 +22,7 @@ #include "CBot/CBotStack.h" +#include "CBot/CBotInstr/CBotInstr.h" #include "CBot/CBotVar/CBotVarArray.h" #include "CBot/CBotVar/CBotVarPointer.h" #include "CBot/CBotVar/CBotVarClass.h" @@ -70,6 +71,8 @@ CBotVar::CBotVar(const CBotToken &name) : CBotVar() CBotVar::~CBotVar( ) { delete m_token; + delete m_InitExpr; + delete m_LimExpr; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/CBot/CBotVar/CBotVarClass.cpp b/src/CBot/CBotVar/CBotVarClass.cpp index 4da99b2d..de3d274f 100644 --- a/src/CBot/CBotVar/CBotVarClass.cpp +++ b/src/CBot/CBotVar/CBotVarClass.cpp @@ -394,12 +394,13 @@ void CBotVarClass::DecrementUse() CBotVar* pThis = CBotVar::Create("this", CBotTypNullPointer); pThis->SetPointer(this); - CBotVar* pResult = nullptr; std::string nom = std::string("~") + m_pClass->GetName(); long ident = 0; - while ( pile->IsOk() && !m_pClass->ExecuteMethode(ident, nom, pThis, ppVars, pResult, pile, nullptr)) ; // waits for the end + CBotToken token(nom); // TODO + + while ( pile->IsOk() && !m_pClass->ExecuteMethode(ident, pThis, ppVars, CBotTypResult(CBotTypVoid), pile, &token)) ; // waits for the end pile->ResetError(err, start,end); diff --git a/src/CBot/CBotVar/CBotVarString.h b/src/CBot/CBotVar/CBotVarString.h index 162264d2..28d3018d 100644 --- a/src/CBot/CBotVar/CBotVarString.h +++ b/src/CBot/CBotVar/CBotVarString.h @@ -48,12 +48,12 @@ public: SetValString(ToString(val)); } - int GetValInt() + int GetValInt() override { return FromString(GetValString()); } - float GetValFloat() + float GetValFloat() override { return FromString(GetValString()); } diff --git a/src/CBot/CMakeLists.txt b/src/CBot/CMakeLists.txt index 6ac4d130..dcb39e1a 100644 --- a/src/CBot/CMakeLists.txt +++ b/src/CBot/CMakeLists.txt @@ -2,8 +2,6 @@ set(SOURCES CBot.h CBotCStack.cpp CBotCStack.h - CBotCallMethode.cpp - CBotCallMethode.h CBotClass.cpp CBotClass.h CBotDebug.cpp diff --git a/src/CBot/stdlib/FileFunctions.cpp b/src/CBot/stdlib/FileFunctions.cpp index 4f2f6134..57d778ad 100644 --- a/src/CBot/stdlib/FileFunctions.cpp +++ b/src/CBot/stdlib/FileFunctions.cpp @@ -38,7 +38,7 @@ int g_nextFileId = 1; bool FileClassOpenFile(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) { - std::string mode; + CBotFileAccessHandler::OpenMode openMode = CBotFileAccessHandler::OpenMode::Read; // must be a character string if ( pVar->GetType() != CBotTypString ) { Exception = CBotErrBadString; return false; } @@ -50,8 +50,11 @@ bool FileClassOpenFile(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exc if ( pVar != nullptr ) { // recover mode - mode = pVar->GetValString(); - if ( mode != "r" && mode != "w" && mode != "a" ) { Exception = CBotErrBadParam; return false; } + std::string mode = pVar->GetValString(); + if ( mode == "r" ) openMode = CBotFileAccessHandler::OpenMode::Read; + else if ( mode == "w" ) openMode = CBotFileAccessHandler::OpenMode::Write; + else if ( mode == "a" ) openMode = CBotFileAccessHandler::OpenMode::Append; + else { Exception = CBotErrBadParam; return false; } // no third parameter if ( pVar->GetNext() != nullptr ) { Exception = CBotErrOverParam; return false; } @@ -66,27 +69,20 @@ bool FileClassOpenFile(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exc // which must not be initialized if ( pVar->IsDefined()) { Exception = CBotErrFileOpen; return false; } - if ( !mode.empty() ) - { - // opens the requested file - assert(g_fileHandler != nullptr); + // opens the requested file + assert(g_fileHandler != nullptr); - CBotFileAccessHandler::OpenMode openMode; - if ( mode == "r" ) openMode = CBotFileAccessHandler::OpenMode::Read; - else if ( mode == "w" ) openMode = CBotFileAccessHandler::OpenMode::Write; - else if ( mode == "a" ) openMode = CBotFileAccessHandler::OpenMode::Append; + std::unique_ptr file = g_fileHandler->OpenFile(filename, openMode); - std::unique_ptr file = g_fileHandler->OpenFile(filename, openMode); + if (!file->Opened()) { Exception = CBotErrFileOpen; return false; } - if (!file->Opened()) { Exception = CBotErrFileOpen; return false; } + int fileHandle = g_nextFileId++; + g_files[fileHandle] = std::move(file); - int fileHandle = g_nextFileId++; - g_files[fileHandle] = std::move(file); + // save the file handle + pVar = pThis->GetItem("handle"); + pVar->SetValInt(fileHandle); - // save the file handle - pVar = pThis->GetItem("handle"); - pVar->SetValInt(fileHandle); - } return true; } diff --git a/src/CBot/stdlib/StringFunctions.cpp b/src/CBot/stdlib/StringFunctions.cpp index c50c39d4..77a359cb 100644 --- a/src/CBot/stdlib/StringFunctions.cpp +++ b/src/CBot/stdlib/StringFunctions.cpp @@ -225,8 +225,14 @@ bool rStrFind( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) // puts the result on the stack std::size_t res = s.find(s2); - pResult->SetValInt( res != std::string::npos ? res : -1 ); - if ( res < 0 ) pResult->SetInit( CBotVar::InitType::IS_NAN ); + if (res != std::string::npos) + { + pResult->SetValInt(res); + } + else + { + pResult->SetInit(CBotVar::InitType::IS_NAN); + } return true; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 957cb70d..e0857506 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -165,6 +165,8 @@ set(BASE_SOURCES graphics/core/nulldevice.cpp graphics/core/nulldevice.h graphics/core/texture.h + graphics/core/type.cpp + graphics/core/type.h graphics/core/vertex.h graphics/engine/camera.cpp graphics/engine/camera.h @@ -239,10 +241,14 @@ set(BASE_SOURCES level/robotmain.h level/scene_conditions.cpp level/scene_conditions.h + level/scoreboard.cpp + level/scoreboard.h math/all.h math/const.h math/func.h math/geometry.h + math/half.cpp + math/half.h math/intpoint.h math/matrix.h math/point.h diff --git a/src/app/app.cpp b/src/app/app.cpp index 4ab6440e..f019ccc0 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -674,7 +674,8 @@ bool CApplication::Create() // Create the robot application. m_controller = MakeUnique(); - CThread musicLoadThread([this]() { + CThread musicLoadThread([this]() + { GetLogger()->Debug("Cache sounds...\n"); SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); m_systemUtils->GetCurrentTimeStamp(musicLoadStart); @@ -685,7 +686,8 @@ bool CApplication::Create() m_systemUtils->GetCurrentTimeStamp(musicLoadEnd); float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC); GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime); - }, "Sound loading thread"); + }, + "Sound loading thread"); musicLoadThread.Start(); if (m_runSceneCategory == LevelCategory::Max) @@ -1852,9 +1854,10 @@ bool CApplication::GetSceneTestMode() return m_sceneTest; } -void CApplication::SetTextInput(bool textInputEnabled) +void CApplication::SetTextInput(bool textInputEnabled, int id) { - if (textInputEnabled) + m_textInputEnabled[id] = textInputEnabled; + if (std::any_of(m_textInputEnabled.begin(), m_textInputEnabled.end(), [](std::pair v) { return v.second; })) { SDL_StartTextInput(); } diff --git a/src/app/app.h b/src/app/app.h index b6702907..20721e24 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -35,6 +35,7 @@ #include #include +#include class CEventQueue; @@ -245,7 +246,7 @@ public: //! Enable/disable text input, this toggles the on-screen keyboard on some platforms /** This also allows for writing in CJK languages (not tested!), see https://wiki.libsdl.org/Tutorials/TextInput for detailed explanation */ - void SetTextInput(bool textInputEnabled); + void SetTextInput(bool textInputEnabled, int id); //! Moves (warps) the mouse cursor to the specified position (in interface coords) void MoveMouse(Math::Point pos); @@ -403,4 +404,6 @@ protected: //! Static buffer for putenv locale static char m_languageLocale[50]; + + std::map m_textInputEnabled; }; diff --git a/src/app/signal_handlers.cpp b/src/app/signal_handlers.cpp index 223d7db8..b08a0a22 100644 --- a/src/app/signal_handlers.cpp +++ b/src/app/signal_handlers.cpp @@ -46,7 +46,7 @@ void CSignalHandlers::Init(CSystemUtils* systemUtils) void CSignalHandlers::SignalHandler(int sig) { - std::string signalStr = StrUtils::ToString(signal); + std::string signalStr = StrUtils::ToString(sig); switch(sig) { case SIGSEGV: signalStr = "SIGSEGV, segmentation fault"; break; diff --git a/src/common/error.h b/src/common/error.h index d1bdac2a..7522c01a 100644 --- a/src/common/error.h +++ b/src/common/error.h @@ -19,6 +19,10 @@ #pragma once +/** + * \file common/error.h + * \brief Definition of the Error enum + */ /** * \enum Error @@ -26,87 +30,88 @@ */ enum Error { - ERR_OK = 0, //! < ok - ERR_UNKNOWN = 1, //! < any error - ERR_CONTINUE = 2, //! < continues - ERR_STOP = 3, //! < stops - ERR_CMD = 4, //! < unknown command - ERR_MANIP_FLY = 101, //! < impossible in flight - ERR_MANIP_BUSY = 102, //! < taking: hands already occupied - ERR_MANIP_NIL = 103, //! < taking: nothing has to take - ERR_MANIP_MOTOR = 105, //! < busy: impossible to move - ERR_MANIP_OCC = 106, //! < busy: location already occupied - ERR_MANIP_RADIO = 108, //! < impossible because radioactive - ERR_MANIP_WATER = 109, //! < not possible under water - ERR_MANIP_EMPTY = 110, //! < nothing to deposit - ERR_BUILD_FLY = 120, //! < not possible in flight - ERR_BUILD_WATER = 121, //! < not possible under water - ERR_BUILD_METALAWAY = 123, //! < lack of metal (too far) - ERR_BUILD_METALNEAR = 124, //! < lack of metal (too close) - ERR_BUILD_METALINEX = 125, //! < lack of metal - ERR_BUILD_FLAT = 126, //! < not enough flat ground - ERR_BUILD_FLATLIT = 127, //! < not enough flat ground space - ERR_BUILD_BUSY = 128, //! < location occupied - ERR_BUILD_BASE = 129, //! < too close to the rocket - ERR_BUILD_NARROW = 130, //! < buildings too close - ERR_BUILD_MOTOR = 131, //! < built: not possible in movement - ERR_BUILD_DISABLED = 132, //! < built: can not produce this object in this mission - ERR_BUILD_RESEARCH = 133, //! < built: can not produce not researched object - ERR_SEARCH_FLY = 140, //! < not possible in flight - ERR_SEARCH_MOTOR = 142, //! < impossible in movement - ERR_TERRA_ENERGY = 151, //! < not enough energy - ERR_FIRE_ENERGY = 161, //! < not enough energy - ERR_RECOVER_ENERGY = 171, //! < not enough energy - ERR_RECOVER_NULL = 172, //! < lack of ruin - ERR_CONVERT_EMPTY = 180, //! < no stone was transformed - ERR_SHIELD_ENERGY = 191, //! < not enough energy - ERR_MOVE_IMPOSSIBLE = 200, //! < move impossible - ERR_GOTO_IMPOSSIBLE = 210, //! < goto impossible - ERR_GOTO_ITER = 211, //! < goto too complicated - ERR_GOTO_BUSY = 212, //! < goto destination occupied - ERR_DERRICK_NULL = 300, //! < no ore underground - ERR_STATION_NULL = 301, //! < no energy underground - ERR_TOWER_POWER = 310, //! < no battery - ERR_TOWER_ENERGY = 311, //! < more energy - ERR_RESEARCH_POWER = 320, //! < no battery - ERR_RESEARCH_ENERGY = 321, //! < more energy - ERR_RESEARCH_TYPE = 322, //! < the wrong type of battery - ERR_RESEARCH_ALREADY = 323, //! < research already done - ERR_ENERGY_NULL = 330, //! < no energy underground - ERR_ENERGY_LOW = 331, //! < not enough energy - ERR_ENERGY_EMPTY = 332, //! < lack of metal - ERR_ENERGY_BAD = 333, //! < transforms only the metal - ERR_BASE_DLOCK = 340, //! < doors locked - ERR_BASE_DHUMAN = 341, //! < you must be on spaceship - ERR_LABO_NULL = 350, //! < nothing to analyze - ERR_LABO_BAD = 351, //! < analyzes only organic ball - ERR_LABO_ALREADY = 352, //! < analysis already made - ERR_NUCLEAR_EMPTY = 362, //! < lack of uranium - ERR_NUCLEAR_BAD = 363, //! < transforms only uranium - ERR_FACTORY_NULL = 370, //! < no metal - ERR_FACTORY_NEAR = 371, //! < vehicle too close - ERR_INFO_NULL = 390, //! < no information terminal - ERR_VEH_VIRUS = 400, //! < vehicle infected by a virus - ERR_BAT_VIRUS = 401, //! < building infected by a virus - ERR_DESTROY_NOTFOUND = 410, //! < not found anything to destroy - ERR_WRONG_OBJ = 420, //! < inappropriate vehicle - ERR_VEH_POWER = 500, //! < no battery - ERR_VEH_ENERGY = 501, //! < more energy - ERR_FLAG_FLY = 510, //! < impossible in flight - ERR_FLAG_WATER = 511, //! < impossible during swimming - ERR_FLAG_MOTOR = 512, //! < impossible in movement - ERR_FLAG_BUSY = 513, //! < taking: already creating flag - ERR_FLAG_CREATE = 514, //! < too many flags - ERR_FLAG_PROXY = 515, //! < too close - ERR_FLAG_DELETE = 516, //! < nothing to remove - ERR_MISSION_NOTERM = 600, //! < Mission not completed - ERR_DELETEMOBILE = 700, //! < vehicle destroyed - ERR_DELETEBUILDING = 701, //! < building destroyed - ERR_ENEMY_OBJECT = 703, //! < can't control enemy object - ERR_OBLIGATORYTOKEN = 800, //! < compulsory instruction missing - ERR_PROHIBITEDTOKEN = 801, //! < instruction prohibited - ERR_AIM_IMPOSSIBLE = 900, //! < cannot aim at specified angle(s) - ERR_WRONG_BOT = 910, //! < inappropriate bot + ERR_OK = 0, //!< ok + ERR_UNKNOWN = 1, //!< any error + ERR_CONTINUE = 2, //!< continues + ERR_STOP = 3, //!< stops + ERR_CMD = 4, //!< unknown command + ERR_OBJ_BUSY = 5, //!< object is busy + ERR_MANIP_FLY = 101, //!< impossible in flight + ERR_MANIP_BUSY = 102, //!< taking: hands already occupied + ERR_MANIP_NIL = 103, //!< taking: nothing has to take + ERR_MANIP_MOTOR = 105, //!< busy: impossible to move + ERR_MANIP_OCC = 106, //!< busy: location already occupied + ERR_MANIP_RADIO = 108, //!< impossible because radioactive + ERR_MANIP_WATER = 109, //!< not possible under water + ERR_MANIP_EMPTY = 110, //!< nothing to deposit + ERR_BUILD_FLY = 120, //!< not possible in flight + ERR_BUILD_WATER = 121, //!< not possible under water + ERR_BUILD_METALAWAY = 123, //!< lack of metal (too far) + ERR_BUILD_METALNEAR = 124, //!< lack of metal (too close) + ERR_BUILD_METALINEX = 125, //!< lack of metal + ERR_BUILD_FLAT = 126, //!< not enough flat ground + ERR_BUILD_FLATLIT = 127, //!< not enough flat ground space + ERR_BUILD_BUSY = 128, //!< location occupied + ERR_BUILD_BASE = 129, //!< too close to the rocket + ERR_BUILD_NARROW = 130, //!< buildings too close + ERR_BUILD_MOTOR = 131, //!< built: not possible in movement + ERR_BUILD_DISABLED = 132, //!< built: can not produce this object in this mission + ERR_BUILD_RESEARCH = 133, //!< built: can not produce not researched object + ERR_SEARCH_FLY = 140, //!< not possible in flight + ERR_SEARCH_MOTOR = 142, //!< impossible in movement + ERR_TERRA_ENERGY = 151, //!< not enough energy + ERR_FIRE_ENERGY = 161, //!< not enough energy + ERR_RECOVER_ENERGY = 171, //!< not enough energy + ERR_RECOVER_NULL = 172, //!< lack of ruin + ERR_CONVERT_EMPTY = 180, //!< no stone was transformed + ERR_SHIELD_ENERGY = 191, //!< not enough energy + ERR_MOVE_IMPOSSIBLE = 200, //!< move impossible + ERR_GOTO_IMPOSSIBLE = 210, //!< goto impossible + ERR_GOTO_ITER = 211, //!< goto too complicated + ERR_GOTO_BUSY = 212, //!< goto destination occupied + ERR_DERRICK_NULL = 300, //!< no ore underground + ERR_STATION_NULL = 301, //!< no energy underground + ERR_TOWER_POWER = 310, //!< no battery + ERR_TOWER_ENERGY = 311, //!< more energy + ERR_RESEARCH_POWER = 320, //!< no battery + ERR_RESEARCH_ENERGY = 321, //!< more energy + ERR_RESEARCH_TYPE = 322, //!< the wrong type of battery + ERR_RESEARCH_ALREADY = 323, //!< research already done + ERR_ENERGY_NULL = 330, //!< no energy underground + ERR_ENERGY_LOW = 331, //!< not enough energy + ERR_ENERGY_EMPTY = 332, //!< lack of metal + ERR_ENERGY_BAD = 333, //!< transforms only the metal + ERR_BASE_DLOCK = 340, //!< doors locked + ERR_BASE_DHUMAN = 341, //!< you must be on spaceship + ERR_LABO_NULL = 350, //!< nothing to analyze + ERR_LABO_BAD = 351, //!< analyzes only organic ball + ERR_LABO_ALREADY = 352, //!< analysis already made + ERR_NUCLEAR_EMPTY = 362, //!< lack of uranium + ERR_NUCLEAR_BAD = 363, //!< transforms only uranium + ERR_FACTORY_NULL = 370, //!< no metal + ERR_FACTORY_NEAR = 371, //!< vehicle too close + ERR_INFO_NULL = 390, //!< no information terminal + ERR_VEH_VIRUS = 400, //!< vehicle infected by a virus + ERR_BAT_VIRUS = 401, //!< building infected by a virus + ERR_DESTROY_NOTFOUND = 410, //!< not found anything to destroy + ERR_WRONG_OBJ = 420, //!< inappropriate vehicle + ERR_VEH_POWER = 500, //!< no battery + ERR_VEH_ENERGY = 501, //!< more energy + ERR_FLAG_FLY = 510, //!< impossible in flight + ERR_FLAG_WATER = 511, //!< impossible during swimming + ERR_FLAG_MOTOR = 512, //!< impossible in movement + ERR_FLAG_BUSY = 513, //!< taking: already creating flag + ERR_FLAG_CREATE = 514, //!< too many flags + ERR_FLAG_PROXY = 515, //!< too close + ERR_FLAG_DELETE = 516, //!< nothing to remove + ERR_MISSION_NOTERM = 600, //!< Mission not completed + ERR_DELETEMOBILE = 700, //!< vehicle destroyed + ERR_DELETEBUILDING = 701, //!< building destroyed + ERR_ENEMY_OBJECT = 703, //!< can't control enemy object + ERR_OBLIGATORYTOKEN = 800, //!< compulsory instruction missing + ERR_PROHIBITEDTOKEN = 801, //!< instruction prohibited + ERR_AIM_IMPOSSIBLE = 900, //!< cannot aim at specified angle(s) + ERR_WRONG_BOT = 910, //!< inappropriate bot INFO_FIRST = 10000, //! < first information INFO_BUILD = 10001, //! < construction builded @@ -142,6 +147,9 @@ enum Error INFO_DELETEWORM = 10103, //! < insect killed INFO_DELETESPIDER = 10104, //! < insect killed INFO_BEGINSATCOM = 10105, //! < use your SatCom + INFO_TEAM_FINISH = 10110, + INFO_TEAM_DEAD = 10111, + INFO_TEAM_SCORE = 10112, ERR_MAX //! < number of values }; diff --git a/src/common/event.h b/src/common/event.h index 315a4b5b..27ab1141 100644 --- a/src/common/event.h +++ b/src/common/event.h @@ -172,7 +172,7 @@ enum EventType EVENT_LABEL16 = 106, EVENT_LABEL17 = 107, EVENT_LABEL18 = 108, - EVENT_LABEL19 = 109, + EVENT_LABEL19 = 109, // cursor position overlay EVENT_LIST0 = 110, EVENT_LIST1 = 111, @@ -187,6 +187,11 @@ enum EventType EVENT_LOADING = 120, + EVENT_LABEL_CODE_BATTLE = 121, + + EVENT_SCOREBOARD = 130, + EVENT_SCOREBOARD_MAX = 169, + EVENT_TOOLTIP = 200, EVENT_DIALOG_OK = 300, diff --git a/src/common/resources/outputstream.h b/src/common/resources/outputstream.h index c1ac33d4..1aeaf740 100644 --- a/src/common/resources/outputstream.h +++ b/src/common/resources/outputstream.h @@ -39,7 +39,7 @@ public: /** Construct and Open Stream for writing * * \param filename - * \param Mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file + * \param mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file * */ COutputStream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out); @@ -48,7 +48,7 @@ public: /** Open Stream for writing * * \param filename - * \param Mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file + * \param mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file * */ void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out); diff --git a/src/common/resources/outputstreambuffer.h b/src/common/resources/outputstreambuffer.h index fb7fa538..0945b303 100644 --- a/src/common/resources/outputstreambuffer.h +++ b/src/common/resources/outputstreambuffer.h @@ -38,7 +38,7 @@ public: /** Open Stream Buffer for writing * * \param filename - * \param Mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file + * \param mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file * */ void open(const std::string &filename, std::ios_base::openmode mode); diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 859a61f9..87812d66 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -143,8 +143,14 @@ void InitializeRestext() stringsText[RT_LOADING_TERRAIN_TEX] = TR("Textures"); stringsText[RT_LOADING_TERRAIN_GEN] = TR("Generating"); + stringsText[RT_SCOREBOARD_RESULTS] = TR("Results"); + stringsText[RT_SCOREBOARD_RESULTS_TEXT]= TR("The battle has ended"); + stringsText[RT_SCOREBOARD_RESULTS_LINE]= TR("%s: %d pts"); + + stringsEvent[EVENT_LABEL_CODE_BATTLE] = TR("Code battle"); + stringsEvent[EVENT_BUTTON_OK] = TR("OK"); stringsEvent[EVENT_BUTTON_CANCEL] = TR("Cancel"); stringsEvent[EVENT_BUTTON_NEXT] = TR("Next"); @@ -563,6 +569,7 @@ void InitializeRestext() stringsErr[ERR_UNKNOWN] = TR("Internal error - tell the developers"); stringsErr[ERR_CMD] = TR("Unknown command"); + stringsErr[ERR_OBJ_BUSY] = TR("This object is currently busy"); stringsErr[ERR_MANIP_FLY] = TR("Impossible when flying"); stringsErr[ERR_MANIP_BUSY] = TR("Already carrying something"); stringsErr[ERR_MANIP_NIL] = TR("Nothing to grab"); @@ -670,6 +677,9 @@ void InitializeRestext() stringsErr[INFO_DELETEWORM] = TR("Worm fatally wounded"); stringsErr[INFO_DELETESPIDER] = TR("Spider fatally wounded"); stringsErr[INFO_BEGINSATCOM] = TR("Press \\key help; to read instructions on your SatCom"); + stringsErr[INFO_TEAM_FINISH] = TR("<<< Team %s finished! >>>"); + stringsErr[INFO_TEAM_DEAD] = TR("<<< Team %s lost! >>>"); + stringsErr[INFO_TEAM_SCORE] = TR("<<< Team %s recieved %d points >>>"); @@ -718,6 +728,10 @@ void InitializeRestext() stringsCbot[CBot::CBotErrNoPublic] = TR("Public required"); stringsCbot[CBot::CBotErrNoExpression] = TR("Expression expected after ="); stringsCbot[CBot::CBotErrAmbiguousCall] = TR("Ambiguous call to overloaded function"); + stringsCbot[CBot::CBotErrFuncNotVoid] = TR("Function needs return type \"void\""); + 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::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); diff --git a/src/common/restext.h b/src/common/restext.h index b875f1c4..3c0af1a0 100644 --- a/src/common/restext.h +++ b/src/common/restext.h @@ -140,6 +140,10 @@ enum ResTextType RT_LOADING_TERRAIN_TEX = 222, RT_LOADING_TERRAIN_GEN = 223, + RT_SCOREBOARD_RESULTS = 230, + RT_SCOREBOARD_RESULTS_TEXT= 231, + RT_SCOREBOARD_RESULTS_LINE= 232, + RT_MAX //! < number of values }; diff --git a/src/common/system/system.cpp b/src/common/system/system.cpp index ce594ea7..ff4ad7c5 100644 --- a/src/common/system/system.cpp +++ b/src/common/system/system.cpp @@ -22,6 +22,8 @@ #include "common/config.h" +#include "common/make_unique.h" + #if defined(PLATFORM_WINDOWS) #include "common/system/system_windows.h" #elif defined(PLATFORM_LINUX) @@ -32,8 +34,6 @@ #include "common/system/system_other.h" #endif -#include "common/make_unique.h" - #include #include #include diff --git a/src/graphics/core/device.h b/src/graphics/core/device.h index b4e897db..f2aae7d3 100644 --- a/src/graphics/core/device.h +++ b/src/graphics/core/device.h @@ -26,6 +26,7 @@ #include "graphics/core/color.h" #include "graphics/core/texture.h" +#include "graphics/core/vertex.h" #include "math/intpoint.h" @@ -267,8 +268,10 @@ enum PrimitiveType PRIMITIVE_POINTS, PRIMITIVE_LINES, PRIMITIVE_LINE_STRIP, + PRIMITIVE_LINE_LOOP, PRIMITIVE_TRIANGLES, - PRIMITIVE_TRIANGLE_STRIP + PRIMITIVE_TRIANGLE_STRIP, + PRIMITIVE_TRIANGLE_FAN }; /** @@ -413,6 +416,14 @@ public: //! Sets only the texture wrap modes (for faster than thru stage params) virtual void SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode wrapT) = 0; + //! Renders primitive composed of generic vertices + virtual void DrawPrimitive(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int vertexCount) = 0; + + //! Renders multiple primitives composed of generic vertices + virtual void DrawPrimitives(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int first[], int count[], int drawCount) = 0; + //! Renders primitive composed of vertices with single texture virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices , int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) = 0; diff --git a/src/graphics/core/nulldevice.cpp b/src/graphics/core/nulldevice.cpp index aba9f800..9abdb156 100644 --- a/src/graphics/core/nulldevice.cpp +++ b/src/graphics/core/nulldevice.cpp @@ -168,6 +168,16 @@ void CNullDevice::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, i { } +void CNullDevice::DrawPrimitive(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int vertexCount) +{ +} + +void CNullDevice::DrawPrimitives(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int first[], int count[], int drawCount) +{ +} + void CNullDevice::DrawPrimitives(PrimitiveType type, const Vertex *vertices, int first[], int count[], int drawCount, Color color) { diff --git a/src/graphics/core/nulldevice.h b/src/graphics/core/nulldevice.h index 5721d920..39e3f995 100644 --- a/src/graphics/core/nulldevice.h +++ b/src/graphics/core/nulldevice.h @@ -81,6 +81,11 @@ public: void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT) override; + void DrawPrimitive(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int vertexCount) override; + void DrawPrimitives(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int first[], int count[], int drawCount) override; + void DrawPrimitive(PrimitiveType type, const Vertex* vertices, int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) override; void DrawPrimitive(PrimitiveType type, const VertexTex2* vertices, int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) override; void DrawPrimitive(PrimitiveType type, const VertexCol *vertices, int vertexCount) override; diff --git a/src/graphics/core/texture.h b/src/graphics/core/texture.h index 1ec8ef99..e092abb9 100644 --- a/src/graphics/core/texture.h +++ b/src/graphics/core/texture.h @@ -205,47 +205,6 @@ struct TextureStageParams } }; -/** -* \struct TexGenMode -* \brief Texture generation mode -*/ -enum TexGenMode -{ - //! No texture generation - TEX_GEN_NONE, - //! Object linear mode - TEX_GEN_OBJECT_LINEAR, - //! Eye linear mode - TEX_GEN_EYE_LINEAR, - //! Spherical mapping mode - TEX_GEN_SPHERE_MAP, - //! Normal mapping mode - TEX_GEN_NORMAL_MAP, - //! Reflection mapping mode - TEX_GEN_REFLECTION_MAP -}; - -/** -* \struct TextureGenerationParams -* \brief Parameters for texture coordinate generation -* -* These params define the generation of texture coordinate for given texture unit. -*/ -struct TextureGenerationParams -{ - struct Coord - { - TexGenMode mode = TEX_GEN_NONE; - float plane[4] = {}; - }; - Coord coords[4]; - - void LoadDefault() - { - *this = TextureGenerationParams(); - } -}; - /** * \struct Texture * \brief Info about a texture diff --git a/src/graphics/core/type.cpp b/src/graphics/core/type.cpp new file mode 100644 index 00000000..38dc9f52 --- /dev/null +++ b/src/graphics/core/type.cpp @@ -0,0 +1,56 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +/** +* \file graphics/core/type.cpp +* \brief Type support and conversion +*/ + +#include "graphics/core/type.h" + +#include + +// Graphics module namespace +namespace Gfx +{ + +//! Returns size in bytes of given type +int GetTypeSize(Type type) +{ + switch (type) + { + case Type::BYTE: + case Type::UBYTE: + return 1; + case Type::SHORT: + case Type::USHORT: + case Type::HALF: + return 2; + case Type::INT: + case Type::UINT: + case Type::FLOAT: + return 4; + case Type::DOUBLE: + return 8; + default: + return 0; + } +} + +} // namespace Gfx diff --git a/src/graphics/core/type.h b/src/graphics/core/type.h new file mode 100644 index 00000000..2d493459 --- /dev/null +++ b/src/graphics/core/type.h @@ -0,0 +1,62 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +/** +* \file graphics/core/type.h +* \brief Type support and conversion +*/ + +#pragma once + +// Graphics module namespace +namespace Gfx +{ + +/** +* \enum class Type +* \brief Value types for vertex attributes +*/ +enum class Type : unsigned char +{ + //! Unsigned byte (8-bit) + UBYTE = 0, + //! Signed byte (8-bit) + BYTE, + //! Unsigned short (16-bit) + USHORT, + //! Signed short (16-bit) + SHORT, + //! Unsigned int (32-bit) + UINT, + //! Signed int (32-bit) + INT, + //! Half precision floating-point (16-bit) + HALF, + //! Single precision floating-point (32-bit) + FLOAT, + //! Double precision floating-point (64-bit) + DOUBLE, +}; + +//! Returns size in bytes of given type +int GetTypeSize(Type type); + +// TODO: functions for conversion between types + +} // namespace Gfx diff --git a/src/graphics/core/vertex.h b/src/graphics/core/vertex.h index 8357cc84..254c8d3d 100644 --- a/src/graphics/core/vertex.h +++ b/src/graphics/core/vertex.h @@ -26,11 +26,13 @@ #include "graphics/core/color.h" +#include "graphics/core/type.h" #include "math/point.h" #include "math/vector.h" #include +#include // Graphics module namespace @@ -38,6 +40,58 @@ namespace Gfx { +/** +* \struct VertexAttribute +* \brief Vertex attribute +* +* This structure contains parameters for a vertex attribute. +*/ +struct VertexAttribute +{ + //! true enables vertex attribute + bool enabled = false; + //! true means normalized value (integer types only) + bool normalized = false; + //! Number of elements in the vertex attribute. + //! Valid values are 1, 2, 3, and 4. Depends on specific attribute. + unsigned char size = 0; + //! Type of values in vertex attribute + Type type = Type::UBYTE; + //! Offset to the vertex attribute + int offset = 0; + //! Stride of vertex attribute + int stride = 0; + //! Default values used when attribute is disabled + float values[4] = {0.0f, 0.0f, 0.0f, 0.0f}; +}; + +/** +* \struct VertexFormat +* \brief Vertex format +* +* This structure defines vertex formats for generic vertex arrays. +* +* It contains: +* - vertex coordinate specification +* - color specification +* - normal specification +* - texture coordinate 1 specification +* - texture coordinate 2 specification +*/ +struct VertexFormat +{ + //! Vertex coordinate + VertexAttribute vertex{}; + //! Color + VertexAttribute color{}; + //! Normal + VertexAttribute normal{}; + //! Texture coordinate 1 + VertexAttribute tex1{}; + //! Texture coordinate 2 + VertexAttribute tex2{}; +}; + /** * \struct Vertex * \brief Vertex of a primitive diff --git a/src/graphics/engine/cloud.cpp b/src/graphics/engine/cloud.cpp index 110f39b9..20cd6aa4 100644 --- a/src/graphics/engine/cloud.cpp +++ b/src/graphics/engine/cloud.cpp @@ -234,8 +234,6 @@ void CCloud::Create(const std::string& fileName, m_lines.clear(); for (int y = 0; y < m_brickCount; y++) CreateLine(0, y, m_brickCount); - - return; } void CCloud::Flush() diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 5c4a317a..943491b6 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -2194,7 +2194,7 @@ void CEngine::SetState(int state, const Color& color) m_device->SetTextureStageWrap(0, TEX_WRAP_REPEAT, TEX_WRAP_REPEAT); m_device->SetTextureStageWrap(1, TEX_WRAP_REPEAT, TEX_WRAP_REPEAT); } - else // if (state & ENG_RSTATE_CLAMP) or otherwise + else if (state & ENG_RSTATE_CLAMP) { m_device->SetTextureStageWrap(0, TEX_WRAP_CLAMP, TEX_WRAP_CLAMP); m_device->SetTextureStageWrap(1, TEX_WRAP_CLAMP, TEX_WRAP_CLAMP); @@ -3087,6 +3087,11 @@ const Math::Matrix& CEngine::GetMatView() return m_matView; } +const Math::Matrix& CEngine::GetMatProj() +{ + return m_matProj; +} + Math::Vector CEngine::GetEyePt() { return m_eyePt; @@ -3261,7 +3266,9 @@ void CEngine::Draw3DScene() m_device->SetTransform(TRANSFORM_PROJECTION, m_matProj); m_device->SetTransform(TRANSFORM_VIEW, m_matView); - m_water->DrawBack(); // draws water background + // TODO: This causes a rendering artifact and I can't see anything that breaks if you just comment it out + // So I'll just leave it like that for now ~krzys_h + //m_water->DrawBack(); // draws water background CProfiler::StartPerformanceCounter(PCNT_RENDER_TERRAIN); @@ -4262,15 +4269,15 @@ void CEngine::UpdateGroundSpotTextures() else intensity = Math::Point(ppx-cx, ppy-cy).Length()/dot; - Gfx::Color color; - color.r = Math::Norm(m_groundSpots[i].color.r+intensity); - color.g = Math::Norm(m_groundSpots[i].color.g+intensity); - color.b = Math::Norm(m_groundSpots[i].color.b+intensity); - ppx -= min.x; // on the texture ppy -= min.y; + Math::IntPoint pp(ppx, ppy); - shadowImg.SetPixel(Math::IntPoint(ppx, ppy), color); + Gfx::Color color = shadowImg.GetPixel(pp); + color.r *= Math::Norm(m_groundSpots[i].color.r+intensity); + color.g *= Math::Norm(m_groundSpots[i].color.g+intensity); + color.b *= Math::Norm(m_groundSpots[i].color.b+intensity); + shadowImg.SetPixel(pp, color); } } } @@ -4298,12 +4305,13 @@ void CEngine::UpdateGroundSpotTextures() if (intensity < 0.0f) intensity = 0.0f; - Gfx::Color color; - color.r = Math::Norm(m_groundSpots[i].color.r+intensity); - color.g = Math::Norm(m_groundSpots[i].color.g+intensity); - color.b = Math::Norm(m_groundSpots[i].color.b+intensity); + Math::IntPoint pp(ix, iy); - shadowImg.SetPixel(Math::IntPoint(ix, iy), color); + Gfx::Color color = shadowImg.GetPixel(pp); + color.r *= Math::Norm(m_groundSpots[i].color.r+intensity); + color.g *= Math::Norm(m_groundSpots[i].color.g+intensity); + color.b *= Math::Norm(m_groundSpots[i].color.b+intensity); + shadowImg.SetPixel(pp, color); } } } @@ -4351,19 +4359,21 @@ void CEngine::UpdateGroundSpotTextures() int j = (ix+dot) + (iy+dot) * m_groundMark.dx; if (m_groundMark.table[j] == 1) // green ? { - Gfx::Color color; - color.r = Math::Norm(1.0f-intensity); - color.g = 1.0f; - color.b = Math::Norm(1.0f-intensity); - shadowImg.SetPixel(Math::IntPoint(ppx, ppy), color); + Math::IntPoint pp(ppx, ppy); + Gfx::Color color = shadowImg.GetPixel(pp); + color.r *= Math::Norm(1.0f-intensity); + color.g *= 1.0f; + color.b *= Math::Norm(1.0f-intensity); + shadowImg.SetPixel(pp, color); } if (m_groundMark.table[j] == 2) // red ? { - Gfx::Color color; - color.r = 1.0f; - color.g = Math::Norm(1.0f-intensity); - color.b = Math::Norm(1.0f-intensity); - shadowImg.SetPixel(Math::IntPoint(ppx, ppy), color); + Math::IntPoint pp(ppx, ppy); + Gfx::Color color = shadowImg.GetPixel(pp); + color.r *= 1.0f; + color.g *= Math::Norm(1.0f-intensity); + color.b *= Math::Norm(1.0f-intensity); + shadowImg.SetPixel(pp, color); } } } @@ -4401,6 +4411,9 @@ void CEngine::UpdateGroundSpotTextures() { int px = x / 4.0f / 254.0f * size.x; int py = y / 4.0f / 254.0f * size.y; + // This can happen because the shadow??.png textures have a 1 pixel margin around them + if (px < 0 || px >= size.x || py < 0 || py >= size.y) + continue; shadowImg.SetPixelInt(Math::IntPoint(x-min.x, y-min.y), m_displayGotoImage->GetPixelInt(Math::IntPoint(px, py))); } } diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index e1dadf4d..834de1e3 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -1135,6 +1135,8 @@ public: //! Returns the view matrix const Math::Matrix& GetMatView(); + //! Returns the projection matrix + const Math::Matrix& GetMatProj(); //! Returns the camera center point TEST_VIRTUAL Math::Vector GetEyePt(); //! Returns the camera target point diff --git a/src/graphics/engine/lightning.cpp b/src/graphics/engine/lightning.cpp index 18192fe5..190f5db8 100644 --- a/src/graphics/engine/lightning.cpp +++ b/src/graphics/engine/lightning.cpp @@ -131,7 +131,7 @@ bool CLightning::EventFrame(const Event &event) else { assert(obj->Implements(ObjectInterfaceType::Destroyable)); - dynamic_cast(obj)->DamageObject(DamageType::Lightning); + dynamic_cast(obj)->DamageObject(DamageType::Lightning, std::numeric_limits::infinity()); } } diff --git a/src/graphics/engine/particle.cpp b/src/graphics/engine/particle.cpp index 5aaf45fa..f5c254e3 100644 --- a/src/graphics/engine/particle.cpp +++ b/src/graphics/engine/particle.cpp @@ -952,10 +952,9 @@ void CParticle::FrameParticle(float rTime) { CObject* object = SearchObjectGun(m_particle[i].goal, m_particle[i].pos, m_particle[i].type, m_particle[i].objFather); m_particle[i].goal = m_particle[i].pos; - if (object != nullptr) + if (object != nullptr && object->Implements(ObjectInterfaceType::Damageable)) { - assert(object->Implements(ObjectInterfaceType::Damageable)); - dynamic_cast(object)->DamageObject(DamageType::Phazer, 0.002f); + dynamic_cast(object)->DamageObject(DamageType::Phazer, 0.002f, m_particle[i].objFather); } m_particle[i].zoom = 1.0f-(m_particle[i].time-m_particle[i].duration); @@ -1107,7 +1106,7 @@ void CParticle::FrameParticle(float rTime) continue; } - if (m_particle[i].testTime >= 0.1f) + if (m_particle[i].testTime >= 0.05f) { m_particle[i].testTime = 0.0f; @@ -1156,8 +1155,10 @@ void CParticle::FrameParticle(float rTime) m_particle[i].goal = m_particle[i].pos; if (object != nullptr) { - assert(object->Implements(ObjectInterfaceType::Damageable)); - dynamic_cast(object)->DamageObject(DamageType::Fire, 0.002f); + if (object->Implements(ObjectInterfaceType::Damageable)) + { + dynamic_cast(object)->DamageObject(DamageType::Fire, 0.001f, m_particle[i].objFather); + } m_exploGunCounter++; @@ -1215,7 +1216,7 @@ void CParticle::FrameParticle(float rTime) continue; } - if (m_particle[i].testTime >= 0.2f) + if (m_particle[i].testTime >= 0.1f) { m_particle[i].testTime = 0.0f; CObject* object = SearchObjectGun(m_particle[i].goal, m_particle[i].pos, m_particle[i].type, m_particle[i].objFather); @@ -1238,8 +1239,10 @@ void CParticle::FrameParticle(float rTime) if (object->GetType() != OBJECT_HUMAN) Play(SOUND_TOUCH, m_particle[i].pos, 1.0f); - assert(object->Implements(ObjectInterfaceType::Damageable)); - dynamic_cast(object)->DamageObject(DamageType::Organic, 0.2f); // starts explosion + if (object->Implements(ObjectInterfaceType::Damageable)) + { + dynamic_cast(object)->DamageObject(DamageType::Organic, 0.1f, m_particle[i].objFather); // starts explosion + } } } } @@ -1261,7 +1264,7 @@ void CParticle::FrameParticle(float rTime) continue; } - if (m_particle[i].testTime >= 0.2f) + if (m_particle[i].testTime >= 0.1f) { m_particle[i].testTime = 0.0f; CObject* object = SearchObjectGun(m_particle[i].goal, m_particle[i].pos, m_particle[i].type, m_particle[i].objFather); @@ -1281,8 +1284,10 @@ void CParticle::FrameParticle(float rTime) } else { - assert(object->Implements(ObjectInterfaceType::Damageable)); - dynamic_cast(object)->DamageObject(DamageType::Fire); // starts explosion + if (object->Implements(ObjectInterfaceType::Damageable)) + { + dynamic_cast(object)->DamageObject(DamageType::Fire, std::numeric_limits::infinity(), m_particle[i].objFather); // starts explosion + } } } } @@ -1301,7 +1306,7 @@ void CParticle::FrameParticle(float rTime) continue; } - if (m_particle[i].testTime >= 0.1f) + if (m_particle[i].testTime >= 0.05f) { m_particle[i].testTime = 0.0f; @@ -1338,8 +1343,10 @@ void CParticle::FrameParticle(float rTime) m_particle[i].goal = m_particle[i].pos; if (object != nullptr) { - assert(object->Implements(ObjectInterfaceType::Damageable)); - dynamic_cast(object)->DamageObject(DamageType::Organic, 0.002f); + if (object->Implements(ObjectInterfaceType::Damageable)) + { + dynamic_cast(object)->DamageObject(DamageType::Organic, 0.001f, m_particle[i].objFather); + } m_exploGunCounter ++; @@ -2416,7 +2423,7 @@ void CParticle::FrameParticle(float rTime) if (object != nullptr) { assert(object->Implements(ObjectInterfaceType::Damageable)); - dynamic_cast(object)->DamageObject(DamageType::Tower); + dynamic_cast(object)->DamageObject(DamageType::Tower, std::numeric_limits::infinity(), m_particle[i].objFather); } } @@ -3502,6 +3509,7 @@ CObject* CParticle::SearchObjectGun(Math::Vector old, Math::Vector pos, box2.z += min; CObject* best = nullptr; + float best_dist = std::numeric_limits::infinity(); bool shield = false; for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects()) { @@ -3535,7 +3543,7 @@ CObject* CParticle::SearchObjectGun(Math::Vector old, Math::Vector pos, { continue; } - if (!obj->Implements(ObjectInterfaceType::Damageable)) continue; + if (!obj->Implements(ObjectInterfaceType::Damageable) && !obj->IsBulletWall()) continue; Math::Vector oPos = obj->GetPosition(); @@ -3563,8 +3571,12 @@ CObject* CParticle::SearchObjectGun(Math::Vector old, Math::Vector pos, // Test the center of the object, which is necessary for objects // that have no sphere in the center (station). float dist = Math::Distance(oPos, pos)-4.0f; - if (dist < min) + float obj_dist = Math::Distance(old, oPos); + if (dist < min && obj_dist < best_dist) + { best = obj; + best_dist = obj_dist; + } for (const auto& crashSphere : obj->GetAllCrashSpheres()) { @@ -3577,8 +3589,12 @@ CObject* CParticle::SearchObjectGun(Math::Vector old, Math::Vector pos, Math::Vector p = Math::Projection(old, pos, oPos); float ddist = Math::Distance(p, oPos)-oRadius; - if (ddist < min) + float obj_dist = Math::Distance(old, oPos); + if (ddist < min && obj_dist < best_dist) + { best = obj; + best_dist = obj_dist; + } } } @@ -3724,4 +3740,24 @@ Color CParticle::GetFogColor(Math::Vector pos) return result; } +void CParticle::CutObjectLink(CObject* obj) +{ + for (int i = 0; i < MAXPARTICULE*MAXPARTITYPE; i++) + { + if (!m_particle[i].used) continue; + + if (m_particle[i].objLink == obj) + { + // If the object this particle's coordinates are linked to doesn't exist anymore, remove the particle + DeleteRank(i); + } + + if (m_particle[i].objFather == obj) + { + // If the object that spawned this partcle doesn't exist anymore, remove the link + m_particle[i].objFather = nullptr; + } + } +} + } // namespace Gfx diff --git a/src/graphics/engine/particle.h b/src/graphics/engine/particle.h index 03707a9c..08dfc432 100644 --- a/src/graphics/engine/particle.h +++ b/src/graphics/engine/particle.h @@ -291,6 +291,9 @@ public: //! Draws all the particles void DrawParticle(int sheet); + //! Indicates that the object binds to the particle no longer exists, without deleting it + void CutObjectLink(CObject* obj); + protected: //! Removes a particle of given rank void DeleteRank(int rank); diff --git a/src/graphics/engine/pyro.cpp b/src/graphics/engine/pyro.cpp index 8ac406e8..5bc9f76d 100644 --- a/src/graphics/engine/pyro.cpp +++ b/src/graphics/engine/pyro.cpp @@ -2157,6 +2157,9 @@ bool CPyro::BurnIsKeepPart(int part) void CPyro::BurnTerminate() { + if (m_object == nullptr) + return; + if (m_type == PT_BURNO) // organic object is burning? { DeleteObject(true, true); // removes the insect @@ -2296,6 +2299,7 @@ void CPyro::FallProgress(float rTime) if (floor) // reaches the ground? { assert(m_object->Implements(ObjectInterfaceType::Destroyable)); + // TODO: implement "killer"? dynamic_cast(m_object)->DestroyObject(DestructionType::Explosion); } } @@ -2319,6 +2323,7 @@ void CPyro::FallProgress(float rTime) else { assert(m_object->Implements(ObjectInterfaceType::Destroyable)); + // TODO: implement "killer"? dynamic_cast(m_object)->DestroyObject(DestructionType::Explosion); } } diff --git a/src/graphics/engine/terrain.cpp b/src/graphics/engine/terrain.cpp index 7ff24172..8467b86d 100644 --- a/src/graphics/engine/terrain.cpp +++ b/src/graphics/engine/terrain.cpp @@ -229,7 +229,7 @@ bool CTerrain::LoadResources(const std::string& fileName) if ( (data->surface->w != size) || (data->surface->h != size) ) { - GetLogger()->Error("Invalid resource file\n"); + GetLogger()->Error("Invalid resource file! Expected %dx%d\n", size, size); return false; } @@ -311,10 +311,11 @@ bool CTerrain::LoadRelief(const std::string &fileName, float scaleRelief, ImageData *data = img.GetData(); int size = (m_mosaicCount*m_brickCount)+1; + GetLogger()->Debug("Expected relief size for current terrain configuration is %dx%d\n", size, size); if ( (data->surface->w != size) || (data->surface->h != size) ) { - GetLogger()->Error("Invalid relief file!\n"); + GetLogger()->Error("Invalid relief file! Expected %dx%d\n", size, size); return false; } diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index aece13e9..53bd7f6a 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -643,37 +643,37 @@ UTF8Char CText::TranslateSpecialChar(int specialChar) case CHAR_NEWLINE: // Unicode: U+21B2 - ch.c1 = 0xE2; - ch.c2 = 0x86; - ch.c3 = 0xB2; + ch.c1 = static_cast(0xE2); + ch.c2 = static_cast(0x86); + ch.c3 = static_cast(0xB2); break; case CHAR_DOT: // Unicode: U+23C5 - ch.c1 = 0xE2; - ch.c2 = 0x8F; - ch.c3 = 0x85; + ch.c1 = static_cast(0xE2); + ch.c2 = static_cast(0x8F); + ch.c3 = static_cast(0x85); break; case CHAR_SQUARE: // Unicode: U+25FD - ch.c1 = 0xE2; - ch.c2 = 0x97; - ch.c3 = 0xBD; + ch.c1 = static_cast(0xE2); + ch.c2 = static_cast(0x97); + ch.c3 = static_cast(0xBD); break; case CHAR_SKIP_RIGHT: // Unicode: U+25B6 - ch.c1 = 0xE2; - ch.c2 = 0x96; - ch.c3 = 0xB6; + ch.c1 = static_cast(0xE2); + ch.c2 = static_cast(0x96); + ch.c3 = static_cast(0xB6); break; case CHAR_SKIP_LEFT: // Unicode: U+25C0 - ch.c1 = 0xE2; - ch.c2 = 0x97; - ch.c3 = 0x80; + ch.c1 = static_cast(0xE2); + ch.c2 = static_cast(0x97); + ch.c3 = static_cast(0x80); break; default: diff --git a/src/graphics/opengl/gl14device.cpp b/src/graphics/opengl/gl14device.cpp index 7b93aaff..39993f8d 100644 --- a/src/graphics/opengl/gl14device.cpp +++ b/src/graphics/opengl/gl14device.cpp @@ -258,11 +258,25 @@ bool CGL14Device::Create() { GetLogger()->Info("Core VBO supported\n", glMajor, glMinor); m_vertexBufferType = VBT_VBO_CORE; + + // Set function pointers + m_glGenBuffers = glGenBuffers; + m_glDeleteBuffers = glDeleteBuffers; + m_glBindBuffer = glBindBuffer; + m_glBufferData = glBufferData; + m_glBufferSubData = glBufferSubData; } else if (vboARB) // VBO ARB extension available { GetLogger()->Info("ARB VBO supported\n"); m_vertexBufferType = VBT_VBO_ARB; + + // Set function pointers + m_glGenBuffers = glGenBuffersARB; + m_glDeleteBuffers = glDeleteBuffersARB; + m_glBindBuffer = glBindBufferARB; + m_glBufferData = glBufferDataARB; + m_glBufferSubData = glBufferSubDataARB; } else // no VBO support { @@ -800,44 +814,22 @@ Texture CGL14Device::CreateDepthTexture(int width, int height, int depth) GLuint format = GL_DEPTH_COMPONENT; - if (m_shadowMappingSupport == SMS_CORE) + switch (depth) { - switch (depth) - { - case 16: - format = GL_DEPTH_COMPONENT16; - break; - case 24: - format = GL_DEPTH_COMPONENT24; - break; - case 32: - format = GL_DEPTH_COMPONENT32; - break; - } - - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_INT, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + case 16: + format = GL_DEPTH_COMPONENT16; + break; + case 24: + format = GL_DEPTH_COMPONENT24; + break; + case 32: + format = GL_DEPTH_COMPONENT32; + break; } - else - { - switch (depth) - { - case 16: - format = GL_DEPTH_COMPONENT16_ARB; - break; - case 24: - format = GL_DEPTH_COMPONENT24_ARB; - break; - case 32: - format = GL_DEPTH_COMPONENT32_ARB; - break; - } - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_INT, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); - } + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_INT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; @@ -1379,6 +1371,168 @@ void CGL14Device::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, i glDisableClientState(GL_COLOR_ARRAY); } +void CGL14Device::DrawPrimitive(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int vertexCount) +{ + const char *ptr = reinterpret_cast(vertices); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(format.vertex.size, + TransformType(format.vertex.type), + format.vertex.stride, + ptr + format.vertex.offset); + + if (format.color.enabled) + { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(format.color.size, + TransformType(format.color.type), + format.color.stride, + ptr + format.color.offset); + } + else + glColor4fv(format.color.values); + + if (format.normal.enabled) + { + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(TransformType(format.normal.type), + format.normal.stride, + ptr + format.normal.offset); + } + else + glNormal3fv(format.normal.values); + + glClientActiveTexture(GL_TEXTURE0 + m_remap[0]); + if (format.tex1.enabled) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(format.tex1.size, + TransformType(format.tex1.type), + format.tex1.stride, + ptr + format.tex1.offset); + } + else + glTexCoord2fv(format.tex1.values); + + glClientActiveTexture(GL_TEXTURE0 + m_remap[1]); + if (format.tex2.enabled) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(format.tex2.size, + TransformType(format.tex2.type), + format.tex2.stride, + ptr + format.tex2.offset); + } + else + glTexCoord2fv(format.tex2.values); + + glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount); + + glDisableClientState(GL_VERTEX_ARRAY); + + if (format.color.enabled) glDisableClientState(GL_COLOR_ARRAY); + if (format.normal.enabled) glDisableClientState(GL_NORMAL_ARRAY); + + if (format.tex1.enabled) + { + glClientActiveTexture(GL_TEXTURE0 + m_remap[0]); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (format.tex2.enabled) + { + glClientActiveTexture(GL_TEXTURE0 + m_remap[1]); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + +void CGL14Device::DrawPrimitives(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int first[], int count[], int drawCount) +{ + const char *ptr = reinterpret_cast(vertices); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(format.vertex.size, + TransformType(format.vertex.type), + format.vertex.stride, + ptr + format.vertex.offset); + + if (format.color.enabled) + { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(format.color.size, + TransformType(format.color.type), + format.color.stride, + ptr + format.color.offset); + } + else + glColor4fv(format.color.values); + + if (format.normal.enabled) + { + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(TransformType(format.normal.type), + format.normal.stride, + ptr + format.normal.offset); + } + else + glNormal3fv(format.normal.values); + + glClientActiveTexture(GL_TEXTURE0 + m_remap[0]); + if (format.tex1.enabled) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(format.tex1.size, + TransformType(format.tex1.type), + format.tex1.stride, + ptr + format.tex1.offset); + } + else + glTexCoord2fv(format.tex1.values); + + glClientActiveTexture(GL_TEXTURE0 + m_remap[1]); + if (format.tex2.enabled) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(format.tex2.size, + TransformType(format.tex2.type), + format.tex2.stride, + ptr + format.tex2.offset); + } + else + glTexCoord2fv(format.tex2.values); + + GLenum t = TranslateGfxPrimitive(type); + + if (m_multiDrawArrays) + { + glMultiDrawArrays(t, first, count, drawCount); + } + else + { + for (int i = 0; i < drawCount; i++) + glDrawArrays(t, first[i], count[i]); + } + + glDisableClientState(GL_VERTEX_ARRAY); + + if (format.color.enabled) glDisableClientState(GL_COLOR_ARRAY); + if (format.normal.enabled) glDisableClientState(GL_NORMAL_ARRAY); + + if (format.tex1.enabled) + { + glClientActiveTexture(GL_TEXTURE0 + m_remap[0]); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (format.tex2.enabled) + { + glClientActiveTexture(GL_TEXTURE0 + m_remap[1]); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + void CGL14Device::DrawPrimitives(PrimitiveType type, const Vertex *vertices, int first[], int count[], int drawCount, Color color) { @@ -1494,20 +1648,10 @@ unsigned int CGL14Device::CreateStaticBuffer(PrimitiveType primitiveType, const info.vertexCount = vertexCount; info.bufferId = 0; - if(m_vertexBufferType == VBT_VBO_CORE) - { - glGenBuffers(1, &info.bufferId); - glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); - glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(Vertex), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - else - { - glGenBuffersARB(1, &info.bufferId); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, info.bufferId); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexCount * sizeof(Vertex), vertices, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - } + m_glGenBuffers(1, &info.bufferId); + m_glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); + m_glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(Vertex), vertices, GL_STATIC_DRAW); + m_glBindBuffer(GL_ARRAY_BUFFER, 0); m_vboObjects[id] = info; } @@ -1538,20 +1682,10 @@ unsigned int CGL14Device::CreateStaticBuffer(PrimitiveType primitiveType, const info.vertexCount = vertexCount; info.bufferId = 0; - if(m_vertexBufferType == VBT_VBO_CORE) - { - glGenBuffers(1, &info.bufferId); - glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); - glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexTex2), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - else - { - glGenBuffersARB(1, &info.bufferId); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, info.bufferId); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexCount * sizeof(VertexTex2), vertices, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - } + m_glGenBuffers(1, &info.bufferId); + m_glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); + m_glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexTex2), vertices, GL_STATIC_DRAW); + m_glBindBuffer(GL_ARRAY_BUFFER, 0); m_vboObjects[id] = info; } @@ -1582,20 +1716,10 @@ unsigned int CGL14Device::CreateStaticBuffer(PrimitiveType primitiveType, const info.vertexCount = vertexCount; info.bufferId = 0; - if(m_vertexBufferType == VBT_VBO_CORE) - { - glGenBuffers(1, &info.bufferId); - glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); - glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexCol), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - else - { - glGenBuffersARB(1, &info.bufferId); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, info.bufferId); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexCount * sizeof(VertexCol), vertices, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - } + m_glGenBuffers(1, &info.bufferId); + m_glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); + m_glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexCol), vertices, GL_STATIC_DRAW); + m_glBindBuffer(GL_ARRAY_BUFFER, 0); m_vboObjects[id] = info; } @@ -1626,18 +1750,9 @@ void CGL14Device::UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primit info.vertexType = VERTEX_TYPE_NORMAL; info.vertexCount = vertexCount; - if(m_vertexBufferType == VBT_VBO_CORE) - { - glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); - glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(Vertex), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - else - { - glBindBufferARB(GL_ARRAY_BUFFER_ARB, info.bufferId); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexCount * sizeof(Vertex), vertices, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - } + m_glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); + m_glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(Vertex), vertices, GL_STATIC_DRAW); + m_glBindBuffer(GL_ARRAY_BUFFER, 0); } else { @@ -1662,18 +1777,9 @@ void CGL14Device::UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primit info.vertexType = VERTEX_TYPE_TEX2; info.vertexCount = vertexCount; - if(m_vertexBufferType == VBT_VBO_CORE) - { - glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); - glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexTex2), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - else - { - glBindBufferARB(GL_ARRAY_BUFFER_ARB, info.bufferId); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexCount * sizeof(VertexTex2), vertices, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - } + m_glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); + m_glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexTex2), vertices, GL_STATIC_DRAW); + m_glBindBuffer(GL_ARRAY_BUFFER, 0); } else { @@ -1698,18 +1804,9 @@ void CGL14Device::UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primit info.vertexType = VERTEX_TYPE_COL; info.vertexCount = vertexCount; - if(m_vertexBufferType == VBT_VBO_CORE) - { - glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); - glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexCol), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - else - { - glBindBufferARB(GL_ARRAY_BUFFER_ARB, info.bufferId); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexCount * sizeof(VertexCol), vertices, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - } + m_glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); + m_glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexCol), vertices, GL_STATIC_DRAW); + m_glBindBuffer(GL_ARRAY_BUFFER, 0); } else { @@ -1729,10 +1826,7 @@ void CGL14Device::DrawStaticBuffer(unsigned int bufferId) if (it == m_vboObjects.end()) return; - if(m_vertexBufferType == VBT_VBO_CORE) - glBindBuffer(GL_ARRAY_BUFFER, (*it).second.bufferId); - else - glBindBufferARB(GL_ARRAY_BUFFER_ARB, (*it).second.bufferId); + m_glBindBuffer(GL_ARRAY_BUFFER, (*it).second.bufferId); if ((*it).second.vertexType == VERTEX_TYPE_NORMAL) { @@ -1795,10 +1889,7 @@ void CGL14Device::DrawStaticBuffer(unsigned int bufferId) glDisableClientState(GL_COLOR_ARRAY); } - if(m_vertexBufferType == VBT_VBO_CORE) - glBindBuffer(GL_ARRAY_BUFFER, 0); - else - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + m_glBindBuffer(GL_ARRAY_BUFFER, 0); } else { @@ -1814,10 +1905,7 @@ void CGL14Device::DestroyStaticBuffer(unsigned int bufferId) if (it == m_vboObjects.end()) return; - if(m_vertexBufferType == VBT_VBO_CORE) - glDeleteBuffers(1, &(*it).second.bufferId); - else - glDeleteBuffersARB(1, &(*it).second.bufferId); + m_glDeleteBuffers(1, &(*it).second.bufferId); m_vboObjects.erase(it); } diff --git a/src/graphics/opengl/gl14device.h b/src/graphics/opengl/gl14device.h index 96190394..4106b841 100644 --- a/src/graphics/opengl/gl14device.h +++ b/src/graphics/opengl/gl14device.h @@ -119,6 +119,11 @@ public: void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT) override; + virtual void DrawPrimitive(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int vertexCount) override; + virtual void DrawPrimitives(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int first[], int count[], int drawCount) override; + virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices , int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) override; virtual void DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, @@ -290,6 +295,14 @@ private: bool m_shadowMapping = false; //! true means that quality shadows are enabled bool m_shadowQuality = true; + + + //! Pointers to OpenGL functions + PFNGLGENBUFFERSPROC m_glGenBuffers = nullptr; + PFNGLDELETEBUFFERSPROC m_glDeleteBuffers = nullptr; + PFNGLBINDBUFFERPROC m_glBindBuffer = nullptr; + PFNGLBUFFERDATAPROC m_glBufferData = nullptr; + PFNGLBUFFERSUBDATAPROC m_glBufferSubData = nullptr; }; diff --git a/src/graphics/opengl/gl21device.cpp b/src/graphics/opengl/gl21device.cpp index e1a96dbe..48467668 100644 --- a/src/graphics/opengl/gl21device.cpp +++ b/src/graphics/opengl/gl21device.cpp @@ -242,7 +242,7 @@ bool CGL21Device::Create() glViewport(0, 0, m_config.size.x, m_config.size.y); // this is set in shader - int numLights = 8; + int numLights = 4; m_lights = std::vector(numLights, Light()); m_lightsEnabled = std::vector (numLights, false); @@ -408,7 +408,7 @@ bool CGL21Device::Create() uni.fogColor = glGetUniformLocation(m_normalProgram, "uni_FogColor"); uni.shadowColor = glGetUniformLocation(m_normalProgram, "uni_ShadowColor"); - uni.lightingEnabled = glGetUniformLocation(m_normalProgram, "uni_LightingEnabled"); + uni.lightCount = glGetUniformLocation(m_normalProgram, "uni_LightCount"); uni.ambientColor = glGetUniformLocation(m_normalProgram, "uni_Material.ambient"); uni.diffuseColor = glGetUniformLocation(m_normalProgram, "uni_Material.diffuse"); @@ -417,12 +417,6 @@ bool CGL21Device::Create() GLchar name[64]; for (int i = 0; i < 8; i++) { - sprintf(name, "uni_Light[%d].Enabled", i); - uni.lights[i].enabled = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].Type", i); - uni.lights[i].type = glGetUniformLocation(m_normalProgram, name); - sprintf(name, "uni_Light[%d].Position", i); uni.lights[i].position = glGetUniformLocation(m_normalProgram, name); @@ -434,18 +428,6 @@ bool CGL21Device::Create() sprintf(name, "uni_Light[%d].Specular", i); uni.lights[i].specular = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].Attenuation", i); - uni.lights[i].attenuation = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].SpotDirection", i); - uni.lights[i].spotDirection = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].Exponent", i); - uni.lights[i].spotExponent = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].SpotCutoff", i); - uni.lights[i].spotCutoff = glGetUniformLocation(m_normalProgram, name); } // Set default uniform values @@ -476,10 +458,7 @@ bool CGL21Device::Create() glUniform1f(uni.shadowColor, 0.5f); - glUniform1i(uni.lightingEnabled, 0); - - for (int i = 0; i < 8; i++) - glUniform1i(uni.lights[i].enabled, 0); + glUniform1i(uni.lightCount, 0); } // Obtain uniform locations from interface rendering program and initialize them @@ -594,6 +573,7 @@ void CGL21Device::ConfigChanged(const DeviceConfig& newConfig) // Reset state m_lighting = false; + m_updateLights = true; glViewport(0, 0, m_config.size.x, m_config.size.y); @@ -721,36 +701,7 @@ void CGL21Device::SetLight(int index, const Light &light) m_lights[index] = light; - LightLocations &loc = m_uniforms[m_mode].lights[index]; - - glUniform4fv(loc.ambient, 1, light.ambient.Array()); - glUniform4fv(loc.diffuse, 1, light.diffuse.Array()); - glUniform4fv(loc.specular, 1, light.specular.Array()); - glUniform3f(loc.attenuation, light.attenuation0, light.attenuation1, light.attenuation2); - - if (light.type == LIGHT_DIRECTIONAL) - { - glUniform1i(loc.type, 1); - glUniform4f(loc.position, -light.direction.x, -light.direction.y, -light.direction.z, 0.0f); - } - else if (light.type == LIGHT_POINT) - { - glUniform1i(loc.type, 2); - glUniform4f(loc.position, light.position.x, light.position.y, light.position.z, 1.0f); - - glUniform3f(loc.spotDirection, 0.0f, 1.0f, 0.0f); - glUniform1f(loc.spotCutoff, -1.0f); - glUniform1f(loc.spotExponent, 1.0f); - } - else if (light.type == LIGHT_SPOT) - { - glUniform1i(loc.type, 3); - glUniform4f(loc.position, light.position.x, light.position.y, light.position.z, 1.0f); - - glUniform3f(loc.spotDirection, -light.direction.x, -light.direction.y, -light.direction.z); - glUniform1f(loc.spotCutoff, std::cos(light.spotAngle)); - glUniform1f(loc.spotExponent, light.spotIntensity); - } + m_updateLights = true; } void CGL21Device::SetLightEnabled(int index, bool enabled) @@ -760,7 +711,7 @@ void CGL21Device::SetLightEnabled(int index, bool enabled) m_lightsEnabled[index] = enabled; - glUniform1i(m_uniforms[m_mode].lights[index].enabled, enabled ? 1 : 0); + m_updateLights = true; } /** If image is invalid, returns invalid texture. @@ -798,12 +749,13 @@ Texture CGL21Device::CreateTexture(ImageData *data, const TextureCreateParams &p result.originalSize = result.size; - glActiveTexture(GL_TEXTURE0); - glEnable(GL_TEXTURE_2D); - glGenTextures(1, &result.id); + + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, result.id); + glEnable(GL_TEXTURE_2D); + // Set texture parameters GLint minF = GL_NEAREST, magF = GL_NEAREST; int mipmapLevel = 1; @@ -991,7 +943,7 @@ void CGL21Device::SetTexture(int index, const Texture &texture) glBindTexture(GL_TEXTURE_2D, texture.id); // Params need to be updated for the new bound texture - UpdateTextureStatus(); + UpdateTextureState(index); UpdateTextureParams(index); } @@ -1009,7 +961,7 @@ void CGL21Device::SetTexture(int index, unsigned int textureId) glBindTexture(GL_TEXTURE_2D, textureId); // Params need to be updated for the new bound texture - UpdateTextureStatus(); + UpdateTextureState(index); UpdateTextureParams(index); } @@ -1024,15 +976,54 @@ void CGL21Device::SetTextureEnabled(int index, bool enabled) if (same) return; // nothing to do - UpdateTextureStatus(); + UpdateTextureState(index); } -void CGL21Device::UpdateTextureStatus() +void CGL21Device::UpdateTextureState(int index) { - for (int i = 0; i < 3; i++) + bool enabled = m_texturesEnabled[index] && (m_currentTextures[index].id != 0); + glUniform1i(m_uniforms[m_mode].textureEnabled[index], enabled ? 1 : 0); +} + +void CGL21Device::UpdateLights() +{ + m_updateLights = false; + + // If not in normal rendering mode, return immediately + if (m_mode != 0) return; + + // Lighting enabled + if (m_lighting) { - bool enabled = m_texturesEnabled[i] && (m_currentTextures[i].id != 0); - glUniform1i(m_uniforms[m_mode].textureEnabled[i], enabled ? 1 : 0); + int index = 0; + + // Iterate all lights + for (unsigned int i = 0; i < m_lights.size(); i++) + { + // If disabled, ignore and continue + if (!m_lightsEnabled[i]) continue; + + // If not directional, ignore and continue + if (m_lights[i].type != LIGHT_DIRECTIONAL) continue; + + Light &light = m_lights[i]; + LightLocations &uni = m_uniforms[m_mode].lights[index]; + + glUniform4fv(uni.ambient, 1, light.ambient.Array()); + glUniform4fv(uni.diffuse, 1, light.diffuse.Array()); + glUniform4fv(uni.specular, 1, light.specular.Array()); + + glUniform4f(uni.position, -light.direction.x, -light.direction.y, -light.direction.z, 0.0f); + + index++; + } + + glUniform1i(m_uniforms[m_mode].lightCount, index); + } + // Lighting disabled + else + { + glUniform1i(m_uniforms[m_mode].lightCount, 0); } } @@ -1044,6 +1035,12 @@ inline void CGL21Device::BindVBO(GLuint vbo) m_currentVBO = vbo; } +inline void CGL21Device::BindTexture(int index, GLuint texture) +{ + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, texture); +} + /** Sets the texture parameters for the given texture stage. If the given texture was not set (bound) yet, nothing happens. @@ -1121,6 +1118,8 @@ void CGL21Device::SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode void CGL21Device::DrawPrimitive(PrimitiveType type, const Vertex *vertices, int vertexCount, Color color) { + if (m_updateLights) UpdateLights(); + BindVBO(0); Vertex* vs = const_cast(vertices); @@ -1148,6 +1147,8 @@ void CGL21Device::DrawPrimitive(PrimitiveType type, const Vertex *vertices, int void CGL21Device::DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, Color color) { + if (m_updateLights) UpdateLights(); + BindVBO(0); VertexTex2* vs = const_cast(vertices); @@ -1180,6 +1181,8 @@ void CGL21Device::DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, void CGL21Device::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, int vertexCount) { + if (m_updateLights) UpdateLights(); + BindVBO(0); VertexCol* vs = const_cast(vertices); @@ -1196,9 +1199,171 @@ void CGL21Device::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, i glDisableClientState(GL_COLOR_ARRAY); } +void CGL21Device::DrawPrimitive(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int vertexCount) +{ + if (m_updateLights) UpdateLights(); + + BindVBO(0); + + const char *ptr = reinterpret_cast(vertices); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(format.vertex.size, + TransformType(format.vertex.type), + format.vertex.stride, + ptr + format.vertex.offset); + + if (format.color.enabled) + { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(format.color.size, + TransformType(format.color.type), + format.color.stride, + ptr + format.color.offset); + } + else + glColor4fv(format.color.values); + + if (format.normal.enabled) + { + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(TransformType(format.normal.type), + format.normal.stride, + ptr + format.normal.offset); + } + else + glNormal3fv(format.normal.values); + + glClientActiveTexture(GL_TEXTURE0); + if (format.tex1.enabled) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(format.tex1.size, + TransformType(format.tex1.type), + format.tex1.stride, + ptr + format.tex1.offset); + } + else + glTexCoord2fv(format.tex1.values); + + glClientActiveTexture(GL_TEXTURE1); + if (format.tex2.enabled) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(format.tex2.size, + TransformType(format.tex2.type), + format.tex2.stride, + ptr + format.tex2.offset); + } + else + glTexCoord2fv(format.tex2.values); + + glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount); + + glDisableClientState(GL_VERTEX_ARRAY); + + if (format.color.enabled) glDisableClientState(GL_COLOR_ARRAY); + if (format.normal.enabled) glDisableClientState(GL_NORMAL_ARRAY); + + if (format.tex1.enabled) + { + glClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (format.tex2.enabled) + { + glClientActiveTexture(GL_TEXTURE1); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + +void CGL21Device::DrawPrimitives(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int first[], int count[], int drawCount) +{ + if (m_updateLights) UpdateLights(); + + BindVBO(0); + + const char *ptr = reinterpret_cast(vertices); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(format.vertex.size, + TransformType(format.vertex.type), + format.vertex.stride, + ptr + format.vertex.offset); + + if (format.color.enabled) + { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(format.color.size, + TransformType(format.color.type), + format.color.stride, + ptr + format.color.offset); + } + else + glColor4fv(format.color.values); + + if (format.normal.enabled) + { + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(TransformType(format.normal.type), + format.normal.stride, + ptr + format.normal.offset); + } + else + glNormal3fv(format.normal.values); + + glClientActiveTexture(GL_TEXTURE0); + if (format.tex1.enabled) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(format.tex1.size, + TransformType(format.tex1.type), + format.tex1.stride, + ptr + format.tex1.offset); + } + else + glTexCoord2fv(format.tex1.values); + + glClientActiveTexture(GL_TEXTURE1); + if (format.tex2.enabled) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(format.tex2.size, + TransformType(format.tex2.type), + format.tex2.stride, + ptr + format.tex2.offset); + } + else + glTexCoord2fv(format.tex2.values); + + glMultiDrawArrays(TranslateGfxPrimitive(type), first, count, drawCount); + + glDisableClientState(GL_VERTEX_ARRAY); + + if (format.color.enabled) glDisableClientState(GL_COLOR_ARRAY); + if (format.normal.enabled) glDisableClientState(GL_NORMAL_ARRAY); + + if (format.tex1.enabled) + { + glClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (format.tex2.enabled) + { + glClientActiveTexture(GL_TEXTURE1); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + void CGL21Device::DrawPrimitives(PrimitiveType type, const Vertex *vertices, int first[], int count[], int drawCount, Color color) { + if (m_updateLights) UpdateLights(); + BindVBO(0); Vertex* vs = const_cast(vertices); @@ -1225,6 +1390,8 @@ void CGL21Device::DrawPrimitives(PrimitiveType type, const Vertex *vertices, void CGL21Device::DrawPrimitives(PrimitiveType type, const VertexTex2 *vertices, int first[], int count[], int drawCount, Color color) { + if (m_updateLights) UpdateLights(); + BindVBO(0); VertexTex2* vs = const_cast(vertices); @@ -1258,6 +1425,8 @@ void CGL21Device::DrawPrimitives(PrimitiveType type, const VertexTex2 *vertices, void CGL21Device::DrawPrimitives(PrimitiveType type, const VertexCol *vertices, int first[], int count[], int drawCount) { + if (m_updateLights) UpdateLights(); + BindVBO(0); VertexCol* vs = const_cast(vertices); @@ -1418,6 +1587,8 @@ void CGL21Device::DrawStaticBuffer(unsigned int bufferId) if (it == m_vboObjects.end()) return; + if (m_updateLights) UpdateLights(); + BindVBO((*it).second.bufferId); if ((*it).second.vertexType == VERTEX_TYPE_NORMAL) @@ -1585,9 +1756,11 @@ void CGL21Device::SetRenderState(RenderState state, bool enabled) } else if (state == RENDER_STATE_LIGHTING) { + if (m_lighting == enabled) return; + m_lighting = enabled; - glUniform1i(m_uniforms[m_mode].lightingEnabled, enabled ? 1 : 0); + m_updateLights = true; return; } diff --git a/src/graphics/opengl/gl21device.h b/src/graphics/opengl/gl21device.h index 20cf2156..9a2bea7e 100644 --- a/src/graphics/opengl/gl21device.h +++ b/src/graphics/opengl/gl21device.h @@ -100,6 +100,11 @@ public: void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT) override; + virtual void DrawPrimitive(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int vertexCount) override; + virtual void DrawPrimitives(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int first[], int count[], int drawCount) override; + virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices , int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) override; virtual void DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, @@ -178,10 +183,14 @@ public: private: //! Updates the texture params for given texture stage void UpdateTextureParams(int index); - //! Updates texture status - void UpdateTextureStatus(); + //! Updates texture state + void UpdateTextureState(int index); + //! Update light parameters + void UpdateLights(); //! Binds VBO inline void BindVBO(GLuint vbo); + //! Binds texture + inline void BindTexture(int index, GLuint texture); private: //! Current config @@ -203,6 +212,8 @@ private: //! Whether lighting is enabled bool m_lighting = false; + //! true means that lights need to be updated + bool m_updateLights = false; //! Current lights std::vector m_lights; //! Current lights enable status diff --git a/src/graphics/opengl/gl33device.cpp b/src/graphics/opengl/gl33device.cpp index df67e5c1..74853713 100644 --- a/src/graphics/opengl/gl33device.cpp +++ b/src/graphics/opengl/gl33device.cpp @@ -233,7 +233,7 @@ bool CGL33Device::Create() glViewport(0, 0, m_config.size.x, m_config.size.y); // this is set in shader - m_capabilities.maxLights = 8; + m_capabilities.maxLights = 4; m_lights = std::vector(m_capabilities.maxLights, Light()); m_lightsEnabled = std::vector(m_capabilities.maxLights, false); @@ -381,14 +381,14 @@ bool CGL33Device::Create() uni.shadowColor = glGetUniformLocation(m_normalProgram, "uni_ShadowColor"); - uni.lightingEnabled = glGetUniformLocation(m_normalProgram, "uni_LightingEnabled"); + uni.lightCount = glGetUniformLocation(m_normalProgram, "uni_LightCount"); uni.ambientColor = glGetUniformLocation(m_normalProgram, "uni_AmbientColor"); uni.diffuseColor = glGetUniformLocation(m_normalProgram, "uni_DiffuseColor"); uni.specularColor = glGetUniformLocation(m_normalProgram, "uni_SpecularColor"); GLchar name[64]; - for (int i = 0; i < 8; i++) + for (int i = 0; i < m_capabilities.maxLights; i++) { LightLocations &light = uni.lights[i]; @@ -440,12 +440,9 @@ bool CGL33Device::Create() glUniform1f(uni.shadowColor, 0.5f); glUniform1i(uni.alphaTestEnabled, 0); - glUniform1f(uni.alphaReference, 1.0f); + glUniform1f(uni.alphaReference, 0.5f); - glUniform1i(uni.lightingEnabled, 0); - - for (int i = 0; i < 8; i++) - glUniform1i(uni.lights[i].enabled, 0); + glUniform1i(uni.lightCount, 0); } // Obtain uniform locations for interface program @@ -523,21 +520,18 @@ bool CGL33Device::Create() m_framebuffers["default"] = MakeUnique(framebufferParams); - // create dynamic buffers - for (int i = 0; i < 3; i++) - { - glGenVertexArrays(1, &m_dynamicBuffers[i].vao); + // create dynamic buffer + glGenVertexArrays(1, &m_dynamicBuffer.vao); - m_dynamicBuffers[i].size = 4 * 1024 * 1024; - m_dynamicBuffers[i].offset = 0; + m_dynamicBuffer.size = 4 * 1024 * 1024; + m_dynamicBuffer.offset = 0; - glGenBuffers(1, &m_dynamicBuffers[i].vbo); - glBindBuffer(GL_ARRAY_BUFFER, m_dynamicBuffers[i].vbo); - glBufferData(GL_ARRAY_BUFFER, m_dynamicBuffers[i].size, nullptr, GL_STREAM_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); + glGenBuffers(1, &m_dynamicBuffer.vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_dynamicBuffer.vbo); + glBufferData(GL_ARRAY_BUFFER, m_dynamicBuffer.size, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); - m_vboMemory += m_dynamicBuffers[i].size; - } + m_vboMemory += m_dynamicBuffer.size; GetLogger()->Info("CDevice created successfully\n"); @@ -563,13 +557,10 @@ void CGL33Device::Destroy() DestroyAllTextures(); // delete dynamic buffer - for (int i = 0; i < 3; i++) - { - glDeleteVertexArrays(1, &m_dynamicBuffers[i].vao); - glDeleteBuffers(1, &m_dynamicBuffers[i].vbo); + glDeleteVertexArrays(1, &m_dynamicBuffer.vao); + glDeleteBuffers(1, &m_dynamicBuffer.vbo); - m_vboMemory -= m_dynamicBuffers[i].size; - } + m_vboMemory -= m_dynamicBuffer.size; m_lights.clear(); m_lightsEnabled.clear(); @@ -643,7 +634,9 @@ void CGL33Device::SetRenderMode(RenderMode mode) m_uni = &m_uniforms[m_mode]; - UpdateRenderingMode(); + UpdateTextureState(0); + UpdateTextureState(1); + UpdateTextureState(2); } void CGL33Device::SetTransform(TransformType type, const Math::Matrix &matrix) @@ -714,23 +707,7 @@ void CGL33Device::SetLight(int index, const Light &light) m_lights[index] = light; - LightLocations &uni = m_uni->lights[index]; - - glUniform4fv(uni.ambient, 1, light.ambient.Array()); - glUniform4fv(uni.diffuse, 1, light.diffuse.Array()); - glUniform4fv(uni.specular, 1, light.specular.Array()); - glUniform3f(uni.attenuation, light.attenuation0, light.attenuation1, light.attenuation2); - - if (light.type == LIGHT_DIRECTIONAL) - { - glUniform4f(uni.position, -light.direction.x, -light.direction.y, -light.direction.z, 0.0f); - } - else - { - glUniform4f(uni.position, light.position.x, light.position.y, light.position.z, 1.0f); - } - - // TODO: add spotlight params + m_updateLights = true; } void CGL33Device::SetLightEnabled(int index, bool enabled) @@ -740,7 +717,7 @@ void CGL33Device::SetLightEnabled(int index, bool enabled) m_lightsEnabled[index] = enabled; - glUniform1i(m_uni->lights[index].enabled, enabled ? 1 : 0); + m_updateLights = true; } /** If image is invalid, returns invalid texture. @@ -775,10 +752,9 @@ Texture CGL33Device::CreateTexture(ImageData *data, const TextureCreateParams &p result.originalSize = result.size; - glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &result.id); - glBindTexture(GL_TEXTURE_2D, result.id); + + BindTexture(m_freeTexture, result.id); // Set texture parameters GLint minF = GL_NEAREST, magF = GL_NEAREST; @@ -841,9 +817,6 @@ Texture CGL33Device::CreateTexture(ImageData *data, const TextureCreateParams &p m_allTextures.insert(result); - // Restore the previous state of 1st stage - glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id); - return result; } @@ -855,10 +828,9 @@ Texture CGL33Device::CreateDepthTexture(int width, int height, int depth) result.size.x = width; result.size.y = height; - glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &result.id); - glBindTexture(GL_TEXTURE_2D, result.id); + + BindTexture(m_freeTexture, result.id); GLuint format = GL_DEPTH_COMPONENT; @@ -887,16 +859,14 @@ Texture CGL33Device::CreateDepthTexture(int width, int height, int depth) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color); - glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id); - return result; } void CGL33Device::UpdateTexture(const Texture& texture, Math::IntPoint offset, ImageData* data, TexImgFormat format) { - glActiveTexture(GL_TEXTURE0); + if (texture.id == 0) return; - glBindTexture(GL_TEXTURE_2D, texture.id); + BindTexture(m_freeTexture, texture.id); PreparedTextureData texData = PrepareTextureData(data, format); @@ -951,18 +921,16 @@ void CGL33Device::SetTexture(int index, const Texture &texture) { assert(index >= 0 && index < static_cast( m_currentTextures.size() )); - bool same = m_currentTextures[index].id == texture.id; + if (m_currentTextures[index].id == texture.id) + return; + + BindTexture(index, texture.id); m_currentTextures[index] = texture; // remember the new value - if (same) - return; // nothing to do - - glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, texture.id); - // Params need to be updated for the new bound texture UpdateTextureParams(index); + UpdateTextureState(index); } void CGL33Device::SetTexture(int index, unsigned int textureId) @@ -972,27 +940,25 @@ void CGL33Device::SetTexture(int index, unsigned int textureId) if (m_currentTextures[index].id == textureId) return; // nothing to do - m_currentTextures[index].id = textureId; + BindTexture(index, textureId); - glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, textureId); + m_currentTextures[index].id = textureId; // Params need to be updated for the new bound texture UpdateTextureParams(index); + UpdateTextureState(index); } void CGL33Device::SetTextureEnabled(int index, bool enabled) { assert(index >= 0 && index < static_cast( m_currentTextures.size() )); - bool same = m_texturesEnabled[index] == enabled; + if (m_texturesEnabled[index] == enabled) + return; m_texturesEnabled[index] = enabled; - if (same) - return; // nothing to do - - UpdateRenderingMode(); + UpdateTextureState(index); } /** @@ -1071,148 +1037,176 @@ void CGL33Device::SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode void CGL33Device::DrawPrimitive(PrimitiveType type, const Vertex *vertices, int vertexCount, Color color) { + if (m_updateLights) UpdateLights(); + Vertex* vs = const_cast(vertices); unsigned int size = vertexCount * sizeof(Vertex); - DynamicBuffer& buffer = m_dynamicBuffers[0]; + DynamicBuffer& buffer = m_dynamicBuffer; BindVAO(buffer.vao); BindVBO(buffer.vbo); unsigned int offset = UploadVertexData(buffer, vs, size); - // Start of the buffer, reinitialize binding state - if (offset == 0) - { - // Vertex coordinate - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), - reinterpret_cast(offsetof(Vertex, coord))); + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offset + offsetof(Vertex, coord))); - // Normal - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), - reinterpret_cast(offsetof(Vertex, normal))); - - // Color - glDisableVertexAttribArray(2); - - // Texture coordinate 0 - glEnableVertexAttribArray(3); - glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), - reinterpret_cast(offsetof(Vertex, texCoord))); - - // Texture coordinate 1 - glDisableVertexAttribArray(4); - glVertexAttrib2f(4, 0.0f, 0.0f); - } + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offset + offsetof(Vertex, normal))); + // Color + glDisableVertexAttribArray(2); glVertexAttrib4fv(2, color.Array()); - UpdateRenderingMode(); + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offset + offsetof(Vertex, texCoord))); - int first = offset / sizeof(Vertex); + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); - glDrawArrays(TranslateGfxPrimitive(type), first, vertexCount); + glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount); } void CGL33Device::DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, Color color) { + if (m_updateLights) UpdateLights(); + VertexTex2* vs = const_cast(vertices); unsigned int size = vertexCount * sizeof(VertexTex2); - DynamicBuffer& buffer = m_dynamicBuffers[1]; + DynamicBuffer& buffer = m_dynamicBuffer; BindVAO(buffer.vao); BindVBO(buffer.vbo); unsigned int offset = UploadVertexData(buffer, vs, size); - if (offset == 0) - { - // Vertex coordinate - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), - reinterpret_cast(offsetof(VertexTex2, coord))); + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), + reinterpret_cast(offset + offsetof(VertexTex2, coord))); - // Normal - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), - reinterpret_cast(offsetof(VertexTex2, normal))); - - // Color - glDisableVertexAttribArray(2); - - // Texture coordinate 0 - glEnableVertexAttribArray(3); - glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), - reinterpret_cast(offsetof(VertexTex2, texCoord))); - - // Texture coordinate 1 - glEnableVertexAttribArray(4); - glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), - reinterpret_cast(offsetof(VertexTex2, texCoord2))); - } + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), + reinterpret_cast(offset + offsetof(VertexTex2, normal))); + // Color + glDisableVertexAttribArray(2); glVertexAttrib4fv(2, color.Array()); - UpdateRenderingMode(); + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), + reinterpret_cast(offset + offsetof(VertexTex2, texCoord))); - int first = offset / sizeof(VertexTex2); + // Texture coordinate 1 + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), + reinterpret_cast(offset + offsetof(VertexTex2, texCoord2))); - glDrawArrays(TranslateGfxPrimitive(type), first, vertexCount); + glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount); } void CGL33Device::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, int vertexCount) { + if (m_updateLights) UpdateLights(); + VertexCol* vs = const_cast(vertices); unsigned int size = vertexCount * sizeof(VertexCol); - DynamicBuffer& buffer = m_dynamicBuffers[2]; + DynamicBuffer& buffer = m_dynamicBuffer; BindVAO(buffer.vao); BindVBO(buffer.vbo); unsigned int offset = UploadVertexData(buffer, vs, size); - if (offset == 0) - { - // Vertex coordinate - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol), - reinterpret_cast(offsetof(VertexCol, coord))); + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol), + reinterpret_cast(offset + offsetof(VertexCol, coord))); - // Normal - glDisableVertexAttribArray(1); - glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); + // Normal + glDisableVertexAttribArray(1); + glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); - // Color - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol), - reinterpret_cast(offsetof(VertexCol, color))); + // Color + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol), + reinterpret_cast(offset + offsetof(VertexCol, color))); - // Texture coordinate 0 - glDisableVertexAttribArray(3); - glVertexAttrib2f(3, 0.0f, 0.0f); + // Texture coordinate 0 + glDisableVertexAttribArray(3); + glVertexAttrib2f(3, 0.0f, 0.0f); - // Texture coordinate 1 - glDisableVertexAttribArray(4); - glVertexAttrib2f(4, 0.0f, 0.0f); - } + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); - UpdateRenderingMode(); + glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount); +} - int first = offset / sizeof(VertexCol); +void CGL33Device::DrawPrimitive(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int vertexCount) +{ + if (m_updateLights) UpdateLights(); - glDrawArrays(TranslateGfxPrimitive(type), first, vertexCount); + DynamicBuffer& buffer = m_dynamicBuffer; + + BindVAO(buffer.vao); + BindVBO(buffer.vbo); + + unsigned int offset = UploadVertexData(buffer, vertices, size); + + // Update vertex attribute bindings + UpdateVertexAttribute(0, format.vertex, offset); + UpdateVertexAttribute(1, format.normal, offset); + UpdateVertexAttribute(2, format.color, offset); + UpdateVertexAttribute(3, format.tex1, offset); + UpdateVertexAttribute(4, format.tex2, offset); + + glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount); +} + +void CGL33Device::DrawPrimitives(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int first[], int count[], int drawCount) +{ + if (m_updateLights) UpdateLights(); + + DynamicBuffer& buffer = m_dynamicBuffer; + + BindVAO(buffer.vao); + BindVBO(buffer.vbo); + + unsigned int offset = UploadVertexData(buffer, vertices, size); + + // Update vertex attribute bindings + UpdateVertexAttribute(0, format.vertex, offset); + UpdateVertexAttribute(1, format.normal, offset); + UpdateVertexAttribute(2, format.color, offset); + UpdateVertexAttribute(3, format.tex1, offset); + UpdateVertexAttribute(4, format.tex2, offset); + + glMultiDrawArrays(TranslateGfxPrimitive(type), first, count, drawCount); } void CGL33Device::DrawPrimitives(PrimitiveType type, const Vertex *vertices, int first[], int count[], int drawCount, Color color) { + if (m_updateLights) UpdateLights(); + Vertex* vs = const_cast(vertices); int vertexCount = 0; @@ -1227,56 +1221,44 @@ void CGL33Device::DrawPrimitives(PrimitiveType type, const Vertex *vertices, unsigned int size = vertexCount * sizeof(Vertex); - DynamicBuffer& buffer = m_dynamicBuffers[0]; + DynamicBuffer& buffer = m_dynamicBuffer; BindVAO(buffer.vao); BindVBO(buffer.vbo); unsigned int offset = UploadVertexData(buffer, vs, size); - if (offset == 0) - { - // Vertex coordinate - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), - reinterpret_cast(offsetof(Vertex, coord))); + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offset + offsetof(Vertex, coord))); - // Normal - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), - reinterpret_cast(offsetof(Vertex, normal))); - - // Color - glDisableVertexAttribArray(2); - - // Texture coordinate 0 - glEnableVertexAttribArray(3); - glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), - reinterpret_cast(offsetof(Vertex, texCoord))); - - // Texture coordinate 1 - glDisableVertexAttribArray(4); - glVertexAttrib2f(4, 0.0f, 0.0f); - } + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offset + offsetof(Vertex, normal))); + // Color + glDisableVertexAttribArray(2); glVertexAttrib4fv(2, color.Array()); - UpdateRenderingMode(); + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offset + offsetof(Vertex, texCoord))); - int firstOffset = offset / sizeof(Vertex); - - for (int i = 0; i < drawCount; i++) - first[i] += firstOffset; + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); glMultiDrawArrays(TranslateGfxPrimitive(type), first, count, drawCount); - - for (int i = 0; i < drawCount; i++) - first[i] -= firstOffset; } void CGL33Device::DrawPrimitives(PrimitiveType type, const VertexTex2 *vertices, int first[], int count[], int drawCount, Color color) { + if (m_updateLights) UpdateLights(); + VertexTex2* vs = const_cast(vertices); int vertexCount = 0; @@ -1291,57 +1273,45 @@ void CGL33Device::DrawPrimitives(PrimitiveType type, const VertexTex2 *vertices, unsigned int size = vertexCount * sizeof(VertexTex2); - DynamicBuffer& buffer = m_dynamicBuffers[1]; + DynamicBuffer& buffer = m_dynamicBuffer; BindVAO(buffer.vao); BindVBO(buffer.vbo); unsigned int offset = UploadVertexData(buffer, vs, size); - if (offset == 0) - { - // Vertex coordinate - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), - reinterpret_cast(offsetof(VertexTex2, coord))); + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), + reinterpret_cast(offset + offsetof(VertexTex2, coord))); - // Normal - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), - reinterpret_cast(offsetof(VertexTex2, normal))); - - // Color - glDisableVertexAttribArray(2); - - // Texture coordinate 0 - glEnableVertexAttribArray(3); - glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), - reinterpret_cast(offsetof(VertexTex2, texCoord))); - - // Texture coordinate 1 - glEnableVertexAttribArray(4); - glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), - reinterpret_cast(offsetof(VertexTex2, texCoord2))); - } + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), + reinterpret_cast(offset + offsetof(VertexTex2, normal))); + // Color + glDisableVertexAttribArray(2); glVertexAttrib4fv(2, color.Array()); - UpdateRenderingMode(); + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), + reinterpret_cast(offset + offsetof(VertexTex2, texCoord))); - int firstOffset = offset / sizeof(VertexTex2); - - for (int i = 0; i < drawCount; i++) - first[i] += firstOffset; + // Texture coordinate 1 + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), + reinterpret_cast(offset + offsetof(VertexTex2, texCoord2))); glMultiDrawArrays(TranslateGfxPrimitive(type), first, count, drawCount); - - for (int i = 0; i < drawCount; i++) - first[i] -= firstOffset; } void CGL33Device::DrawPrimitives(PrimitiveType type, const VertexCol *vertices, int first[], int count[], int drawCount) { + if (m_updateLights) UpdateLights(); + VertexCol* vs = const_cast(vertices); int vertexCount = 0; @@ -1356,49 +1326,36 @@ void CGL33Device::DrawPrimitives(PrimitiveType type, const VertexCol *vertices, unsigned int size = vertexCount * sizeof(VertexCol); - DynamicBuffer& buffer = m_dynamicBuffers[2]; + DynamicBuffer& buffer = m_dynamicBuffer; BindVAO(buffer.vao); BindVBO(buffer.vbo); unsigned int offset = UploadVertexData(buffer, vs, size); - if (offset == 0) - { - // Vertex coordinate - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol), - reinterpret_cast(offsetof(VertexCol, coord))); + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol), + reinterpret_cast(offset + offsetof(VertexCol, coord))); - // Normal - glDisableVertexAttribArray(1); - glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); + // Normal + glDisableVertexAttribArray(1); + glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); - // Color - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol), - reinterpret_cast(offsetof(VertexCol, color))); + // Color + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol), + reinterpret_cast(offset + offsetof(VertexCol, color))); - // Texture coordinate 0 - glDisableVertexAttribArray(3); - glVertexAttrib2f(3, 0.0f, 0.0f); + // Texture coordinate 0 + glDisableVertexAttribArray(3); + glVertexAttrib2f(3, 0.0f, 0.0f); - // Texture coordinate 1 - glDisableVertexAttribArray(4); - glVertexAttrib2f(4, 0.0f, 0.0f); - } - - UpdateRenderingMode(); - - int firstOffset = offset / sizeof(VertexCol); - - for (int i = 0; i < drawCount; i++) - first[i] += firstOffset; + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); glMultiDrawArrays(TranslateGfxPrimitive(type), first, count, drawCount); - - for (int i = 0; i < drawCount; i++) - first[i] -= firstOffset; } unsigned int CGL33Device::CreateStaticBuffer(PrimitiveType primitiveType, const Vertex* vertices, int vertexCount) @@ -1716,14 +1673,14 @@ void CGL33Device::UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primit void CGL33Device::DrawStaticBuffer(unsigned int bufferId) { + if (m_updateLights) UpdateLights(); + auto it = m_vboObjects.find(bufferId); if (it == m_vboObjects.end()) return; VertexBufferInfo &info = (*it).second; - UpdateRenderingMode(); - BindVAO(info.vao); GLenum mode = TranslateGfxPrimitive(info.primitiveType); @@ -1851,7 +1808,9 @@ void CGL33Device::SetRenderState(RenderState state, bool enabled) { m_lighting = enabled; - glUniform1i(m_uni->lightingEnabled, enabled ? 1 : 0); + m_updateLights = true; + + //glUniform1i(m_uni->lightingEnabled, enabled ? 1 : 0); return; } @@ -1977,13 +1936,9 @@ void CGL33Device::CopyFramebufferToTexture(Texture& texture, int xOffset, int yO { if (texture.id == 0) return; - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture.id); + BindTexture(m_freeTexture, texture.id); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, x, y, width, height); - - // Restore previous texture - glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id); } std::unique_ptr CGL33Device::GetFrameBufferPixels() const @@ -2029,16 +1984,52 @@ void CGL33Device::DeleteFramebuffer(std::string name) } } -void CGL33Device::UpdateRenderingMode() +inline void CGL33Device::UpdateTextureState(int index) { - bool enabled = m_texturesEnabled[0] && m_currentTextures[0].id != 0; - glUniform1i(m_uni->textureEnabled[0], enabled ? 1 : 0); + bool enabled = m_texturesEnabled[index] && (m_currentTextures[index].id != 0); + glUniform1i(m_uni->textureEnabled[index], enabled ? 1 : 0); +} - enabled = m_texturesEnabled[1] && m_currentTextures[1].id != 0; - glUniform1i(m_uni->textureEnabled[1], enabled ? 1 : 0); +void CGL33Device::UpdateLights() +{ + m_updateLights = false; - enabled = m_texturesEnabled[2] && m_currentTextures[2].id != 0; - glUniform1i(m_uni->textureEnabled[2], enabled ? 1 : 0); + // If not in normal rendering mode, return immediately + if (m_mode != 0) return; + + // Lighting enabled + if (m_lighting) + { + int index = 0; + + // Iterate all lights + for (unsigned int i = 0; i < m_lights.size(); i++) + { + // If disabled, ignore and continue + if (!m_lightsEnabled[i]) continue; + + // If not directional, ignore and continue + if (m_lights[i].type != LIGHT_DIRECTIONAL) continue; + + Light &light = m_lights[i]; + LightLocations &uni = m_uni->lights[index]; + + glUniform4fv(uni.ambient, 1, light.ambient.Array()); + glUniform4fv(uni.diffuse, 1, light.diffuse.Array()); + glUniform4fv(uni.specular, 1, light.specular.Array()); + + glUniform4f(uni.position, -light.direction.x, -light.direction.y, -light.direction.z, 0.0f); + + index++; + } + + glUniform1i(m_uni->lightCount, index); + } + // Lighting disabled + else + { + glUniform1i(m_uni->lightCount, 0); + } } inline void CGL33Device::BindVBO(GLuint vbo) @@ -2057,7 +2048,13 @@ inline void CGL33Device::BindVAO(GLuint vao) m_currentVAO = vao; } -unsigned int CGL33Device::UploadVertexData(DynamicBuffer& buffer, void* data, unsigned int size) +inline void CGL33Device::BindTexture(int index, GLuint texture) +{ + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, texture); +} + +unsigned int CGL33Device::UploadVertexData(DynamicBuffer& buffer, const void* data, unsigned int size) { unsigned int nextOffset = buffer.offset + size; @@ -2095,6 +2092,25 @@ unsigned int CGL33Device::UploadVertexData(DynamicBuffer& buffer, void* data, un return currentOffset; } +void CGL33Device::UpdateVertexAttribute(int index, const VertexAttribute &attribute, int offset) +{ + if (attribute.enabled) + { + glEnableVertexAttribArray(index); + glVertexAttribPointer(index, + attribute.size, + TranslateType(attribute.type), + attribute.normalized ? GL_TRUE : GL_FALSE, + attribute.stride, + reinterpret_cast(offset + attribute.offset)); + } + else + { + glDisableVertexAttribArray(index); + glVertexAttrib4fv(index, attribute.values); + } +} + bool CGL33Device::IsAnisotropySupported() { return m_capabilities.anisotropySupported; diff --git a/src/graphics/opengl/gl33device.h b/src/graphics/opengl/gl33device.h index a47fb999..12764b6f 100644 --- a/src/graphics/opengl/gl33device.h +++ b/src/graphics/opengl/gl33device.h @@ -115,6 +115,11 @@ public: void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT) override; + virtual void DrawPrimitive(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int vertexCount) override; + virtual void DrawPrimitives(PrimitiveType type, const void *vertices, + int size, const VertexFormat &format, int first[], int count[], int drawCount) override; + virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices , int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) override; virtual void DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, @@ -193,16 +198,22 @@ public: private: //! Updates the texture params for given texture stage void UpdateTextureParams(int index); - //! Updates rendering mode - void UpdateRenderingMode(); + //! Updates texture state + inline void UpdateTextureState(int index); + //! Update light parameters + void UpdateLights(); //! Binds VBO inline void BindVBO(GLuint vbo); //! Binds VAO inline void BindVAO(GLuint vao); + //! Binds texture + inline void BindTexture(int index, GLuint texture); //! Uploads data to dynamic buffer and returns offset to it - unsigned int UploadVertexData(DynamicBuffer& buffer, void* data, unsigned int size); + unsigned int UploadVertexData(DynamicBuffer& buffer, const void* data, unsigned int size); + + inline void UpdateVertexAttribute(int index, const VertexAttribute &attribute, int offset); private: //! Current config @@ -226,6 +237,8 @@ private: //! Whether lighting is enabled bool m_lighting = false; + //! true means that light update is needed + bool m_updateLights = false; //! Current lights std::vector m_lights; //! Current lights enable status @@ -240,6 +253,8 @@ private: //! Set of all created textures std::set m_allTextures; + //! Free texture unit + const int m_freeTexture = 3; //! Type of vertex structure enum VertexType @@ -283,7 +298,7 @@ private: //! Shader program for shadow rendering GLuint m_shadowProgram = 0; - DynamicBuffer m_dynamicBuffers[3]; + DynamicBuffer m_dynamicBuffer; //! Current mode unsigned int m_mode = 0; diff --git a/src/graphics/opengl/glutil.cpp b/src/graphics/opengl/glutil.cpp index bd6ae886..66018213 100644 --- a/src/graphics/opengl/glutil.cpp +++ b/src/graphics/opengl/glutil.cpp @@ -335,8 +335,10 @@ GLenum TranslateGfxPrimitive(PrimitiveType type) case PRIMITIVE_POINTS: flag = GL_POINTS; break; case PRIMITIVE_LINES: flag = GL_LINES; break; case PRIMITIVE_LINE_STRIP: flag = GL_LINE_STRIP; break; + case PRIMITIVE_LINE_LOOP: flag = GL_LINE_LOOP; break; case PRIMITIVE_TRIANGLES: flag = GL_TRIANGLES; break; case PRIMITIVE_TRIANGLE_STRIP: flag = GL_TRIANGLE_STRIP; break; + case PRIMITIVE_TRIANGLE_FAN: flag = GL_TRIANGLE_FAN; break; default: assert(false); break; } return flag; @@ -441,6 +443,23 @@ GLenum TranslateTextureCoordinateGen(int index) return textureCoordGen[index]; } +GLenum TranslateType(Type type) +{ + switch (type) + { + case Type::BYTE: return GL_BYTE; + case Type::UBYTE: return GL_UNSIGNED_BYTE; + case Type::SHORT: return GL_SHORT; + case Type::USHORT: return GL_UNSIGNED_SHORT; + case Type::INT: return GL_INT; + case Type::UINT: return GL_UNSIGNED_INT; + case Type::HALF: return GL_HALF_FLOAT; + case Type::FLOAT: return GL_FLOAT; + case Type::DOUBLE: return GL_DOUBLE; + default: return 0; + } +} + std::string lastShaderError; std::string GetLastShaderError() diff --git a/src/graphics/opengl/glutil.h b/src/graphics/opengl/glutil.h index 2da4cf20..f6b0f16f 100644 --- a/src/graphics/opengl/glutil.h +++ b/src/graphics/opengl/glutil.h @@ -93,6 +93,8 @@ GLenum TranslateTextureCoordinate(int index); GLenum TranslateTextureCoordinateGen(int index); +GLenum TranslateType(Type type); + std::string GetLastShaderError(); GLint LoadShader(GLint type, const char* filename); @@ -192,8 +194,8 @@ struct UniformLocations //! Shadow color GLint shadowColor = -1; - //! true enables lighting - GLint lightingEnabled = -1; + // Number of enabled lights + GLint lightCount = -1; //! Ambient color GLint ambientColor = -1; //! Diffuse color diff --git a/src/graphics/opengl/shaders/gl21/fs_normal.glsl b/src/graphics/opengl/shaders/gl21/fs_normal.glsl index fdf59ac2..d21bc9cf 100644 --- a/src/graphics/opengl/shaders/gl21/fs_normal.glsl +++ b/src/graphics/opengl/shaders/gl21/fs_normal.glsl @@ -19,6 +19,8 @@ // FRAGMENT SHADER - NORMAL MODE #version 120 +#define CONFIG_QUALITY_SHADOWS 1 + uniform sampler2D uni_PrimaryTexture; uniform sampler2D uni_SecondaryTexture; uniform sampler2DShadow uni_ShadowTexture; @@ -34,6 +36,26 @@ uniform vec4 uni_FogColor; uniform float uni_ShadowColor; +struct LightParams +{ + vec4 Position; + vec4 Ambient; + vec4 Diffuse; + vec4 Specular; +}; + +struct Material +{ + vec4 ambient; + vec4 diffuse; + vec4 specular; +}; + +uniform Material uni_Material; + +uniform int uni_LightCount; +uniform LightParams uni_Light[4]; + varying float pass_Distance; varying vec4 pass_Color; varying vec3 pass_Normal; @@ -47,6 +69,55 @@ void main() { vec4 color = pass_Color; + if (uni_LightCount > 0) + { + vec4 ambient = vec4(0.0f); + vec4 diffuse = vec4(0.0f); + vec4 specular = vec4(0.0f); + + vec3 normal = normalize(pass_Normal); + + for (int i = 0; i < uni_LightCount; i++) + { + LightParams light = uni_Light[i]; + + vec3 lightDirection = light.Position.xyz; + vec3 reflectDirection = -reflect(lightDirection, normal); + + float diffuseComponent = clamp(dot(normal, lightDirection), 0.0f, 1.0f); + float specularComponent = clamp(pow(dot(normal, lightDirection + reflectDirection), 10.0f), 0.0f, 1.0f); + + ambient += light.Ambient; + diffuse += diffuseComponent * light.Diffuse; + specular += specularComponent * light.Specular; + } + + float shadow = 1.0f; + + if (uni_TextureEnabled[2]) + { +#ifdef CONFIG_QUALITY_SHADOWS + float offset = 0.00025f; + + float value = (1.0f / 5.0f) * (shadow2D(uni_ShadowTexture, pass_TexCoord2).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3( offset, 0.0f, 0.0f)).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3(-offset, 0.0f, 0.0f)).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3( 0.0f, offset, 0.0f)).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3( 0.0f, -offset, 0.0f)).x); + + shadow = mix(uni_ShadowColor, 1.0f, value); +#else + shadow = mix(uni_ShadowColor, 1.0f, shadow2D(uni_ShadowTexture, pass_TexCoord2).x); +#endif + } + + vec4 result = ambient * uni_Material.ambient + + diffuse * uni_Material.diffuse * shadow + + specular * uni_Material.specular * shadow; + + color = clamp(vec4(result.rgb, 1.0f), 0.0f, 1.0f); + } + if (uni_TextureEnabled[0]) { color = color * texture2D(uni_PrimaryTexture, pass_TexCoord0); @@ -57,16 +128,6 @@ void main() color = color * texture2D(uni_SecondaryTexture, pass_TexCoord1); } - if (uni_TextureEnabled[2]) - { - vec3 normal = pass_Normal * (2.0f * gl_Color.x - 1.0f); - - if (dot(normal, const_LightDirection) < 0.0f) - color.rgb *= uni_ShadowColor; - else - color.rgb *= mix(uni_ShadowColor, 1.0f, shadow2D(uni_ShadowTexture, pass_TexCoord2).x); - } - if (uni_FogEnabled) { float interpolate = (pass_Distance - uni_FogRange.x) / (uni_FogRange.y - uni_FogRange.x); diff --git a/src/graphics/opengl/shaders/gl21/vs_normal.glsl b/src/graphics/opengl/shaders/gl21/vs_normal.glsl index 117f09b5..9195cd20 100644 --- a/src/graphics/opengl/shaders/gl21/vs_normal.glsl +++ b/src/graphics/opengl/shaders/gl21/vs_normal.glsl @@ -25,33 +25,6 @@ uniform mat4 uni_ModelMatrix; uniform mat4 uni_ShadowMatrix; uniform mat4 uni_NormalMatrix; -struct LightParams -{ - bool Enabled; - int Type; - vec4 Position; - vec4 Ambient; - vec4 Diffuse; - vec4 Specular; - float Shininess; - vec3 Attenuation; - vec3 SpotDirection; - float SpotCutoff; - float SpotExponent; -}; - -struct Material -{ - vec4 ambient; - vec4 diffuse; - vec4 specular; -}; - -uniform Material uni_Material; - -uniform bool uni_LightingEnabled; -uniform LightParams uni_Light[8]; - varying float pass_Distance; varying vec4 pass_Color; varying vec3 pass_Normal; @@ -65,75 +38,11 @@ void main() vec4 eyeSpace = uni_ViewMatrix * position; vec4 shadowCoord = uni_ShadowMatrix * position; - vec4 color = gl_Color; - - vec3 normal = normalize((uni_NormalMatrix * vec4(gl_Normal, 0.0f)).xyz); - - if (uni_LightingEnabled) - { - vec4 ambient = vec4(0.0f); - vec4 diffuse = vec4(0.0f); - vec4 specular = vec4(0.0f); - - for (int i = 0; i < 8; i++) - { - if (uni_Light[i].Enabled) - { - LightParams light = uni_Light[i]; - - vec3 lightDirection = light.Position.xyz; - float atten = 1.0f; - - if (light.Position.w > 0.5f) - { - float dist = distance(light.Position.xyz, position.xyz); - - float atten = 1.0f / dot(light.Attenuation, - vec3(1.0f, dist, dist * dist)); - - lightDirection = normalize(light.Position.xyz - position.xyz); - } - - float spot = 1.0f; - - if (light.SpotCutoff > 0.0f) - { - float cone = dot(light.SpotDirection, lightDirection); - - if (cone > light.SpotCutoff) - { - spot = pow(cone, light.SpotExponent); - } - else - { - continue; - } - } - - vec3 reflectDirection = -reflect(lightDirection, normal); - - float component = atten * spot; - float diffuseComponent = clamp(dot(normal, lightDirection), 0.0f, 1.0f); - float specularComponent = clamp(pow(dot(normal, lightDirection + reflectDirection), light.Shininess), 0.0f, 1.0f); - - ambient += component * light.Ambient * uni_Material.ambient; - diffuse += component * diffuseComponent * light.Diffuse * uni_Material.diffuse; - specular += component * specularComponent * light.Specular * uni_Material.specular; - } - } - - vec4 result = ambient + diffuse + specular; - - color = clamp(vec4(result.rgb, uni_Material.diffuse), 0.0f, 1.0f); - } - gl_Position = uni_ProjectionMatrix * eyeSpace; - gl_FrontColor = vec4(1.0f); - gl_BackColor = vec4(0.0f); + pass_Color = gl_Color; + pass_Normal = normalize((uni_NormalMatrix * vec4(gl_Normal, 0.0f)).xyz); pass_Distance = abs(eyeSpace.z / eyeSpace.w); - pass_Color = color; - pass_Normal = normal; pass_TexCoord0 = gl_MultiTexCoord0.st; pass_TexCoord1 = gl_MultiTexCoord1.st; pass_TexCoord2 = shadowCoord.xyz / shadowCoord.w; diff --git a/src/graphics/opengl/shaders/gl33/fs_normal.glsl b/src/graphics/opengl/shaders/gl33/fs_normal.glsl index 200aadef..2827d7a2 100644 --- a/src/graphics/opengl/shaders/gl33/fs_normal.glsl +++ b/src/graphics/opengl/shaders/gl33/fs_normal.glsl @@ -20,6 +20,8 @@ // FRAGMENT SHADER - NORMAL MODE #version 330 core +#define CONFIG_QUALITY_SHADOWS 1 + uniform sampler2D uni_PrimaryTexture; uniform sampler2D uni_SecondaryTexture; uniform sampler2DShadow uni_ShadowTexture; @@ -37,11 +39,27 @@ uniform float uni_ShadowColor; uniform bool uni_AlphaTestEnabled; uniform float uni_AlphaReference; +struct LightParams +{ + vec4 Position; + vec4 Ambient; + vec4 Diffuse; + vec4 Specular; +}; + +uniform vec4 uni_AmbientColor; +uniform vec4 uni_DiffuseColor; +uniform vec4 uni_SpecularColor; + +uniform int uni_LightCount; +uniform LightParams uni_Light[4]; + in VertexData { vec4 Color; vec2 TexCoord0; vec2 TexCoord1; + vec3 Normal; vec4 ShadowCoord; vec4 LightColor; float Distance; @@ -53,6 +71,53 @@ void main() { vec4 color = data.Color; + if (uni_LightCount > 0) + { + vec4 ambient = vec4(0.0f); + vec4 diffuse = vec4(0.0f); + vec4 specular = vec4(0.0f); + + vec3 normal = normalize(data.Normal); + + for (int i = 0; i < uni_LightCount; i++) + { + vec3 lightDirection = uni_Light[i].Position.xyz; + + vec3 reflectDirection = -reflect(lightDirection, normal); + + ambient += uni_Light[i].Ambient; + diffuse += clamp(dot(normal, lightDirection), 0.0f, 1.0f) + * uni_Light[i].Diffuse; + specular += clamp(pow(dot(normal, lightDirection + reflectDirection), 10.0f), 0.0f, 1.0f) + * uni_Light[i].Specular; + } + + float shadow = 1.0f; + + if (uni_ShadowTextureEnabled) + { +#ifdef CONFIG_QUALITY_SHADOWS + float offset = 0.00025f; + + float value = (1.0f / 5.0f) * (texture(uni_ShadowTexture, data.ShadowCoord.xyz) + + texture(uni_ShadowTexture, data.ShadowCoord.xyz + vec3( offset, 0.0f, 0.0f)) + + texture(uni_ShadowTexture, data.ShadowCoord.xyz + vec3(-offset, 0.0f, 0.0f)) + + texture(uni_ShadowTexture, data.ShadowCoord.xyz + vec3( 0.0f, offset, 0.0f)) + + texture(uni_ShadowTexture, data.ShadowCoord.xyz + vec3( 0.0f, -offset, 0.0f))); + + shadow = mix(uni_ShadowColor, 1.0f, value); +#else + shadow = mix(uni_ShadowColor, 1.0f, texture(uni_ShadowTexture, data.ShadowCoord.xyz)); +#endif + } + + vec4 result = uni_AmbientColor * ambient + + uni_DiffuseColor * diffuse * shadow + + uni_SpecularColor * specular * shadow; + + color = vec4(min(vec3(1.0f), result.rgb), 1.0f); + } + if (uni_PrimaryTextureEnabled) { color = color * texture(uni_PrimaryTexture, data.TexCoord0); @@ -63,11 +128,6 @@ void main() color = color * texture(uni_SecondaryTexture, data.TexCoord1); } - if (uni_ShadowTextureEnabled) - { - color = color * mix(uni_ShadowColor, 1.0f, texture(uni_ShadowTexture, data.ShadowCoord.xyz)); - } - if (uni_FogEnabled) { float interpolate = (data.Distance - uni_FogRange.x) / (uni_FogRange.y - uni_FogRange.x); diff --git a/src/graphics/opengl/shaders/gl33/vs_normal.glsl b/src/graphics/opengl/shaders/gl33/vs_normal.glsl index 2c1b5bc6..aeed3c60 100644 --- a/src/graphics/opengl/shaders/gl33/vs_normal.glsl +++ b/src/graphics/opengl/shaders/gl33/vs_normal.glsl @@ -20,24 +20,6 @@ // VERTEX SHADER - NORMAL MODE #version 330 core -struct LightParams -{ - bool Enabled; - vec4 Position; - vec4 Ambient; - vec4 Diffuse; - vec4 Specular; - float Shininess; - vec3 Attenuation; -}; - -uniform vec4 uni_AmbientColor; -uniform vec4 uni_DiffuseColor; -uniform vec4 uni_SpecularColor; - -uniform bool uni_LightingEnabled; -uniform LightParams uni_Light[8]; - uniform mat4 uni_ProjectionMatrix; uniform mat4 uni_ViewMatrix; uniform mat4 uni_ModelMatrix; @@ -55,6 +37,7 @@ out VertexData vec4 Color; vec2 TexCoord0; vec2 TexCoord1; + vec3 Normal; vec4 ShadowCoord; vec4 LightColor; float Distance; @@ -70,58 +53,7 @@ void main() data.Color = in_Color; data.TexCoord0 = in_TexCoord0; data.TexCoord1 = in_TexCoord1; + data.Normal = normalize((uni_NormalMatrix * vec4(in_Normal, 0.0f)).xyz); data.ShadowCoord = vec4(shadowCoord.xyz / shadowCoord.w, 1.0f); data.Distance = abs(eyeSpace.z); - - vec4 color = in_Color; - - if (uni_LightingEnabled) - { - vec4 ambient = vec4(0.0f); - vec4 diffuse = vec4(0.0f); - vec4 specular = vec4(0.0f); - - vec3 normal = normalize((uni_NormalMatrix * vec4(in_Normal, 0.0f)).xyz); - - for(int i=0; i<8; i++) - { - if(uni_Light[i].Enabled) - { - vec3 lightDirection = vec3(0.0f); - float atten; - - // Directional light - if(uni_Light[i].Position[3] == 0.0f) - { - lightDirection = uni_Light[i].Position.xyz; - atten = 1.0f; - } - // Point light - else - { - vec3 lightDirection = normalize(uni_Light[i].Position.xyz - position.xyz); - float dist = distance(uni_Light[i].Position.xyz, position.xyz); - - atten = 1.0f / (uni_Light[i].Attenuation.x - + uni_Light[i].Attenuation.y * dist - + uni_Light[i].Attenuation.z * dist * dist); - } - - vec3 reflectDirection = -reflect(lightDirection, normal); - - ambient += uni_Light[i].Ambient; - diffuse += atten * clamp(dot(normal, lightDirection), 0.0f, 1.0f) * uni_Light[i].Diffuse; - specular += atten * clamp(pow(dot(normal, lightDirection + reflectDirection), 10.0f), 0.0f, 1.0f) * uni_Light[i].Specular; - } - } - - vec4 result = uni_AmbientColor * ambient - + uni_DiffuseColor * diffuse - + uni_SpecularColor * specular; - - color.rgb = min(vec3(1.0f), result.rgb); - color.a = 1.0f; //min(1.0f, 1.0f); - - data.Color = color; - } } diff --git a/src/level/parser/parserparam.cpp b/src/level/parser/parserparam.cpp index fb3c82f7..37773efa 100644 --- a/src/level/parser/parserparam.cpp +++ b/src/level/parser/parserparam.cpp @@ -467,6 +467,8 @@ ObjectType CLevelParserParam::ToObjectType(std::string value) if (value == "Barrier1" ) return OBJECT_BARRIER1; if (value == "Barrier2" ) return OBJECT_BARRIER2; if (value == "Barrier3" ) return OBJECT_BARRIER3; + if (value == "Barricade0" ) return OBJECT_BARRICADE0; + if (value == "Barricade1" ) return OBJECT_BARRICADE1; if (value == "Teen0" ) return OBJECT_TEEN0; if (value == "Teen1" ) return OBJECT_TEEN1; if (value == "Teen2" ) return OBJECT_TEEN2; @@ -662,6 +664,8 @@ const std::string CLevelParserParam::FromObjectType(ObjectType value) if (value == OBJECT_BARRIER1 ) return "Barrier1"; if (value == OBJECT_BARRIER2 ) return "Barrier2"; if (value == OBJECT_BARRIER3 ) return "Barrier3"; + if (value == OBJECT_BARRICADE0 ) return "Barricade0"; + if (value == OBJECT_BARRICADE1 ) return "Barricade1"; if (value == OBJECT_TEEN0 ) return "Teen0"; if (value == OBJECT_TEEN1 ) return "Teen1"; if (value == OBJECT_TEEN2 ) return "Teen2"; diff --git a/src/level/player_profile.cpp b/src/level/player_profile.cpp index d3603f1c..bedd76cd 100644 --- a/src/level/player_profile.cpp +++ b/src/level/player_profile.cpp @@ -133,7 +133,7 @@ std::string CPlayerProfile::GetLastName() { std::string name; - if(!GetConfigFile().GetStringProperty("Gamer", "LastName", name)) + if(!GetConfigFile().GetStringProperty("Gamer", "LastName", name) || name.empty()) GetResource(RES_TEXT, RT_NAME_DEFAULT, name); return name; diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index cfb98b4e..6159652e 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -55,6 +55,7 @@ #include "level/mainmovie.h" #include "level/player_profile.h" #include "level/scene_conditions.h" +#include "level/scoreboard.h" #include "level/parser/parser.h" @@ -202,8 +203,8 @@ CRobotMain::CRobotMain() m_editLock = false; m_editFull = false; m_hilite = false; - m_selectInsect = false; - m_showSoluce = false; + m_cheatSelectInsect = false; + m_cheatShowSoluce = false; m_codeBattleInit = false; m_codeBattleStarted = false; @@ -211,14 +212,14 @@ CRobotMain::CRobotMain() m_teamNames.clear(); #if DEV_BUILD - m_showAll = true; // for development + m_cheatAllMission = true; // for development #else - m_showAll = false; + m_cheatAllMission = false; #endif m_cheatRadar = false; m_fixScene = false; - m_trainerPilot = false; + m_cheatTrainerPilot = false; m_friendAim = false; m_resetCreate = false; m_shortCut = true; @@ -451,7 +452,7 @@ void CRobotMain::ChangePhase(Phase phase) m_lightning->Flush(); m_planet->Flush(); m_interface->Flush(); - FlushNewScriptName(); + m_newScriptName.clear(); m_sound->SetListener(Math::Vector(0.0f, 0.0f, 0.0f), Math::Vector(0.0f, 0.0f, 1.0f)); m_sound->StopAll(); m_camera->SetType(Gfx::CAM_TYPE_NULL); @@ -763,6 +764,7 @@ bool CRobotMain::ProcessEvent(Event &event) m_interface->SetFocus(pe); if (m_phase == PHASE_SIMUL) m_cmdEditPause = m_pause->ActivatePause(PAUSE_ENGINE); m_cmdEdit = true; + m_commandHistoryIndex = -1; // no element selected in command history } return false; } @@ -777,6 +779,28 @@ bool CRobotMain::ProcessEvent(Event &event) } } + // Browse forward command history with UP key + if (event.type == EVENT_KEY_DOWN && + event.GetData()->key == KEY(UP) && m_cmdEdit) + { + Ui::CEdit* pe = static_cast(m_interface->SearchControl(EVENT_CMD)); + if (pe == nullptr) return false; + std::string cmd = GetNextFromCommandHistory(); + if (!cmd.empty()) pe->SetText(cmd); + return false; + } + + // Browse backward command history with DOWN key + if (event.type == EVENT_KEY_DOWN && + event.GetData()->key == KEY(DOWN) && m_cmdEdit) + { + Ui::CEdit* pe = static_cast(m_interface->SearchControl(EVENT_CMD)); + if (pe == nullptr) return false; + std::string cmd = GetPreviousFromCommandHistory(); + if (!cmd.empty()) pe->SetText(cmd); + return false; + } + if (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(RETURN) && m_cmdEdit) { @@ -793,6 +817,7 @@ bool CRobotMain::ProcessEvent(Event &event) m_cmdEditPause = nullptr; } ExecuteCmd(cmd); + PushToCommandHistory(cmd); m_cmdEdit = false; return false; } @@ -922,7 +947,7 @@ bool CRobotMain::ProcessEvent(Event &event) } if (data->slot == INPUT_SLOT_HUMAN) { - SelectHuman(); + SelectObject(SearchHuman()); } if (data->slot == INPUT_SLOT_NEXT && ((event.kmodState & KEY_MOD(CTRL)) != 0)) { @@ -1159,7 +1184,7 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) if (cmd == "trainerpilot") { - m_trainerPilot = !m_trainerPilot; + m_cheatTrainerPilot = !m_cheatTrainerPilot; return; } @@ -1410,20 +1435,20 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) if (cmd == "selectinsect") { - m_selectInsect = !m_selectInsect; + m_cheatSelectInsect = !m_cheatSelectInsect; return; } if (cmd == "showsoluce") { - m_showSoluce = !m_showSoluce; + m_cheatShowSoluce = !m_cheatShowSoluce; m_ui->ShowSoluceUpdate(); return; } if (cmd == "allmission") { - m_showAll = !m_showAll; + m_cheatAllMission = !m_cheatAllMission; m_ui->AllMissionUpdate(); return; } @@ -1763,20 +1788,17 @@ void CRobotMain::StopDisplayVisit() -//! Updates all the shortcuts void CRobotMain::UpdateShortcuts() { m_short->UpdateShortcuts(); } -//! Returns the object that default was select after the creation of a scene CObject* CRobotMain::GetSelectObject() { if (m_selectObject != nullptr) return m_selectObject; return SearchHuman(); } -//! Deselects everything, and returns the object that was selected CObject* CRobotMain::DeselectAll() { CObject* prev = nullptr; @@ -1833,17 +1855,8 @@ void CRobotMain::SelectOneObject(CObject* obj, bool displayError) { m_camera->SetType(Gfx::CAM_TYPE_BACK); } - - CObject* toto = SearchToto(); - if (toto != nullptr) - { - assert(toto->Implements(ObjectInterfaceType::Movable)); - CMotionToto* mt = static_cast(dynamic_cast(toto)->GetMotion()); - mt->SetLinkType(type); - } } -//! Selects the object aimed by the mouse bool CRobotMain::SelectObject(CObject* obj, bool displayError) { if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT) @@ -1851,7 +1864,8 @@ bool CRobotMain::SelectObject(CObject* obj, bool displayError) if (m_movieLock || m_editLock) return false; if (m_movie->IsExist()) return false; - if (obj != nullptr && !IsSelectable(obj)) return false; + if (obj != nullptr && + (!obj->Implements(ObjectInterfaceType::Controllable) || !(dynamic_cast(obj)->GetSelectable() || m_cheatSelectInsect))) return false; if (m_missionType == MISSION_CODE_BATTLE && m_codeBattleStarted && m_codeBattleSpectator) { @@ -1882,7 +1896,6 @@ bool CRobotMain::SelectObject(CObject* obj, bool displayError) return true; } -//! Deselects the selected object bool CRobotMain::DeselectObject() { DeselectAll(); @@ -1918,49 +1931,11 @@ void CRobotMain::DeleteAllObjects() m_objMan->DeleteAllObjects(); } -//! Selects the human -void CRobotMain::SelectHuman() -{ - SelectObject(SearchHuman()); -} - -//! Returns the object human CObject* CRobotMain::SearchHuman() { return m_objMan->FindNearest(nullptr, OBJECT_HUMAN); } -//! Returns the object toto -CObject* CRobotMain::SearchToto() -{ - return m_objMan->FindNearest(nullptr, OBJECT_TOTO); -} - -//! Returns the nearest selectable object from a given position -CObject* CRobotMain::SearchNearest(Math::Vector pos, CObject* exclu) -{ - float min = 100000.0f; - CObject* best = nullptr; - for (CObject* obj : m_objMan->GetAllObjects()) - { - if (obj == exclu) continue; - if (!IsSelectable(obj)) continue; - - ObjectType type = obj->GetType(); - if (type == OBJECT_TOTO) continue; - - Math::Vector oPos = obj->GetPosition(); - float dist = Math::DistanceProjected(oPos, pos); - if (dist < min) - { - min = dist; - best = obj; - } - } - return best; -} - -//! Returns the selected object CObject* CRobotMain::GetSelect() { for (CObject* obj : m_objMan->GetAllObjects()) @@ -2019,30 +1994,6 @@ CObject* CRobotMain::DetectObject(Math::Point pos) return nullptr; } -//! Indicates whether an object is selectable -// TODO: Refactor this, calling CControllableObject::GetSelectable should always be enough -bool CRobotMain::IsSelectable(CObject* obj) -{ - if (obj->GetType() == OBJECT_TOTO) return true; - if (!obj->Implements(ObjectInterfaceType::Controllable)) return false; - - if (!m_selectInsect) - { - // TODO: Some function in CControllableObject - if ( obj->GetType() == OBJECT_MOTHER || - obj->GetType() == OBJECT_ANT || - obj->GetType() == OBJECT_SPIDER || - obj->GetType() == OBJECT_BEE || - obj->GetType() == OBJECT_WORM || - obj->GetType() == OBJECT_MOBILEtg ) - { - return false; - } - } - - return dynamic_cast(obj)->GetSelectable(); -} - //! Deletes the selected object bool CRobotMain::DestroySelectedObject() @@ -2136,10 +2087,13 @@ void CRobotMain::HiliteObject(Math::Point pos) } } - if (IsSelectable(obj)) + if (obj->Implements(ObjectInterfaceType::Controllable) && (dynamic_cast(obj)->GetSelectable() || m_cheatSelectInsect)) { - assert(obj->Implements(ObjectInterfaceType::Controllable)); - dynamic_cast(obj)->SetHighlight(true); + if (dynamic_cast(obj)->GetSelectable()) + { + // Don't highlight objects that would not be selectable without selectinsect + dynamic_cast(obj)->SetHighlight(true); + } m_map->SetHighlight(obj); m_short->SetHighlight(obj); m_hilite = true; @@ -2565,7 +2519,7 @@ bool CRobotMain::EventFrame(const Event &event) if (m_phase == PHASE_SIMUL) { - if (!m_editLock) + if (!m_editLock && !m_engine->GetPause()) { CheckEndMission(true); UpdateAudio(true); @@ -2589,31 +2543,34 @@ bool CRobotMain::EventFrame(const Event &event) if (m_lostDelay <= 0.0f) { if (m_movieLock) - m_winDelay = 1.0f; + m_lostDelay = 1.0f; else m_eventQueue->AddEvent(Event(EVENT_LOST)); } } - } - if (GetMissionType() == MISSION_CODE_BATTLE) - { - if (!m_codeBattleInit) + if (GetMissionType() == MISSION_CODE_BATTLE) { - // NOTE: It's important to do this AFTER the first update event finished processing - // because otherwise all robot parts are misplaced - m_userPause = m_pause->ActivatePause(PAUSE_ENGINE); - m_codeBattleInit = true; // Will start on resume - } + if (!m_codeBattleInit) + { + // NOTE: It's important to do this AFTER the first update event finished processing + // because otherwise all robot parts are misplaced + m_userPause = m_pause->ActivatePause(PAUSE_ENGINE); + m_codeBattleInit = true; // Will start on resume + } - if (!m_codeBattleStarted && m_userPause == nullptr) - { - m_codeBattleStarted = true; - CreateCodeBattleInterface(); + if (!m_codeBattleStarted && m_userPause == nullptr) + { + m_codeBattleStarted = true; + ApplyCodeBattleInterface(); + CreateCodeBattleInterface(); - SetCodeBattleSpectatorMode(true); + SetCodeBattleSpectatorMode(true); - m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); + m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); + } + + UpdateCodeBattleInterface(); } } @@ -2672,7 +2629,6 @@ bool CRobotMain::EventObject(const Event &event) -//! Load the scene for the character void CRobotMain::ScenePerso() { DeleteAllObjects(); // removes all the current 3D Scene @@ -2743,6 +2699,8 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) m_endTakeResearch = 0; m_endTakeWinDelay = 2.0f; m_endTakeLostDelay = 2.0f; + m_teamFinished.clear(); + m_scoreboard.reset(); m_globalMagnifyDamage = 1.0f; m_obligatoryTokens.clear(); m_mapShow = true; @@ -3365,6 +3323,9 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) assert(m_controller->Implements(ObjectInterfaceType::Programmable)); assert(m_controller->Implements(ObjectInterfaceType::ProgramStorage)); + assert(m_controller->Implements(ObjectInterfaceType::Old)); + dynamic_cast(m_controller)->SetCheckToken(false); + if (line->GetParam("script")->IsDefined()) { CProgramStorageObject* programStorage = dynamic_cast(m_controller); @@ -3600,6 +3561,34 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) continue; } + if (line->GetCommand() == "Scoreboard" && !resetObject) + { + if (line->GetParam("enable")->AsBool(false)) + { + // Create the scoreboard + m_scoreboard = MakeUnique(); + } + continue; + } + if (line->GetCommand() == "ScoreboardKillRule" && !resetObject) + { + if (!m_scoreboard) + throw CLevelParserException("ScoreboardKillRule encountered but scoreboard is not enabled"); + auto rule = MakeUnique(); + rule->Read(line.get()); + m_scoreboard->AddKillRule(std::move(rule)); + continue; + } + if (line->GetCommand() == "ScoreboardEndTakeRule" && !resetObject) + { + if (!m_scoreboard) + throw CLevelParserException("ScoreboardEndTakeRule encountered but scoreboard is not enabled"); + auto rule = MakeUnique(); + rule->Read(line.get()); + m_scoreboard->AddEndTakeRule(std::move(rule)); + continue; + } + if (line->GetCommand() == "ObligatoryToken" && !resetObject) { std::string token = line->GetParam("text")->AsString(); @@ -3649,7 +3638,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) if (line->GetCommand() == "NewScript" && !resetObject) { - AddNewScriptName(line->GetParam("type")->AsObjectType(OBJECT_NULL), const_cast(line->GetParam("name")->AsPath("ai").c_str())); + m_newScriptName.push_back(NewScriptName(line->GetParam("type")->AsObjectType(OBJECT_NULL), const_cast(line->GetParam("name")->AsPath("ai").c_str()))); continue; } @@ -3970,15 +3959,17 @@ void CRobotMain::ChangeColor() } //! Calculates the distance to the nearest object -float CRobotMain::SearchNearestObject(Math::Vector center, CObject *exclu) +namespace +{ +float SearchNearestObject(CObjectManager* objMan, Math::Vector center, CObject* exclu) { float min = 100000.0f; - for (CObject* obj : m_objMan->GetAllObjects()) + for (CObject* obj : objMan->GetAllObjects()) { if (!obj->GetDetectable()) continue; // inactive? if (IsObjectBeingTransported(obj)) continue; - if (obj == exclu) continue; + if (obj == exclu) continue; ObjectType type = obj->GetType(); @@ -3988,35 +3979,36 @@ float CRobotMain::SearchNearestObject(Math::Vector center, CObject *exclu) if (oPos.x != center.x || oPos.z != center.z) { - float dist = Math::Distance(center, oPos)-80.0f; + float dist = Math::Distance(center, oPos) - 80.0f; if (dist < 0.0f) dist = 0.0f; min = Math::Min(min, dist); continue; } } - if (type == OBJECT_STATION || - type == OBJECT_REPAIR || + if (type == OBJECT_STATION || + type == OBJECT_REPAIR || type == OBJECT_DESTROYER) { Math::Vector oPos = obj->GetPosition(); - float dist = Math::Distance(center, oPos)-8.0f; + float dist = Math::Distance(center, oPos) - 8.0f; if (dist < 0.0f) dist = 0.0f; min = Math::Min(min, dist); } - for (const auto& crashSphere : obj->GetAllCrashSpheres()) + for (const auto &crashSphere : obj->GetAllCrashSpheres()) { Math::Vector oPos = crashSphere.sphere.pos; float oRadius = crashSphere.sphere.radius; - float dist = Math::Distance(center, oPos)-oRadius; + float dist = Math::Distance(center, oPos) - oRadius; if (dist < 0.0f) dist = 0.0f; min = Math::Min(min, dist); } } return min; } +} //! Calculates a free space bool CRobotMain::FreeSpace(Math::Vector ¢er, float minRadius, float maxRadius, @@ -4038,7 +4030,7 @@ bool CRobotMain::FreeSpace(Math::Vector ¢er, float minRadius, float maxRadiu pos.z = p.y; pos.y = 0.0f; m_terrain->AdjustToFloor(pos, true); - float dist = SearchNearestObject(pos, exclu); + float dist = SearchNearestObject(m_objMan.get(), pos, exclu); if (dist >= space) { float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f); @@ -4067,7 +4059,7 @@ bool CRobotMain::FreeSpace(Math::Vector ¢er, float minRadius, float maxRadiu pos.z = p.y; pos.y = 0.0f; m_terrain->AdjustToFloor(pos, true); - float dist = SearchNearestObject(pos, exclu); + float dist = SearchNearestObject(m_objMan.get(), pos, exclu); if (dist >= space) { float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f); @@ -4103,7 +4095,7 @@ bool CRobotMain::FlatFreeSpace(Math::Vector ¢er, float minFlat, float minRad pos.z = p.y; pos.y = 0.0f; m_terrain->AdjustToFloor(pos, true); - float dist = SearchNearestObject(pos, exclu); + float dist = SearchNearestObject(m_objMan.get(), pos, exclu); if (dist >= space) { float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f); @@ -4136,7 +4128,7 @@ bool CRobotMain::FlatFreeSpace(Math::Vector ¢er, float minFlat, float minRad pos.z = p.y; pos.y = 0.0f; m_terrain->AdjustToFloor(pos, true); - float dist = SearchNearestObject(pos, exclu); + float dist = SearchNearestObject(m_objMan.get(), pos, exclu); if (dist >= space) { float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f); @@ -4160,7 +4152,7 @@ bool CRobotMain::FlatFreeSpace(Math::Vector ¢er, float minFlat, float minRad float CRobotMain::GetFlatZoneRadius(Math::Vector center, float maxRadius, CObject *exclu) { - float dist = SearchNearestObject(center, exclu); + float dist = SearchNearestObject(m_objMan.get(), center, exclu); if (dist == 0.0f) return 0.0f; if (dist < maxRadius) maxRadius = dist; @@ -4432,36 +4424,19 @@ bool CRobotMain::ReadFileStack(CObject *obj, FILE *file, int objRank) return programmable->ReadStack(file); } - -//! Empty the list -void CRobotMain::FlushNewScriptName() +std::vector CRobotMain::GetNewScriptNames(ObjectType type) { - m_newScriptName.clear(); -} - -//! Adds a script name -void CRobotMain::AddNewScriptName(ObjectType type, const std::string& name) -{ - NewScriptName newscript; - newscript.type = type; - newscript.name = name; - m_newScriptName.push_back(newscript); -} - -//! Seeks a script name for a given type -std::string CRobotMain::GetNewScriptName(ObjectType type, int rank) -{ - for (unsigned int i = 0; i < m_newScriptName.size(); i++) + std::vector names; + for (const auto& newScript : m_newScriptName) { - if (m_newScriptName[i].type == type || - m_newScriptName[i].type == OBJECT_NULL ) + if (newScript.type == type || + newScript.type == OBJECT_NULL ) { - if (rank == 0) return m_newScriptName[i].name; - else rank --; + names.push_back(newScript.name); } } - return ""; + return names; } @@ -4960,56 +4935,81 @@ Error CRobotMain::ProcessEndMissionTakeForGroup(std::vector Error CRobotMain::ProcessEndMissionTake() { // Sort end conditions by teams - std::map> teams; + std::map> teamsEndTake; for (std::unique_ptr& endTake : m_endTake) - teams[endTake->winTeam].push_back(endTake.get()); + teamsEndTake[endTake->winTeam].push_back(endTake.get()); - int teamCount = 0; - bool usesTeamConditions = false; - for (auto it : teams) - { - int team = it.first; - if (team == 0) continue; - usesTeamConditions = true; - if (!m_objMan->TeamExists(team)) continue; - teamCount++; - } + // This is just a smart way to check if we have any map values other than 0 defined + bool usesTeamConditions = teamsEndTake.size() > teamsEndTake.count(0); if (!usesTeamConditions) { - m_missionResult = ProcessEndMissionTakeForGroup(teams[0]); + m_missionResult = ProcessEndMissionTakeForGroup(teamsEndTake[0]); } else { // Special handling for teams m_missionResult = ERR_MISSION_NOTERM; - if (teamCount == 0) + if (GetAllActiveTeams().empty()) { - GetLogger()->Info("All teams died, mission ended with failure\n"); - m_missionResult = INFO_LOST; + GetLogger()->Info("All teams died, mission ended\n"); + if (m_scoreboard) + { + std::string title, text, details_line; + GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS, title); + GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS_TEXT, text); + GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS_LINE, details_line); + std::string details = ""; + for (int team : GetAllTeams()) + { + if (!details.empty()) + details += ", "; + details += StrUtils::Format(details_line.c_str(), GetTeamName(team).c_str(), m_scoreboard->GetScore(team)); + } + m_ui->GetDialog()->StartInformation( + title, + text, + details, + false, true, + [&]() { + ChangePhase(PHASE_WIN); + } + ); + m_endTakeWinDelay = 0.0f; + m_missionResult = ERR_OK; + } + else + { + m_missionResult = INFO_LOST; + } } else { - for (auto it : teams) + for (auto it : teamsEndTake) { int team = it.first; if (team == 0) continue; - if (!m_objMan->TeamExists(team)) continue; + if (m_teamFinished[team]) continue; Error result = ProcessEndMissionTakeForGroup(it.second); if (result == INFO_LOST || result == INFO_LOSTq) { GetLogger()->Info("Team %d lost\n", team); - m_displayText->DisplayText(("<<< Team "+boost::lexical_cast(team)+" lost! >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 10.0f, Ui::TT_ERROR); + std::string text; + GetResource(RES_ERR, INFO_TEAM_DEAD, text); + text = StrUtils::Format(text.c_str(), GetTeamName(team).c_str()); + m_displayText->DisplayText(text.c_str(), Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 10.0f, Ui::TT_ERROR); m_displayText->SetEnable(false); // To prevent "bot destroyed" messages m_objMan->DestroyTeam(team); m_displayText->SetEnable(true); + + m_teamFinished[team] = true; } else if (result == ERR_OK) { - if (m_winDelay == 0.0f) + /*if (m_winDelay == 0.0f) { GetLogger()->Info("Team %d won\n", team); @@ -5025,7 +5025,16 @@ Error CRobotMain::ProcessEndMissionTake() m_displayText->SetEnable(false); } m_missionResult = ERR_OK; - return ERR_OK; + return ERR_OK;*/ + GetLogger()->Info("Team %d finished\n", team); + std::string text; + GetResource(RES_ERR, INFO_TEAM_FINISH, text); + text = StrUtils::Format(text.c_str(), GetTeamName(team).c_str()); + m_displayText->DisplayText(text.c_str(), Math::Vector(0.0f,0.0f,0.0f)); + if (m_scoreboard) + m_scoreboard->ProcessEndTake(team); + m_objMan->DestroyTeam(team, DestructionType::Win); + m_teamFinished[team] = true; } } } @@ -5054,7 +5063,6 @@ Error CRobotMain::CheckEndMission(bool frame) Error result = ProcessEndMissionTake(); if (result != ERR_MISSION_NOTERM) return result; } - // Take action depending on m_missionResult if (m_missionResult == INFO_LOSTq) @@ -5135,7 +5143,7 @@ const std::map& CRobotMain::GetObligatoryTokenList() //! Indicates whether it is possible to control a driving robot bool CRobotMain::GetTrainerPilot() { - return m_trainerPilot; + return m_cheatTrainerPilot; } //! Indicates whether the scene is fixed, without interaction @@ -5158,7 +5166,7 @@ const std::string& CRobotMain::GetScriptFile() bool CRobotMain::GetShowSoluce() { - return m_showSoluce; + return m_cheatShowSoluce; } bool CRobotMain::GetSceneSoluce() @@ -5169,7 +5177,7 @@ bool CRobotMain::GetSceneSoluce() bool CRobotMain::GetShowAll() { - return m_showAll; + return m_cheatAllMission; } bool CRobotMain::GetRadar() @@ -5293,7 +5301,6 @@ void CRobotMain::UpdateSpeedLabel() } -//! Creates interface shortcuts to the units bool CRobotMain::CreateShortcuts() { if (m_phase != PHASE_SIMUL) return false; @@ -5638,7 +5645,7 @@ bool CRobotMain::IsBuildingEnabled(ObjectType type) if(type == OBJECT_PARA) return IsBuildingEnabled(BUILD_PARA); if(type == OBJECT_DESTROYER) return IsBuildingEnabled(BUILD_DESTROYER); - return true; + return false; } bool CRobotMain::IsResearchEnabled(ResearchType type) @@ -5683,11 +5690,6 @@ Error CRobotMain::CanBuildError(ObjectType type, int team) return ERR_OK; } -bool CRobotMain::CanBuild(ObjectType type, int team) -{ - return CanBuildError(type, team) == ERR_OK; -} - Error CRobotMain::CanFactoryError(ObjectType type, int team) { ToolType tool = GetToolFromObject(type); @@ -5711,11 +5713,6 @@ Error CRobotMain::CanFactoryError(ObjectType type, int team) return ERR_OK; } -bool CRobotMain::CanFactory(ObjectType type, int team) -{ - return CanFactoryError(type, team) == ERR_OK; -} - void CRobotMain::PushToSelectionHistory(CObject* obj) { if (!m_selectionHistory.empty() && m_selectionHistory.back() == obj) @@ -5791,12 +5788,16 @@ void CRobotMain::StartDetectEffect(COldObject* object, CObject* target) void CRobotMain::CreateCodeBattleInterface() { - if(m_phase == PHASE_SIMUL) + if (m_phase == PHASE_SIMUL) { Math::Point pos, ddim; + int numTeams = m_scoreboard ? GetAllTeams().size() : 0; + assert(numTeams < EVENT_SCOREBOARD_MAX-EVENT_SCOREBOARD+1); + float textHeight = m_engine->GetText()->GetHeight(Gfx::FONT_COLOBOT, Gfx::FONT_SIZE_SMALL); + ddim.x = 100.0f/640.0f; - ddim.y = 100.0f/480.0f; + ddim.y = 100.0f/480.0f + numTeams * textHeight; pos.x = 540.0f/640.0f; pos.y = 100.0f/480.0f; Ui::CWindow* pw = m_interface->CreateWindows(pos, ddim, 3, EVENT_WINDOW6); @@ -5804,8 +5805,10 @@ void CRobotMain::CreateCodeBattleInterface() ddim.x = 100.0f/640.0f; ddim.y = 16.0f/480.0f; pos.x = 540.0f/640.0f; - pos.y = 178.0f/480.0f; - pw->CreateLabel(pos, ddim, 0, EVENT_LABEL0, "Code battle"); + pos.y = 178.0f/480.0f + numTeams * textHeight; + std::string text; + GetResource(RES_EVENT, EVENT_LABEL_CODE_BATTLE, text); + pw->CreateLabel(pos, ddim, 0, EVENT_LABEL_CODE_BATTLE, text); float titleBarSize = (11.0f/64.0f); // this is from the texture ddim.x = 80.0f/640.0f; @@ -5820,6 +5823,84 @@ void CRobotMain::CreateCodeBattleInterface() { pw->CreateButton(pos, ddim, 13, EVENT_CODE_BATTLE_SPECTATOR); } + + pos.y += ddim.y; + ddim.y = textHeight; + int i = 0; + auto teams = GetAllTeams(); + for (auto it = teams.rbegin(); it != teams.rend(); ++it) + { + int team = *it; + Ui::CControl* pl; + ddim.x = 55.0f/640.0f; + pl = m_codeBattleStarted + ? static_cast(pw->CreateLabel(pos, ddim, 0, static_cast(EVENT_SCOREBOARD+2*(numTeams-i-1)+0), "XXXXX")) + : static_cast(pw->CreateEdit( pos, ddim, 0, static_cast(EVENT_SCOREBOARD+2*(numTeams-i-1)+0))); + pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); + pl->SetFontSize(m_codeBattleStarted ? Gfx::FONT_SIZE_SMALL : Gfx::FONT_SIZE_SMALL*0.75f); + m_codeBattleStarted ? pl->SetName(GetTeamName(team)) : static_cast(pl)->SetText(GetTeamName(team)); + pos.x += 57.5f/640.0f; + ddim.x = 22.5f/640.0f; + pl = m_codeBattleStarted + ? static_cast(pw->CreateLabel(pos, ddim, 0, static_cast(EVENT_SCOREBOARD+2*(numTeams-i-1)+1), "???")) + : static_cast(pw->CreateEdit( pos, ddim, 0, static_cast(EVENT_SCOREBOARD+2*(numTeams-i-1)+1))); + pl->SetTextAlign(Gfx::TEXT_ALIGN_RIGHT); + pl->SetFontSize(m_codeBattleStarted ? Gfx::FONT_SIZE_SMALL : Gfx::FONT_SIZE_SMALL*0.75f); + m_codeBattleStarted ? pl->SetName(StrUtils::ToString(m_scoreboard->GetScore(team))) : static_cast(pl)->SetText(StrUtils::ToString(m_scoreboard->GetScore(team))); + pos.x -= 57.5f/640.0f; + pos.y += ddim.y; + i++; + } + } +} + +void CRobotMain::ApplyCodeBattleInterface() +{ + assert(GetMissionType() == MISSION_CODE_BATTLE); + if (!m_scoreboard) return; + + Ui::CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW6)); + assert(pw != nullptr); + + int i = 0; + for (int team : GetAllTeams()) + { + Ui::CEdit* pl; + + pl = static_cast(pw->SearchControl(static_cast(EVENT_SCOREBOARD+2*i+0))); + assert(pl != nullptr); + m_teamNames[team] = pl->GetText(pl->GetTextLength()); + + pl = static_cast(pw->SearchControl(static_cast(EVENT_SCOREBOARD+2*i+1))); + assert(pl != nullptr); + m_scoreboard->SetScore(team, StrUtils::FromString(pl->GetText(pl->GetTextLength()))); + + i++; + } +} + +void CRobotMain::UpdateCodeBattleInterface() +{ + assert(GetMissionType() == MISSION_CODE_BATTLE); + if (!m_scoreboard) return; + + Ui::CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW6)); + assert(pw != nullptr); + + int i = 0; + for (int team : GetAllTeams()) + { + Ui::CControl* pl; + + pl = pw->SearchControl(static_cast(EVENT_SCOREBOARD+2*i+0)); + assert(pl != nullptr); + pl->SetName(GetTeamName(team)); + + pl = pw->SearchControl(static_cast(EVENT_SCOREBOARD+2*i+1)); + assert(pl != nullptr); + pl->SetName(StrUtils::ToString(m_scoreboard->GetScore(team))); + + i++; } } @@ -5866,3 +5947,55 @@ bool CRobotMain::GetDebugCrashSpheres() { return m_debugCrashSpheres; } + +void CRobotMain::PushToCommandHistory(std::string str) +{ + if (!m_commandHistory.empty() && m_commandHistory.front() == str) // already in history + return; + + m_commandHistory.push_front(str); + + if (m_commandHistory.size() > 50) // to avoid infinite growth + m_commandHistory.pop_back(); +} + +std::string CRobotMain::GetNextFromCommandHistory() +{ + if (m_commandHistory.empty() || static_cast(m_commandHistory.size()) <= m_commandHistoryIndex + 1) // no next element + return ""; + return m_commandHistory[++m_commandHistoryIndex]; +} + +std::string CRobotMain::GetPreviousFromCommandHistory() +{ + if (m_commandHistory.empty() || m_commandHistoryIndex < 1) // first or none element selected + return ""; + return m_commandHistory[--m_commandHistoryIndex]; +} + +CScoreboard* CRobotMain::GetScoreboard() +{ + return m_scoreboard.get(); +} + +std::set CRobotMain::GetAllTeams() +{ + std::set teams = GetAllActiveTeams(); + for(auto& it : m_teamFinished) + { + teams.insert(it.first); + } + return teams; +} + +std::set CRobotMain::GetAllActiveTeams() +{ + std::set teams; + for (CObject* obj : m_objMan->GetAllObjects()) + { + int team = obj->GetTeam(); + if (team == 0) continue; + teams.insert(team); + } + return teams; +} diff --git a/src/level/robotmain.h b/src/level/robotmain.h index 18649c0e..fee753f0 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -87,6 +87,7 @@ class CInput; class CObjectManager; class CSceneEndCondition; class CAudioChangeCondition; +class CScoreboard; class CPlayerProfile; class CSettings; class COldObject; @@ -120,6 +121,8 @@ struct NewScriptName { ObjectType type = OBJECT_NULL; std::string name = ""; + + NewScriptName(ObjectType type, const std::string& name) : type(type), name(name) {} }; @@ -156,6 +159,17 @@ const int SATCOM_PROG = 4; const int SATCOM_SOLUCE = 5; const int SATCOM_MAX = 6; +/** + * \brief Main class managing the game world + * + * This is the main class of the whole game engine. It's main job is to manage main parts of the gameplay, + * like loading levels and checking for win conditions, but it's also a place where all things that don't fit + * elsewhere have landed. + * + * \todo In the future, it would be nice to refactor this class to remove as much unrelated stuff as possible + * + * \nosubgrouping + */ class CRobotMain : public CSingleton { public: @@ -168,11 +182,16 @@ public: Ui::CDisplayText* GetDisplayText(); CPauseManager* GetPauseManager(); + /** + * \name Phase management + */ + //@{ void ChangePhase(Phase phase); bool ProcessEvent(Event &event); Phase GetPhase(); + //@} - bool CreateShortcuts(); + //! Load the scene for apperance customization void ScenePerso(); void SetMovieLock(bool lock); @@ -187,16 +206,32 @@ public: void SetFriendAim(bool friendAim); bool GetFriendAim(); + //! \name Simulation speed management + //@{ void SetSpeed(float speed); float GetSpeed(); + //@} + //! \brief Create the shortcuts at the top of the screen, if they should be visible + //! \see CMainShort::CreateShortcuts + bool CreateShortcuts(); + //! \brief Update the shortcuts at the top of the screen + //! \see CMainShort::UpdateShortcuts void UpdateShortcuts(); - void SelectHuman(); + //! Find the astronaut (::OBJECT_HUMAN) object CObject* SearchHuman(); - CObject* SearchToto(); - CObject* SearchNearest(Math::Vector pos, CObject* exclu); + /** + * \brief Select an object + * \param obj Object to select + * \param displayError If true and the object is currently in error state, automatically display the error message + * + * \note This function automatically adds objects to selection history (see PushToSelectionHistory()) + */ bool SelectObject(CObject* obj, bool displayError=true); + //! Return the object that was selected at the start of the scene CObject* GetSelectObject(); + //! Deselect currently selected object + //! \return Object that was deselected CObject* DeselectAll(); void ResetObject(); @@ -251,9 +286,10 @@ public: void ClearInterface(); void ChangeColor(); - float SearchNearestObject(Math::Vector center, CObject *exclu); bool FreeSpace(Math::Vector ¢er, float minRadius, float maxRadius, float space, CObject *exclu); bool FlatFreeSpace(Math::Vector ¢er, float minFlat, float minRadius, float maxRadius, float space, CObject *exclu); + //! \name In-world indicators + //@{ float GetFlatZoneRadius(Math::Vector center, float maxRadius, CObject *exclu); void HideDropZone(CObject* metal); void ShowDropZone(CObject* metal, CObject* transporter); @@ -262,28 +298,38 @@ public: float radius, float duration=SHOWLIMITTIME); void StartShowLimit(); void FrameShowLimit(float rTime); + //@} void SaveAllScript(); void SaveOneScript(CObject *obj); bool SaveFileStack(CObject *obj, FILE *file, int objRank); bool ReadFileStack(CObject *obj, FILE *file, int objRank); - void FlushNewScriptName(); - void AddNewScriptName(ObjectType type, const std::string& name); - std::string GetNewScriptName(ObjectType type, int rank); + //! Return list of scripts to load to robot created in BotFactory + std::vector GetNewScriptNames(ObjectType type); + + //! Return the scoreboard manager + //! Note: this may return nullptr if the scoreboard is not enabled! + CScoreboard* GetScoreboard(); void SelectPlayer(std::string playerName); CPlayerProfile* GetPlayerProfile(); + /** + * \name Saved game read/write + */ + //@{ bool IOIsBusy(); bool IOWriteScene(std::string filename, std::string filecbot, std::string filescreenshot, const std::string& info, bool emergencySave = false); void IOWriteSceneFinished(); CObject* IOReadScene(std::string filename, std::string filecbot); void IOWriteObject(CLevelParserLine *line, CObject* obj, const std::string& programDir, int objRank); CObject* IOReadObject(CLevelParserLine *line, const std::string& programDir, const std::string& objCounterText, float objectProgress, int objRank = -1); + //@} int CreateSpot(Math::Vector pos, Gfx::Color color); + //! Find the currently selected object CObject* GetSelect(); void DisplayError(Error err, CObject* pObj, float time=10.0f); @@ -298,12 +344,17 @@ public: void StartMissionTimer(); + /** + * \name Autosave management + */ + //@{ void SetAutosave(bool enable); bool GetAutosave(); void SetAutosaveInterval(int interval); int GetAutosaveInterval(); void SetAutosaveSlots(int slots); int GetAutosaveSlots(); + //@} //! Enable mode where completing mission closes the game void SetExitAfterMission(bool exit); @@ -311,49 +362,97 @@ public: //! Returns true if player can interact with things manually bool CanPlayerInteract(); + /** + * \name Team definition management + */ + //@{ //! Returns team name for the given team id const std::string& GetTeamName(int id); //! Returns true if team-specific colored texture is available bool IsTeamColorDefined(int id); + //@} - //! Get/set enabled buildings + /** + * \name EnableBuild/EnableResearch/DoneResearch + * Management of enabled buildings, enabled researches, and completed researches + */ //@{ + /** + * \brief Get enabled buildings + * \return Bitmask of BuildType values + */ int GetEnableBuild(); + /** + * \brief Set enabled buildings + * \param enableBuild Bitmask of BuildType values + */ void SetEnableBuild(int enableBuild); - //@} - //! Get/set enabled researches - //@{ - int GetEnableResearch(); - void SetEnableResearch(int enableResearch); - //@} - //! Get/set done researches - //@{ - int GetDoneResearch(int team); - void SetDoneResearch(int doneResearch, int team); - //@} - //! Returns true if the given building is enabled - //@{ + /** + * \brief Get enabled researches + * \return Bitmask of ResearchType values + */ + int GetEnableResearch(); + /** + * \brief Set enabled researches + * \param enableResearch Bitmask of ResearchType values + */ + void SetEnableResearch(int enableResearch); + /** + * \brief Get done researches + * \param team Team to get researches for + * \return Bitmask of ResearchType values + */ + int GetDoneResearch(int team = 0); + /** + * \brief Set done researches + * \param doneResearch Bitmask of ResearchType values + * \param team Team to set researches for + */ + void SetDoneResearch(int doneResearch, int team = 0); + + //! \brief Check if the given building is enabled bool IsBuildingEnabled(BuildType type); + //! \brief Check if the given building is enabled bool IsBuildingEnabled(ObjectType type); - //@} - //! Returns true if the given research is enabled + //! \brief Check if the given research is enabled bool IsResearchEnabled(ResearchType type); - //! Returns true if the given research is done + //! \brief Check if the given research is done bool IsResearchDone(ResearchType type, int team); - //! Marks research as done + //! \brief Mark given research as done void MarkResearchDone(ResearchType type, int team); - //! Retruns true if all requirements to build this object are met (EnableBuild + DoneResearch) - //@{ - bool CanBuild(ObjectType type, int team); + /** + * \brief Check if all requirements to build this object are met (EnableBuild + DoneResearch) + * \return true if the building can be built, false otherwise + * \see CanBuildError() for a version which returns a specific reason for the build being denied + */ + inline bool CanBuild(ObjectType type, int team) + { + return CanBuildError(type, team) == ERR_OK; + } + /** + * \brief Check if all requirements to build this object are met (EnableBuild + DoneResearch) + * \return One of Error values - ::ERR_OK if the building can be built, ::ERR_BUILD_DISABLED or ::ERR_BUILD_RESEARCH otherwise + * \see CanBuild() for a version which returns a boolean + */ Error CanBuildError(ObjectType type, int team); - //@} - //! Retruns true if all requirements to create this object in BotFactory are met (DoneResearch) - //@{ - bool CanFactory(ObjectType type, int team); + /** + * \brief Check if all requirements to build this object in BotFactory are met (DoneResearch) + * \return true if the robot can be built, false otherwise + * \see CanFactoryError() for a version which returns a specific reason for the build being denied + */ + inline bool CanFactory(ObjectType type, int team) + { + return CanFactoryError(type, team) == ERR_OK; + } + /** + * \brief Check if all requirements to build this object in BotFactory are met (DoneResearch) + * \return One of Error values - ::ERR_OK if the robot can be built, ::ERR_BUILD_DISABLED or ::ERR_BUILD_RESEARCH otherwise + * \see CanFactory() for a version which returns a boolean + */ Error CanFactoryError(ObjectType type, int team); //@} @@ -364,12 +463,16 @@ public: void StartDetectEffect(COldObject* object, CObject* target); - bool IsSelectable(CObject* obj); - + //! Enable crash sphere debug rendering void SetDebugCrashSpheres(bool draw); - + //! Check if crash sphere debug rendering is enabled bool GetDebugCrashSpheres(); + //! Returns a set of all team IDs in the current level + std::set GetAllTeams(); + //! Returns a set of all team IDs in the current level that are still active + std::set GetAllActiveTeams(); + protected: bool EventFrame(const Event &event); bool EventObject(const Event &event); @@ -391,8 +494,11 @@ protected: CObject* DetectObject(Math::Point pos); void ChangeCamera(); void AbortMovie(); + //! \brief Select an object, without deselecting the previous one void SelectOneObject(CObject* obj, bool displayError=true); void HelpObject(); + //! \brief Switch to previous object + //! \see PopFromSelectionHistory() bool DeselectObject(); void DeleteAllObjects(); void UpdateInfoText(); @@ -408,11 +514,24 @@ protected: void PushToSelectionHistory(CObject* obj); CObject* PopFromSelectionHistory(); + //! \name Code battle interface + //@{ void CreateCodeBattleInterface(); + void UpdateCodeBattleInterface(); + void ApplyCodeBattleInterface(); void DestroyCodeBattleInterface(); void SetCodeBattleSpectatorMode(bool mode); + //@} + void UpdateDebugCrashSpheres(); + //! Adds element to the beginning of command history + void PushToCommandHistory(std::string obj); + //! Returns next/previous element from command history and updates index + //@{ + std::string GetNextFromCommandHistory(); + std::string GetPreviousFromCommandHistory(); + //@} protected: CApplication* m_app = nullptr; @@ -472,9 +591,9 @@ protected: ActivePause* m_freePhotoPause = nullptr; bool m_cmdEdit = false; ActivePause* m_cmdEditPause = nullptr; - bool m_selectInsect = false; - bool m_showSoluce = false; - bool m_showAll = false; + bool m_cheatSelectInsect = false; + bool m_cheatShowSoluce = false; + bool m_cheatAllMission = false; bool m_cheatRadar = false; bool m_shortCut = false; std::string m_audioTrack; @@ -496,7 +615,7 @@ protected: bool m_editLock = false; // edition in progress? bool m_editFull = false; // edition in full screen? bool m_hilite = false; - bool m_trainerPilot = false; // remote trainer? + bool m_cheatTrainerPilot = false; // remote trainer? bool m_friendAim = false; bool m_resetCreate = false; bool m_mapShow = false; @@ -548,9 +667,15 @@ protected: long m_endTakeResearch = 0; float m_endTakeWinDelay = 0.0f; float m_endTakeLostDelay = 0.0f; + //! Set to true for teams that have already finished + std::map m_teamFinished; std::vector> m_audioChange; + //! The scoreboard + //! If the scoreboard is not enabled for this level, this will be null + std::unique_ptr m_scoreboard; + std::map m_obligatoryTokens; //! Enabled buildings @@ -585,4 +710,9 @@ protected: std::deque m_selectionHistory; bool m_debugCrashSpheres; + + //! Cheat console command history + std::deque m_commandHistory; + //! Index of currently selected element in command history + int m_commandHistoryIndex; }; diff --git a/src/level/scene_conditions.cpp b/src/level/scene_conditions.cpp index e475dee9..d4ed1bfc 100644 --- a/src/level/scene_conditions.cpp +++ b/src/level/scene_conditions.cpp @@ -29,11 +29,13 @@ #include "object/interface/powered_object.h" #include "object/interface/transportable_object.h" +#include -void CSceneCondition::Read(CLevelParserLine* line) + +void CObjectCondition::Read(CLevelParserLine* line) { this->pos = line->GetParam("pos")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit; - this->dist = line->GetParam("dist")->AsFloat(8.0f)*g_unit; + this->dist = line->GetParam("dist")->AsFloat(std::numeric_limits::infinity())*g_unit; this->type = line->GetParam("type")->AsObjectType(OBJECT_NULL); this->powermin = line->GetParam("powermin")->AsFloat(-1); this->powermax = line->GetParam("powermax")->AsFloat(100); @@ -41,6 +43,82 @@ void CSceneCondition::Read(CLevelParserLine* line) this->drive = line->GetParam("drive")->AsDriveType(DriveType::Other); this->countTransported = line->GetParam("countTransported")->AsBool(true); this->team = line->GetParam("team")->AsInt(0); +} + +bool CObjectCondition::CheckForObject(CObject* obj) +{ + if (!this->countTransported) + { + if (IsObjectBeingTransported(obj)) return false; + } + + ObjectType type = obj->GetType(); + + ToolType tool = GetToolFromObject(type); + DriveType drive = GetDriveFromObject(type); + if (this->tool != ToolType::Other && + tool != this->tool) + return false; + + if (this->drive != DriveType::Other && + drive != this->drive) + return false; + + if (this->tool == ToolType::Other && + this->drive == DriveType::Other && + type != this->type && + this->type != OBJECT_NULL) + return false; + + if ((this->team > 0 && obj->GetTeam() != this->team) || + (this->team < 0 && (obj->GetTeam() == -(this->team) || obj->GetTeam() == 0))) + return false; + + float energyLevel = -1; + CPowerContainerObject* power = nullptr; + if (obj->Implements(ObjectInterfaceType::PowerContainer)) + { + power = dynamic_cast(obj); + } + else if (obj->Implements(ObjectInterfaceType::Powered)) + { + CObject* powerObj = dynamic_cast(obj)->GetPower(); + if(powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer)) + { + power = dynamic_cast(powerObj); + } + } + + if (power != nullptr) + { + energyLevel = power->GetEnergy(); + if (power->GetCapacity() > 1.0f) energyLevel *= 10; // TODO: Who designed it like that ?!?! + } + if (energyLevel < this->powermin || energyLevel > this->powermax) return false; + + Math::Vector oPos; + if (IsObjectBeingTransported(obj)) + oPos = dynamic_cast(obj)->GetTransporter()->GetPosition(); + else + oPos = obj->GetPosition(); + oPos.y = 0.0f; + + Math::Vector bPos = this->pos; + bPos.y = 0.0f; + + if (Math::DistanceProjected(oPos, bPos) <= this->dist) + return true; + + return false; +} + +void CSceneCondition::Read(CLevelParserLine* line) +{ + CObjectCondition::Read(line); + + // Scene conditions STILL use a different default value + // See issue #759 + this->dist = line->GetParam("dist")->AsFloat(8.0f)*g_unit; this->min = line->GetParam("min")->AsInt(1); this->max = line->GetParam("max")->AsInt(9999); @@ -48,74 +126,12 @@ void CSceneCondition::Read(CLevelParserLine* line) int CSceneCondition::CountObjects() { - Math::Vector bPos = this->pos; - bPos.y = 0.0f; - - Math::Vector oPos; - int nb = 0; for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects()) { if (!obj->GetActive()) continue; - - if (!this->countTransported) - { - if (IsObjectBeingTransported(obj)) continue; - } - - ObjectType type = obj->GetType(); - - ToolType tool = GetToolFromObject(type); - DriveType drive = GetDriveFromObject(type); - if (this->tool != ToolType::Other && - tool != this->tool) - continue; - - if (this->drive != DriveType::Other && - drive != this->drive) - continue; - - if (this->tool == ToolType::Other && - this->drive == DriveType::Other && - type != this->type && - this->type != OBJECT_NULL) - continue; - - if ((this->team > 0 && obj->GetTeam() != this->team) || - (this->team < 0 && (obj->GetTeam() == -(this->team) || obj->GetTeam() == 0))) - continue; - - float energyLevel = -1; - CPowerContainerObject* power = nullptr; - if (obj->Implements(ObjectInterfaceType::PowerContainer)) - { - power = dynamic_cast(obj); - } - else if (obj->Implements(ObjectInterfaceType::Powered)) - { - CObject* powerObj = dynamic_cast(obj)->GetPower(); - if(powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer)) - { - power = dynamic_cast(powerObj); - } - } - - if (power != nullptr) - { - energyLevel = power->GetEnergy(); - if (power->GetCapacity() > 1.0f) energyLevel *= 10; // TODO: Who designed it like that ?!?! - } - if (energyLevel < this->powermin || energyLevel > this->powermax) continue; - - if (IsObjectBeingTransported(obj)) - oPos = dynamic_cast(obj)->GetTransporter()->GetPosition(); - else - oPos = obj->GetPosition(); - - oPos.y = 0.0f; - - if (Math::DistanceProjected(oPos, bPos) <= this->dist) - nb ++; + if (!CheckForObject(obj)) continue; + nb ++; } return nb; } @@ -126,7 +142,6 @@ bool CSceneCondition::Check() return nb >= this->min && nb <= this->max; } - void CSceneEndCondition::Read(CLevelParserLine* line) { CSceneCondition::Read(line); diff --git a/src/level/scene_conditions.h b/src/level/scene_conditions.h index 258cc7ef..721c486c 100644 --- a/src/level/scene_conditions.h +++ b/src/level/scene_conditions.h @@ -34,12 +34,13 @@ #include "object/tool_type.h" class CLevelParserLine; +class CObject; /** - * \class CSceneCondition - * \brief Base scene condition structure + * \class CObjectCondition + * \brief Base object condition structure */ -class CSceneCondition +class CObjectCondition { public: Math::Vector pos = Math::Vector(0.0f, 0.0f, 0.0f)*g_unit; @@ -52,11 +53,25 @@ public: bool countTransported = true; int team = 0; + //! Read from line in scene file + virtual void Read(CLevelParserLine* line); + + //! Checks if this condition is met + bool CheckForObject(CObject* obj); +}; + +/** + * \class CSceneCondition + * \brief Base scene condition structure + */ +class CSceneCondition : public CObjectCondition +{ +public: int min = 1; // wins if > int max = 9999; // wins if < //! Read from line in scene file - virtual void Read(CLevelParserLine* line); + void Read(CLevelParserLine* line) override; //! Checks if this condition is met bool Check(); diff --git a/src/level/scoreboard.cpp b/src/level/scoreboard.cpp new file mode 100644 index 00000000..7e4b5b04 --- /dev/null +++ b/src/level/scoreboard.cpp @@ -0,0 +1,113 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2017, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#include "level/scoreboard.h" + +#include "common/restext.h" +#include "common/stringutils.h" + +#include "level/parser/parserline.h" + +#include "level/robotmain.h" + +#include "object/object.h" + +#include "ui/displaytext.h" + +#include + +void CScoreboard::CScoreboardRule::Read(CLevelParserLine* line) +{ + this->score = line->GetParam("score")->AsInt(); +} + +void CScoreboard::CScoreboardKillRule::Read(CLevelParserLine* line) +{ + CScoreboardRule::Read(line); + CObjectCondition::Read(line); +} + +void CScoreboard::CScoreboardEndTakeRule::Read(CLevelParserLine* line) +{ + CScoreboardRule::Read(line); + this->team = line->GetParam("team")->AsInt(0); + this->order = line->GetParam("order")->AsInt(0); +} + +void CScoreboard::AddKillRule(std::unique_ptr rule) +{ + m_rulesKill.push_back(std::move(rule)); +} + +void CScoreboard::AddEndTakeRule(std::unique_ptr rule) +{ + m_rulesEndTake.push_back(std::move(rule)); +} + +void CScoreboard::ProcessKill(CObject* target, CObject* killer) +{ + if (killer == nullptr) return; + if (killer->GetTeam() == 0) return; + for (auto& rule : m_rulesKill) + { + if ((rule->team == killer->GetTeam() || rule->team == 0) && + killer->GetTeam() != 0 && + rule->CheckForObject(target)) + { + AddPoints(killer->GetTeam(), rule->score); + } + } +} + +void CScoreboard::ProcessEndTake(int team) +{ + if (team == 0) return; + m_finishCounter++; + for (auto& rule : m_rulesEndTake) + { + if ((rule->team == team || rule->team == 0) && + (rule->order == m_finishCounter || rule->order == 0)) + { + AddPoints(team, rule->score); + } + } +} + +void CScoreboard::AddPoints(int team, int points) +{ + GetLogger()->Info("Team %d earned %d points\n", team, points); + + CRobotMain* main = CRobotMain::GetInstancePointer(); + std::string text; + GetResource(RES_ERR, INFO_TEAM_SCORE, text); + text = StrUtils::Format(text.c_str(), main->GetTeamName(team).c_str(), points); + main->GetDisplayText()->DisplayText(text.c_str(), Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 10.0f, Ui::TT_WARNING); + + m_score[team] += points; +} + +int CScoreboard::GetScore(int team) +{ + return m_score[team]; +} + +void CScoreboard::SetScore(int team, int points) +{ + m_score[team] = points; +} diff --git a/src/level/scoreboard.h b/src/level/scoreboard.h new file mode 100644 index 00000000..58a5acb5 --- /dev/null +++ b/src/level/scoreboard.h @@ -0,0 +1,125 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2017, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +/** + * \file level/scoreboard.h + * \brief Code responsible for managing the scoreboard used to score complex code battles + */ + +#pragma once + +#include "level/scene_conditions.h" + +#include +#include +#include + +class CObject; + +/** + * \class CScoreboard + * \brief Scoreboard used to score complex code battles + * + * \todo This is pretty much a work-in-progress hack for Diversity. Be wary of possible API changes. + * + * \todo Proper documentation + * + * \see CRobotMain::GetScoreboard() + * + * \section example Usage example + * \code{.scene} + * Scoreboard enable=true // enable the scoreboard + * ScoreboardKillRule type=WheeledShooter team=1 score=500 // destruction of team 1's WheeledShooter gives 100 points to the team that destroyed it + * ScoreboardKillRule type=TargetBot score=100 // destruction of TargetBot (any team) gives 100 points + * ScoreboardEndTakeRule score=1000 // completion of EndMissionTake objectives for any team results in 1000 points for that team + * \endcode + */ +class CScoreboard +{ +public: + //! Creates the scoreboard + //! The scoreboard exists only if enabled in level file + CScoreboard() {}; + //! Destroys the scoreboard + ~CScoreboard() {}; + +public: + /** + * \class CScoreboardRule + * \brief Base class for scoreboard rules + */ + class CScoreboardRule + { + public: + int score = 0; + + //! Read from line in scene file + virtual void Read(CLevelParserLine* line); + }; + + /** + * \class CScoreboardKillRule + * \brief Scoreboard rule for destroying other objects + * \see CScoreboard::AddKillRule() + */ + class CScoreboardKillRule : public CScoreboardRule, public CObjectCondition + { + public: + //! Read from line in scene file + void Read(CLevelParserLine* line) override; + }; + + /** + * \class CScoreboardEndTakeRule + * \brief Scoreboard rule for EndMissionTake rewards + * \see CScoreboard::AddEndTakeRule() + */ + class CScoreboardEndTakeRule : public CScoreboardRule + { + public: + int team = 0; + int order = 0; + + //! Read from line in scene file + void Read(CLevelParserLine* line) override; + }; + +public: + //! Add ScoreboardKillRule + void AddKillRule(std::unique_ptr rule); + //! Add ScoreboardEndTakeRule + void AddEndTakeRule(std::unique_ptr rule); + + //! Called after an object is destroyed by another object + //! \param target The object that has just been destroyed + //! \param killer The object that caused the destruction, can be null + void ProcessKill(CObject* target, CObject* killer = nullptr); + //! Called after EndTake contition has been met, used to handle ScoreboardEndTakeRule + void ProcessEndTake(int team); + + void AddPoints(int team, int points); + int GetScore(int team); + void SetScore(int team, int score); + +private: + std::vector> m_rulesKill = {}; + std::vector> m_rulesEndTake = {}; + std::map m_score; + int m_finishCounter = 0; +}; \ No newline at end of file diff --git a/src/math/all.h b/src/math/all.h index 7137c61e..f56a56f5 100644 --- a/src/math/all.h +++ b/src/math/all.h @@ -28,6 +28,7 @@ #include "math/const.h" #include "math/func.h" #include "math/geometry.h" +#include "math/half.h" #include "math/matrix.h" #include "math/point.h" #include "math/vector.h" diff --git a/src/math/half.cpp b/src/math/half.cpp new file mode 100644 index 00000000..d1339a43 --- /dev/null +++ b/src/math/half.cpp @@ -0,0 +1,116 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#include "math/half.h" + +#include +#include +#include + +// Math module namespace +namespace Math +{ + +//! Converts float to half-float +uint16_t FloatToHalf(float value) +{ + uint16_t sign = (std::copysign(1.0f, value) > 0.0f ? 0x0000 : 0x8000); + + // Infinity + if (std::isinf(value)) + { + return sign | 0x7C00; + } + // NaN + else if (std::isnan(value)) + { + return sign | 0x7FFF; + } + + int exponent; + + float significand = std::fabs(std::frexp(value, &exponent)); + + // Exponent bias + exponent += 15; + + // Crosses upper boundary, clamp to infinity + if (exponent > 31) + { + return sign | 0x7C00; + } + // Crosses lower boundary, clamp to zero + else if (exponent <= 0) + { + return sign | 0x0000; + } + // Zero + else if (significand < 0.25f) + { + return sign | 0x0000; + } + + // Normal value + uint16_t mantissa = static_cast(std::ldexp(2 * significand - 1, 10)); + + uint16_t bits = sign | mantissa | ((exponent - 1) << 10); + + return bits; +} + +//! Converts half-float to float +float HaltToFloat(uint16_t value) +{ + int exponent = (value >> 10) & 0x001F; + int mantissa = (value >> 0) & 0x03FF; + + float result; + + // Zero + if ((exponent == 0) && (mantissa == 0)) + { + result = 0.0f; + } + // Subnormal + else if ((exponent == 0) && (mantissa != 0)) + { + result = std::ldexp(static_cast(mantissa), -24); + } + // Infinity + else if ((exponent == 31) && (mantissa == 0)) + { + result = std::numeric_limits::infinity(); + } + // NaN + else if ((exponent == 31) && (mantissa != 0)) + { + result = std::nanf(""); + } + // Normal number + else + { + result = std::ldexp(static_cast(mantissa | 0x0400), exponent - 25); + } + + float sign = ((value & 0x8000) == 0 ? 1.0f : -1.0f); + + return std::copysignf(result, sign); +} + +} // namespace Math diff --git a/src/math/half.h b/src/math/half.h new file mode 100644 index 00000000..a9d80d91 --- /dev/null +++ b/src/math/half.h @@ -0,0 +1,105 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +/** +* \file math/half.h +* \brief Implementation of half-precision floating point values. +*/ + +#pragma once + +#include + +// Math module namespace +namespace Math +{ + +//! Converts float to half-float binary representation +uint16_t FloatToHalf(float value); + +//! Converts half-float binary representation to float +float HaltToFloat(uint16_t value); + +//@colobot-lint-exclude ClassNamingRule +/** +* \struct half +* \brief half-precision floating point type +* +* Represents a half-precision floating point value. +* Contains the required methods for converting to and from ints and floats. +* +* This type is for storage only. +* Conversion is expensive and should be avoided if possible. +*/ +struct half +{ + //! 16-bit binary representation of half-float + uint16_t bits; + + //! Default constructor + half() + : bits(0) + { + + } + + //! Copy constructor + half(const half& other) + : bits(other.bits) + { + + } + + //! Copy operator + half& operator=(const half& other) + { + bits = other.bits; + + return *this; + } + + //! Conversion constructor from int + explicit half(int value) + : bits(FloatToHalf(static_cast(value))) + { + + } + + //! Conversion constructor from float + explicit half(float value) + : bits(FloatToHalf(value)) + { + + } + + //! Conversion operator to int + explicit operator int() const + { + return static_cast(HaltToFloat(bits)); + } + + //! Conversion operator to float + explicit operator float() const + { + return HaltToFloat(bits); + } +}; +//@end-colobot-lint-exclude + +} // namespace Math diff --git a/src/object/auto/autodestroyer.cpp b/src/object/auto/autodestroyer.cpp index 84333f07..deb77ff7 100644 --- a/src/object/auto/autodestroyer.cpp +++ b/src/object/auto/autodestroyer.cpp @@ -107,7 +107,7 @@ Error CAutoDestroyer::StartAction(int param) m_bExplo = false; } else - return ERR_UNKNOWN; + return ERR_OBJ_BUSY; } return ERR_OK; } diff --git a/src/object/auto/autofactory.cpp b/src/object/auto/autofactory.cpp index 17337975..7dfc280d 100644 --- a/src/object/auto/autofactory.cpp +++ b/src/object/auto/autofactory.cpp @@ -129,7 +129,7 @@ Error CAutoFactory::StartAction(int param) { if ( m_phase != AFP_WAIT ) { - return ERR_OK; + return ERR_OBJ_BUSY; } m_type = type; @@ -664,11 +664,9 @@ bool CAutoFactory::CreateVehicle() if (vehicle->Implements(ObjectInterfaceType::ProgramStorage)) { CProgramStorageObject* programStorage = dynamic_cast(vehicle); - for ( int i=0 ; ; i++ ) + for (const std::string& name : m_main->GetNewScriptNames(m_type)) { - std::string name = m_main->GetNewScriptName(m_type, i); - if (name.empty()) break; - Program* prog = programStorage->GetOrAddProgram(i); + Program* prog = programStorage->AddProgram(); programStorage->ReadProgram(prog, InjectLevelPathsForCurrentLevel(name)); prog->readOnly = true; prog->filename = name; diff --git a/src/object/auto/autolabo.cpp b/src/object/auto/autolabo.cpp index 0fb087d5..1cbcbc24 100644 --- a/src/object/auto/autolabo.cpp +++ b/src/object/auto/autolabo.cpp @@ -121,7 +121,7 @@ Error CAutoLabo::StartAction(int param) { if ( m_phase != ALAP_WAIT ) { - return ERR_UNKNOWN; + return ERR_OBJ_BUSY; } m_research = static_cast(param); diff --git a/src/object/auto/autoresearch.cpp b/src/object/auto/autoresearch.cpp index 2303d7c9..32380de4 100644 --- a/src/object/auto/autoresearch.cpp +++ b/src/object/auto/autoresearch.cpp @@ -104,7 +104,7 @@ Error CAutoResearch::StartAction(int param) { if ( m_phase != ALP_WAIT ) { - return ERR_UNKNOWN; + return ERR_OBJ_BUSY; } m_research = static_cast(param); diff --git a/src/object/interface/damageable_object.h b/src/object/interface/damageable_object.h index 27b35ca7..0813a286 100644 --- a/src/object/interface/damageable_object.h +++ b/src/object/interface/damageable_object.h @@ -23,6 +23,8 @@ #include +class CObject; + /** * \enum DamageType * \brief Type of damage, for use in CDamageableObject::DamageObject @@ -56,5 +58,5 @@ 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()) = 0; + virtual bool DamageObject(DamageType type, float force = std::numeric_limits::infinity(), CObject* killer = nullptr) = 0; }; diff --git a/src/object/interface/destroyable_object.h b/src/object/interface/destroyable_object.h index d3e6ee02..48f4736b 100644 --- a/src/object/interface/destroyable_object.h +++ b/src/object/interface/destroyable_object.h @@ -32,6 +32,7 @@ enum class DestructionType ExplosionWater = 2, //!< explosion underwater Burn = 3, //!< burning Drowned = 4, //!< drowned (only for Me) + Win = 5, //!< used when removing objects from a team that won }; /** @@ -65,7 +66,7 @@ public: //! Destroy the object immediately. Use this only if you are 100% sure this is what you want, because object with magnifyDamage=0 should be able to bypass all damage. It's recommended to use CDamageableObject::DamageObject() instead. /** NOTE: After this function exits, you can assume the object has been definetly destroyed */ - virtual void DestroyObject(DestructionType type) = 0; + virtual void DestroyObject(DestructionType type, CObject* killer = nullptr) = 0; //! Returns the distance modifier for CLightning, used to modify hit probability. Value in range [0..1], where 0 is never and 1 is normal probability virtual float GetLightningHitProbability() = 0; diff --git a/src/object/motion/motionhuman.cpp b/src/object/motion/motionhuman.cpp index cd70d450..99469a90 100644 --- a/src/object/motion/motionhuman.cpp +++ b/src/object/motion/motionhuman.cpp @@ -71,6 +71,7 @@ CMotionHuman::CMotionHuman(COldObject* object) m_time = 0.0f; m_tired = 0.0f; m_bDisplayPerso = false; + m_glassesRank = -1; } // Object's constructor. @@ -198,12 +199,12 @@ void CMotionHuman::Create(Math::Vector pos, float angle, ObjectType type, glasses = m_main->GetGamerGlasses(); if ( glasses != 0 && type == OBJECT_HUMAN ) { - rank = m_engine->CreateObject(); - m_engine->SetObjectType(rank, Gfx::ENG_OBJTYPE_DESCENDANT); - m_object->SetObjectRank(15, rank); + m_glassesRank = m_engine->CreateObject(); + m_engine->SetObjectType(m_glassesRank, Gfx::ENG_OBJTYPE_DESCENDANT); + m_object->SetObjectRank(15, m_glassesRank); m_object->SetObjectParent(15, 1); sprintf(filename, "human2g%d.mod", glasses); - modelManager->AddModelReference(filename, false, rank); + modelManager->AddModelReference(filename, false, m_glassesRank); } // Creates the right arm. @@ -652,6 +653,18 @@ bool CMotionHuman::EventFrame(const Event &event) m_object->SetCirVibration(Math::Vector(0.0f, m_main->GetPersoAngle()+0.2f, 0.0f)); } + if ( m_glassesRank != -1 ) + { + if ( m_camera->GetType() == Gfx::CAM_TYPE_ONBOARD ) + { + m_engine->SetObjectDrawWorld(m_glassesRank, false); + } + else + { + m_engine->SetObjectDrawWorld(m_glassesRank, true); + } + } + bSwim = m_physics->GetSwim(); rot = m_physics->GetCirMotionY(MO_MOTSPEED); diff --git a/src/object/motion/motionhuman.h b/src/object/motion/motionhuman.h index e0bb981c..9449ed2b 100644 --- a/src/object/motion/motionhuman.h +++ b/src/object/motion/motionhuman.h @@ -91,4 +91,5 @@ protected: float m_time; float m_tired; bool m_bDisplayPerso; + int m_glassesRank; }; diff --git a/src/object/motion/motiontoto.cpp b/src/object/motion/motiontoto.cpp index 18db7471..c0a2296a 100644 --- a/src/object/motion/motiontoto.cpp +++ b/src/object/motion/motiontoto.cpp @@ -57,7 +57,6 @@ CMotionToto::CMotionToto(COldObject* object) : CMotion(object) m_blinkTime = 0.0f; m_blinkProgress = -1.0f; m_lastMotorParticle = 0.0f; - m_type = OBJECT_NULL; m_mousePos = Math::Point(0.0f, 0.0f); } @@ -326,35 +325,38 @@ bool CMotionToto::EventFrame(const Event &event) shift = 18.0f-progress*11.0f; // shift is left verti = 10.0f-progress* 8.0f; // shift at the top + CObject* selected = m_main->GetSelect(); + ObjectType type = selected != nullptr ? selected->GetType() : OBJECT_NULL; + if ( m_actionType == -1 && - (m_type == OBJECT_HUMAN || - m_type == OBJECT_TECH || - m_type == OBJECT_MOBILEwa || - m_type == OBJECT_MOBILEta || - m_type == OBJECT_MOBILEfa || - m_type == OBJECT_MOBILEia || - m_type == OBJECT_MOBILEwc || - m_type == OBJECT_MOBILEtc || - m_type == OBJECT_MOBILEfc || - m_type == OBJECT_MOBILEic || - m_type == OBJECT_MOBILEwi || - m_type == OBJECT_MOBILEti || - m_type == OBJECT_MOBILEfi || - m_type == OBJECT_MOBILEii || - m_type == OBJECT_MOBILEws || - m_type == OBJECT_MOBILEts || - m_type == OBJECT_MOBILEfs || - m_type == OBJECT_MOBILEis || - m_type == OBJECT_MOBILErt || - m_type == OBJECT_MOBILErc || - m_type == OBJECT_MOBILErr || - m_type == OBJECT_MOBILErs || - m_type == OBJECT_MOBILEsa || - m_type == OBJECT_MOBILEwt || - m_type == OBJECT_MOBILEtt || - m_type == OBJECT_MOBILEft || - m_type == OBJECT_MOBILEit || - m_type == OBJECT_MOBILEdr ) ) // vehicle? + (type == OBJECT_HUMAN || + type == OBJECT_TECH || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr ) ) // vehicle? { m_clownTime += event.rTime; if ( m_clownTime >= m_clownDelay ) @@ -832,10 +834,3 @@ Error CMotionToto::SetAction(int action, float time) return ERR_OK; } - -// Specifies the type of the object is attached to toto. - -void CMotionToto::SetLinkType(ObjectType type) -{ - m_type = type; -} diff --git a/src/object/motion/motiontoto.h b/src/object/motion/motiontoto.h index 1e97c03a..fe907308 100644 --- a/src/object/motion/motiontoto.h +++ b/src/object/motion/motiontoto.h @@ -43,7 +43,6 @@ public: void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager) override; bool EventProcess(const Event &event) override; Error SetAction(int action, float time=0.2f) override; - void SetLinkType(ObjectType type); void StartDisplayInfo(); void StopDisplayInfo(); @@ -65,6 +64,5 @@ protected: float m_blinkTime; float m_blinkProgress; int m_soundChannel; - ObjectType m_type; Math::Point m_mousePos; }; diff --git a/src/object/object.h b/src/object/object.h index 960d753e..4fcfe686 100644 --- a/src/object/object.h +++ b/src/object/object.h @@ -206,6 +206,11 @@ public: //! Is this object detectable (not dead and not underground)? virtual bool GetDetectable() { return true; } + //! Returns true if this object can collide with bullets even though it's not damageable itself + //! This is useful to make Barriers protect from bullets + //! \todo It will work like this for now but later I'd like to refactor this to something more manageable ~krzys_h + virtual bool IsBulletWall() { return false; } + protected: //! Transform crash sphere by object's world matrix virtual void TransformCrashSphere(Math::Sphere& crashSphere) = 0; diff --git a/src/object/object_factory.cpp b/src/object/object_factory.cpp index 76e9d59d..0b16e171 100644 --- a/src/object/object_factory.cpp +++ b/src/object/object_factory.cpp @@ -149,6 +149,8 @@ CObjectUPtr CObjectFactory::CreateObject(const ObjectCreateParams& params) case OBJECT_BARRIER1: case OBJECT_BARRIER2: case OBJECT_BARRIER3: + case OBJECT_BARRICADE0: + case OBJECT_BARRICADE1: return CreateBarrier(params); case OBJECT_PLANT0: @@ -579,6 +581,48 @@ CObjectUPtr CObjectFactory::CreateBarrier(const ObjectCreateParams& params) obj->CreateShadowCircle(10.0f, 0.5f, Gfx::ENG_SHADOW_WORM); } + if ( type == OBJECT_BARRICADE0 ) + { + int rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, Gfx::ENG_OBJTYPE_FIX); + obj->SetObjectRank(0, rank); + m_oldModelManager->AddModelReference("barricade0.mod", false, rank, obj->GetTeam()); + obj->SetPosition(pos); + obj->SetRotationY(angle); + + obj->AddCrashSphere(CrashSphere(Math::Vector( 3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector( 0.0f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector(-3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector( 3.5f, 6.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector( 0.0f, 6.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector(-3.5f, 6.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + + obj->CreateShadowCircle(6.0f, 0.5f, Gfx::ENG_SHADOW_WORM); + } + + if ( type == OBJECT_BARRICADE1 ) + { + int rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, Gfx::ENG_OBJTYPE_FIX); + obj->SetObjectRank(0, rank); + m_oldModelManager->AddModelReference("barricade1.mod", false, rank, obj->GetTeam()); + obj->SetPosition(pos); + obj->SetRotationY(angle); + + obj->AddCrashSphere(CrashSphere(Math::Vector( 8.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector( 3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector( 0.0f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector(-3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector(-8.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector( 8.5f, 6.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector( 3.5f, 6.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector( 0.0f, 6.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector(-3.5f, 6.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + obj->AddCrashSphere(CrashSphere(Math::Vector(-8.5f, 6.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f)); + + obj->CreateShadowCircle(12.0f, 0.5f, Gfx::ENG_SHADOW_WORM); + } + pos = obj->GetPosition(); obj->SetPosition(pos); // to display the shadows immediately diff --git a/src/object/object_manager.cpp b/src/object/object_manager.cpp index a903a3a4..a33a89fc 100644 --- a/src/object/object_manager.cpp +++ b/src/object/object_manager.cpp @@ -199,7 +199,7 @@ bool CObjectManager::TeamExists(int team) return false; } -void CObjectManager::DestroyTeam(int team) +void CObjectManager::DestroyTeam(int team, DestructionType destructionType) { assert(team != 0); @@ -209,7 +209,7 @@ void CObjectManager::DestroyTeam(int team) { if (object->Implements(ObjectInterfaceType::Destroyable)) { - dynamic_cast(object)->DestroyObject(DestructionType::Explosion); + dynamic_cast(object)->DestroyObject(destructionType); } else { @@ -311,7 +311,9 @@ std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector this } if ( oType == OBJECT_BARRIER2 || - oType == OBJECT_BARRIER3 ) // barriers? + oType == OBJECT_BARRIER3 || + oType == OBJECT_BARRICADE0 || + oType == OBJECT_BARRICADE1 ) // barriers? { oType = OBJECT_BARRIER1; // any barrier } diff --git a/src/object/object_manager.h b/src/object/object_manager.h index 8a4cbd6e..73ae0015 100644 --- a/src/object/object_manager.h +++ b/src/object/object_manager.h @@ -32,6 +32,7 @@ #include "object/object_create_params.h" #include "object/object_interface_type.h" #include "object/object_type.h" +#include "object/interface/destroyable_object.h" #include #include @@ -180,7 +181,7 @@ public: //! Destroy all objects of team // TODO: This should be probably moved to separate class - void DestroyTeam(int team); + void DestroyTeam(int team, DestructionType destructionType = DestructionType::Explosion); //! Counts all objects implementing given interface int CountObjectsImplementing(ObjectInterfaceType interface); diff --git a/src/object/object_type.h b/src/object/object_type.h index 7467ff3e..88c9587c 100644 --- a/src/object/object_type.h +++ b/src/object/object_type.h @@ -33,203 +33,205 @@ enum ObjectType { OBJECT_NULL = 0, - OBJECT_PORTICO = 2, //! < Portico - OBJECT_BASE = 3, //! < SpaceShip - OBJECT_DERRICK = 4, //! < Derrick - OBJECT_FACTORY = 5, //! < BotFactory - OBJECT_STATION = 6, //! < PowerStation - OBJECT_CONVERT = 7, //! < Converter - OBJECT_REPAIR = 8, //! < RepairStation - OBJECT_TOWER = 9, //! < DefenseTower - OBJECT_NEST = 10, //! < AlienNest - OBJECT_RESEARCH = 11, //! < ResearchCenter - OBJECT_RADAR = 12, //! < RadarStation - OBJECT_ENERGY = 13, //! < PowerPlant - OBJECT_LABO = 14, //! < AutoLab - OBJECT_NUCLEAR = 15, //! < NuclearPlant - OBJECT_START = 16, //! < StartArea - OBJECT_END = 17, //! < EndArea - OBJECT_INFO = 18, //! < ExchangePost - OBJECT_PARA = 19, //! < PowerCaptor - OBJECT_TARGET1 = 20, //! < Target1 (gate) - OBJECT_TARGET2 = 21, //! < Target2 (center) - OBJECT_SAFE = 22, //! < Vault - OBJECT_HUSTON = 23, //! < Houston - OBJECT_DESTROYER = 24, //! < Destroyer - OBJECT_STONE = 31, //! < TitaniumOre - OBJECT_URANIUM = 32, //! < UraniumOre - OBJECT_METAL = 33, //! < Titanium - OBJECT_POWER = 34, //! < PowerCell - OBJECT_ATOMIC = 35, //! < NuclearCell - OBJECT_BULLET = 36, //! < OrgaMatter - OBJECT_BBOX = 37, //! < BlackBox - OBJECT_TNT = 38, //! < TNT - OBJECT_MARKPOWER = 50, //! < PowerSpot - OBJECT_MARKSTONE = 51, //! < TitaniumSpot - OBJECT_MARKURANIUM = 52, //! < UraniumSpot - OBJECT_MARKKEYa = 53, //! < KeyASpot - OBJECT_MARKKEYb = 54, //! < KeyBSpot - OBJECT_MARKKEYc = 55, //! < KeyCSpot - OBJECT_MARKKEYd = 56, //! < KeyDSpot - OBJECT_BOMB = 60, //! < Mine - OBJECT_WINFIRE = 61, //! < Firework - OBJECT_SHOW = 62, //! < arrow above object (Visit) - OBJECT_BAG = 63, //! < Bag - OBJECT_PLANT0 = 70, //! < Greenery0 - OBJECT_PLANT1 = 71, //! < Greenery1 - OBJECT_PLANT2 = 72, //! < Greenery2 - OBJECT_PLANT3 = 73, //! < Greenery3 - OBJECT_PLANT4 = 74, //! < Greenery4 - OBJECT_PLANT5 = 75, //! < Greenery5 - OBJECT_PLANT6 = 76, //! < Greenery6 - OBJECT_PLANT7 = 77, //! < Greenery7 - OBJECT_PLANT8 = 78, //! < Greenery8 - OBJECT_PLANT9 = 79, //! < Greenery9 - OBJECT_PLANT10 = 80, //! < Greenery10 - OBJECT_PLANT11 = 81, //! < Greenery11 - OBJECT_PLANT12 = 82, //! < Greenery12 - OBJECT_PLANT13 = 83, //! < Greenery13 - OBJECT_PLANT14 = 84, //! < Greenery14 - OBJECT_PLANT15 = 85, //! < Greenery15 - OBJECT_PLANT16 = 86, //! < Greenery16 - OBJECT_PLANT17 = 87, //! < Greenery17 - OBJECT_PLANT18 = 88, //! < Greenery18 - OBJECT_PLANT19 = 89, //! < Greenery19 - OBJECT_TREE0 = 90, //! < Tree0 - OBJECT_TREE1 = 91, //! < Tree1 - OBJECT_TREE2 = 92, //! < Tree2 - OBJECT_TREE3 = 93, //! < Tree3 - OBJECT_TREE4 = 94, //! < Tree4 - OBJECT_TREE5 = 95, //! < Tree5 - OBJECT_MOBILEwt = 100, //! < PracticeBot - OBJECT_MOBILEtt = 101, //! < track-trainer (unused) - OBJECT_MOBILEft = 102, //! < fly-trainer (unused) - OBJECT_MOBILEit = 103, //! < insect-trainer (unused) - OBJECT_MOBILEwa = 110, //! < WheeledGrabber - OBJECT_MOBILEta = 111, //! < TrackedGrabber - OBJECT_MOBILEfa = 112, //! < WingedGrabber - OBJECT_MOBILEia = 113, //! < LeggedGrabber - OBJECT_MOBILEwc = 120, //! < WheeledShooter - OBJECT_MOBILEtc = 121, //! < TrackedShooter - OBJECT_MOBILEfc = 122, //! < WingedShooter - OBJECT_MOBILEic = 123, //! < LeggedShooter - OBJECT_MOBILEwi = 130, //! < WheeledOrgaShooter - OBJECT_MOBILEti = 131, //! < TrackedOrgaShooter - OBJECT_MOBILEfi = 132, //! < WingedOrgaShooter - OBJECT_MOBILEii = 133, //! < LeggedOrgaShooter - OBJECT_MOBILEws = 140, //! < WheeledSniffer - OBJECT_MOBILEts = 141, //! < TrackedSniffer - OBJECT_MOBILEfs = 142, //! < WingedSniffer - OBJECT_MOBILEis = 143, //! < LeggedSniffer - OBJECT_MOBILErt = 200, //! < Thumper - OBJECT_MOBILErc = 201, //! < PhazerShooter - OBJECT_MOBILErr = 202, //! < Recycler - OBJECT_MOBILErs = 203, //! < Shielder - OBJECT_MOBILEsa = 210, //! < Subber - OBJECT_MOBILEtg = 211, //! < TargetBot - OBJECT_MOBILEdr = 212, //! < Scribbler - OBJECT_CONTROLLER = 213, //! < MissionController - OBJECT_WAYPOINT = 250, //! < WayPoint - OBJECT_FLAGb = 260, //! < BlueFlag - OBJECT_FLAGr = 261, //! < RedFlag - OBJECT_FLAGg = 262, //! < GreenFlag - OBJECT_FLAGy = 263, //! < YellowFlag - OBJECT_FLAGv = 264, //! < VioletFlag - OBJECT_KEYa = 270, //! < KeyA - OBJECT_KEYb = 271, //! < KeyB - OBJECT_KEYc = 272, //! < KeyC - OBJECT_KEYd = 273, //! < KeyD - OBJECT_HUMAN = 300, //! < Me - OBJECT_TOTO = 301, //! < Robby (toto) - OBJECT_TECH = 302, //! < Tech - OBJECT_BARRIER0 = 400, //! < Barrier0 - OBJECT_BARRIER1 = 401, //! < Barrier1 - OBJECT_BARRIER2 = 402, //! < Barrier2 - OBJECT_BARRIER3 = 403, //! < Barrier3 - OBJECT_MOTHER = 500, //! < AlienQueen - OBJECT_EGG = 501, //! < AlienEgg - OBJECT_ANT = 502, //! < AlienAnt - OBJECT_SPIDER = 503, //! < AlienSpider - OBJECT_BEE = 504, //! < AlienWasp - OBJECT_WORM = 505, //! < AlienWorm - OBJECT_RUINmobilew1 = 600, //! < WreckBotw1 - OBJECT_RUINmobilew2 = 601, //! < WreckBotw2 - OBJECT_RUINmobilet1 = 602, //! < WreckBott1 - OBJECT_RUINmobilet2 = 603, //! < WreckBott2 - OBJECT_RUINmobiler1 = 604, //! < WreckBotr1 - OBJECT_RUINmobiler2 = 605, //! < WreckBotr2 - OBJECT_RUINfactory = 606, //! < RuinBotFactory - OBJECT_RUINdoor = 607, //! < RuinDoor - OBJECT_RUINsupport = 608, //! < RuinSupport - OBJECT_RUINradar = 609, //! < RuinRadar - OBJECT_RUINconvert = 610, //! < RuinConvert - OBJECT_RUINbase = 611, //! < RuinBaseCamp - OBJECT_RUINhead = 612, //! < RuinHeadCamp - OBJECT_TEEN0 = 620, //! < Teen0 - OBJECT_TEEN1 = 621, //! < Teen1 - OBJECT_TEEN2 = 622, //! < Teen2 - OBJECT_TEEN3 = 623, //! < Teen3 - OBJECT_TEEN4 = 624, //! < Teen4 - OBJECT_TEEN5 = 625, //! < Teen5 - OBJECT_TEEN6 = 626, //! < Teen6 - OBJECT_TEEN7 = 627, //! < Teen7 - OBJECT_TEEN8 = 628, //! < Teen8 - OBJECT_TEEN9 = 629, //! < Teen9 - OBJECT_TEEN10 = 630, //! < Teen10 - OBJECT_TEEN11 = 631, //! < Teen11 - OBJECT_TEEN12 = 632, //! < Teen12 - OBJECT_TEEN13 = 633, //! < Teen13 - OBJECT_TEEN14 = 634, //! < Teen14 - OBJECT_TEEN15 = 635, //! < Teen15 - OBJECT_TEEN16 = 636, //! < Teen16 - OBJECT_TEEN17 = 637, //! < Teen17 - OBJECT_TEEN18 = 638, //! < Teen18 - OBJECT_TEEN19 = 639, //! < Teen19 - OBJECT_TEEN20 = 640, //! < Teen20 - OBJECT_TEEN21 = 641, //! < Teen21 - OBJECT_TEEN22 = 642, //! < Teen22 - OBJECT_TEEN23 = 643, //! < Teen23 - OBJECT_TEEN24 = 644, //! < Teen24 - OBJECT_TEEN25 = 645, //! < Teen25 - OBJECT_TEEN26 = 646, //! < Teen26 - OBJECT_TEEN27 = 647, //! < Teen27 - OBJECT_TEEN28 = 648, //! < Teen28 - OBJECT_TEEN29 = 649, //! < Teen29 - OBJECT_TEEN30 = 650, //! < Teen30 - OBJECT_TEEN31 = 651, //! < Teen31 - OBJECT_TEEN32 = 652, //! < Teen32 - OBJECT_TEEN33 = 653, //! < Teen33 - OBJECT_TEEN34 = 654, //! < Stone (Teen34) - OBJECT_TEEN35 = 655, //! < Teen35 - OBJECT_TEEN36 = 656, //! < Teen36 - OBJECT_TEEN37 = 657, //! < Teen37 - OBJECT_TEEN38 = 658, //! < Teen38 - OBJECT_TEEN39 = 659, //! < Teen39 - OBJECT_TEEN40 = 660, //! < Teen40 - OBJECT_TEEN41 = 661, //! < Teen41 - OBJECT_TEEN42 = 662, //! < Teen42 - OBJECT_TEEN43 = 663, //! < Teen43 - OBJECT_TEEN44 = 664, //! < Teen44 - OBJECT_QUARTZ0 = 700, //! < Quartz0 - OBJECT_QUARTZ1 = 701, //! < Quartz1 - OBJECT_QUARTZ2 = 702, //! < Quartz2 - OBJECT_QUARTZ3 = 703, //! < Quartz3 - OBJECT_ROOT0 = 710, //! < MegaStalk0 - OBJECT_ROOT1 = 711, //! < MegaStalk1 - OBJECT_ROOT2 = 712, //! < MegaStalk2 - OBJECT_ROOT3 = 713, //! < MegaStalk3 - OBJECT_ROOT4 = 714, //! < MegaStalk4 - OBJECT_ROOT5 = 715, //! < MegaStalk5 - OBJECT_MUSHROOM1 = 731, //! < Mushroom1 - OBJECT_MUSHROOM2 = 732, //! < Mushroom2 - OBJECT_APOLLO1 = 900, //! < ApolloLEM - OBJECT_APOLLO2 = 901, //! < ApolloJeep - OBJECT_APOLLO3 = 902, //! < ApolloFlag - OBJECT_APOLLO4 = 903, //! < ApolloModule - OBJECT_APOLLO5 = 904, //! < ApolloAntenna - OBJECT_HOME1 = 910, //! < Home + OBJECT_PORTICO = 2, //!< Portico + OBJECT_BASE = 3, //!< SpaceShip + OBJECT_DERRICK = 4, //!< Derrick + OBJECT_FACTORY = 5, //!< BotFactory + OBJECT_STATION = 6, //!< PowerStation + OBJECT_CONVERT = 7, //!< Converter + OBJECT_REPAIR = 8, //!< RepairStation + OBJECT_TOWER = 9, //!< DefenseTower + OBJECT_NEST = 10, //!< AlienNest + OBJECT_RESEARCH = 11, //!< ResearchCenter + OBJECT_RADAR = 12, //!< RadarStation + OBJECT_ENERGY = 13, //!< PowerPlant + OBJECT_LABO = 14, //!< AutoLab + OBJECT_NUCLEAR = 15, //!< NuclearPlant + OBJECT_START = 16, //!< StartArea + OBJECT_END = 17, //!< EndArea + OBJECT_INFO = 18, //!< ExchangePost + OBJECT_PARA = 19, //!< PowerCaptor + OBJECT_TARGET1 = 20, //!< Target1 (gate) + OBJECT_TARGET2 = 21, //!< Target2 (center) + OBJECT_SAFE = 22, //!< Vault + OBJECT_HUSTON = 23, //!< Houston + OBJECT_DESTROYER = 24, //!< Destroyer + OBJECT_STONE = 31, //!< TitaniumOre + OBJECT_URANIUM = 32, //!< UraniumOre + OBJECT_METAL = 33, //!< Titanium + OBJECT_POWER = 34, //!< PowerCell + OBJECT_ATOMIC = 35, //!< NuclearCell + OBJECT_BULLET = 36, //!< OrgaMatter + OBJECT_BBOX = 37, //!< BlackBox + OBJECT_TNT = 38, //!< TNT + OBJECT_MARKPOWER = 50, //!< PowerSpot + OBJECT_MARKSTONE = 51, //!< TitaniumSpot + OBJECT_MARKURANIUM = 52, //!< UraniumSpot + OBJECT_MARKKEYa = 53, //!< KeyASpot + OBJECT_MARKKEYb = 54, //!< KeyBSpot + OBJECT_MARKKEYc = 55, //!< KeyCSpot + OBJECT_MARKKEYd = 56, //!< KeyDSpot + OBJECT_BOMB = 60, //!< Mine + OBJECT_WINFIRE = 61, //!< Firework + OBJECT_SHOW = 62, //!< arrow above object (Visit) + OBJECT_BAG = 63, //!< Bag + OBJECT_PLANT0 = 70, //!< Greenery0 + OBJECT_PLANT1 = 71, //!< Greenery1 + OBJECT_PLANT2 = 72, //!< Greenery2 + OBJECT_PLANT3 = 73, //!< Greenery3 + OBJECT_PLANT4 = 74, //!< Greenery4 + OBJECT_PLANT5 = 75, //!< Greenery5 + OBJECT_PLANT6 = 76, //!< Greenery6 + OBJECT_PLANT7 = 77, //!< Greenery7 + OBJECT_PLANT8 = 78, //!< Greenery8 + OBJECT_PLANT9 = 79, //!< Greenery9 + OBJECT_PLANT10 = 80, //!< Greenery10 + OBJECT_PLANT11 = 81, //!< Greenery11 + OBJECT_PLANT12 = 82, //!< Greenery12 + OBJECT_PLANT13 = 83, //!< Greenery13 + OBJECT_PLANT14 = 84, //!< Greenery14 + OBJECT_PLANT15 = 85, //!< Greenery15 + OBJECT_PLANT16 = 86, //!< Greenery16 + OBJECT_PLANT17 = 87, //!< Greenery17 + OBJECT_PLANT18 = 88, //!< Greenery18 + OBJECT_PLANT19 = 89, //!< Greenery19 + OBJECT_TREE0 = 90, //!< Tree0 + OBJECT_TREE1 = 91, //!< Tree1 + OBJECT_TREE2 = 92, //!< Tree2 + OBJECT_TREE3 = 93, //!< Tree3 + OBJECT_TREE4 = 94, //!< Tree4 + OBJECT_TREE5 = 95, //!< Tree5 + OBJECT_MOBILEwt = 100, //!< PracticeBot + OBJECT_MOBILEtt = 101, //!< track-trainer (unused) + OBJECT_MOBILEft = 102, //!< fly-trainer (unused) + OBJECT_MOBILEit = 103, //!< insect-trainer (unused) + OBJECT_MOBILEwa = 110, //!< WheeledGrabber + OBJECT_MOBILEta = 111, //!< TrackedGrabber + OBJECT_MOBILEfa = 112, //!< WingedGrabber + OBJECT_MOBILEia = 113, //!< LeggedGrabber + OBJECT_MOBILEwc = 120, //!< WheeledShooter + OBJECT_MOBILEtc = 121, //!< TrackedShooter + OBJECT_MOBILEfc = 122, //!< WingedShooter + OBJECT_MOBILEic = 123, //!< LeggedShooter + OBJECT_MOBILEwi = 130, //!< WheeledOrgaShooter + OBJECT_MOBILEti = 131, //!< TrackedOrgaShooter + OBJECT_MOBILEfi = 132, //!< WingedOrgaShooter + OBJECT_MOBILEii = 133, //!< LeggedOrgaShooter + OBJECT_MOBILEws = 140, //!< WheeledSniffer + OBJECT_MOBILEts = 141, //!< TrackedSniffer + OBJECT_MOBILEfs = 142, //!< WingedSniffer + OBJECT_MOBILEis = 143, //!< LeggedSniffer + OBJECT_MOBILErt = 200, //!< Thumper + OBJECT_MOBILErc = 201, //!< PhazerShooter + OBJECT_MOBILErr = 202, //!< Recycler + OBJECT_MOBILErs = 203, //!< Shielder + OBJECT_MOBILEsa = 210, //!< Subber + OBJECT_MOBILEtg = 211, //!< TargetBot + OBJECT_MOBILEdr = 212, //!< Scribbler + OBJECT_CONTROLLER = 213, //!< MissionController + OBJECT_WAYPOINT = 250, //!< WayPoint + OBJECT_FLAGb = 260, //!< BlueFlag + OBJECT_FLAGr = 261, //!< RedFlag + OBJECT_FLAGg = 262, //!< GreenFlag + OBJECT_FLAGy = 263, //!< YellowFlag + OBJECT_FLAGv = 264, //!< VioletFlag + OBJECT_KEYa = 270, //!< KeyA + OBJECT_KEYb = 271, //!< KeyB + OBJECT_KEYc = 272, //!< KeyC + OBJECT_KEYd = 273, //!< KeyD + OBJECT_HUMAN = 300, //!< Me + OBJECT_TOTO = 301, //!< Robby (toto) + OBJECT_TECH = 302, //!< Tech + OBJECT_BARRIER0 = 400, //!< Barrier0 + OBJECT_BARRIER1 = 401, //!< Barrier1 + OBJECT_BARRIER2 = 402, //!< Barrier2 + OBJECT_BARRIER3 = 403, //!< Barrier3 + OBJECT_BARRICADE0 = 410, //!< Barricade0 + OBJECT_BARRICADE1 = 411, //!< Barricade1 + OBJECT_MOTHER = 500, //!< AlienQueen + OBJECT_EGG = 501, //!< AlienEgg + OBJECT_ANT = 502, //!< AlienAnt + OBJECT_SPIDER = 503, //!< AlienSpider + OBJECT_BEE = 504, //!< AlienWasp + OBJECT_WORM = 505, //!< AlienWorm + OBJECT_RUINmobilew1 = 600, //!< WreckBotw1 + OBJECT_RUINmobilew2 = 601, //!< WreckBotw2 + OBJECT_RUINmobilet1 = 602, //!< WreckBott1 + OBJECT_RUINmobilet2 = 603, //!< WreckBott2 + OBJECT_RUINmobiler1 = 604, //!< WreckBotr1 + OBJECT_RUINmobiler2 = 605, //!< WreckBotr2 + OBJECT_RUINfactory = 606, //!< RuinBotFactory + OBJECT_RUINdoor = 607, //!< RuinDoor + OBJECT_RUINsupport = 608, //!< RuinSupport + OBJECT_RUINradar = 609, //!< RuinRadar + OBJECT_RUINconvert = 610, //!< RuinConvert + OBJECT_RUINbase = 611, //!< RuinBaseCamp + OBJECT_RUINhead = 612, //!< RuinHeadCamp + OBJECT_TEEN0 = 620, //!< Teen0 + OBJECT_TEEN1 = 621, //!< Teen1 + OBJECT_TEEN2 = 622, //!< Teen2 + OBJECT_TEEN3 = 623, //!< Teen3 + OBJECT_TEEN4 = 624, //!< Teen4 + OBJECT_TEEN5 = 625, //!< Teen5 + OBJECT_TEEN6 = 626, //!< Teen6 + OBJECT_TEEN7 = 627, //!< Teen7 + OBJECT_TEEN8 = 628, //!< Teen8 + OBJECT_TEEN9 = 629, //!< Teen9 + OBJECT_TEEN10 = 630, //!< Teen10 + OBJECT_TEEN11 = 631, //!< Teen11 + OBJECT_TEEN12 = 632, //!< Teen12 + OBJECT_TEEN13 = 633, //!< Teen13 + OBJECT_TEEN14 = 634, //!< Teen14 + OBJECT_TEEN15 = 635, //!< Teen15 + OBJECT_TEEN16 = 636, //!< Teen16 + OBJECT_TEEN17 = 637, //!< Teen17 + OBJECT_TEEN18 = 638, //!< Teen18 + OBJECT_TEEN19 = 639, //!< Teen19 + OBJECT_TEEN20 = 640, //!< Teen20 + OBJECT_TEEN21 = 641, //!< Teen21 + OBJECT_TEEN22 = 642, //!< Teen22 + OBJECT_TEEN23 = 643, //!< Teen23 + OBJECT_TEEN24 = 644, //!< Teen24 + OBJECT_TEEN25 = 645, //!< Teen25 + OBJECT_TEEN26 = 646, //!< Teen26 + OBJECT_TEEN27 = 647, //!< Teen27 + OBJECT_TEEN28 = 648, //!< Teen28 + OBJECT_TEEN29 = 649, //!< Teen29 + OBJECT_TEEN30 = 650, //!< Teen30 + OBJECT_TEEN31 = 651, //!< Teen31 + OBJECT_TEEN32 = 652, //!< Teen32 + OBJECT_TEEN33 = 653, //!< Teen33 + OBJECT_TEEN34 = 654, //!< Stone (Teen34) + OBJECT_TEEN35 = 655, //!< Teen35 + OBJECT_TEEN36 = 656, //!< Teen36 + OBJECT_TEEN37 = 657, //!< Teen37 + OBJECT_TEEN38 = 658, //!< Teen38 + OBJECT_TEEN39 = 659, //!< Teen39 + OBJECT_TEEN40 = 660, //!< Teen40 + OBJECT_TEEN41 = 661, //!< Teen41 + OBJECT_TEEN42 = 662, //!< Teen42 + OBJECT_TEEN43 = 663, //!< Teen43 + OBJECT_TEEN44 = 664, //!< Teen44 + OBJECT_QUARTZ0 = 700, //!< Quartz0 + OBJECT_QUARTZ1 = 701, //!< Quartz1 + OBJECT_QUARTZ2 = 702, //!< Quartz2 + OBJECT_QUARTZ3 = 703, //!< Quartz3 + OBJECT_ROOT0 = 710, //!< MegaStalk0 + OBJECT_ROOT1 = 711, //!< MegaStalk1 + OBJECT_ROOT2 = 712, //!< MegaStalk2 + OBJECT_ROOT3 = 713, //!< MegaStalk3 + OBJECT_ROOT4 = 714, //!< MegaStalk4 + OBJECT_ROOT5 = 715, //!< MegaStalk5 + OBJECT_MUSHROOM1 = 731, //!< Mushroom1 + OBJECT_MUSHROOM2 = 732, //!< Mushroom2 + OBJECT_APOLLO1 = 900, //!< ApolloLEM + OBJECT_APOLLO2 = 901, //!< ApolloJeep + OBJECT_APOLLO3 = 902, //!< ApolloFlag + OBJECT_APOLLO4 = 903, //!< ApolloModule + OBJECT_APOLLO5 = 904, //!< ApolloAntenna + OBJECT_HOME1 = 910, //!< Home - OBJECT_MAX = 1000 //! < number of values + OBJECT_MAX = 1000 //!< number of values }; struct ObjectTypeHash diff --git a/src/object/old_object.cpp b/src/object/old_object.cpp index c8937423..dbe92b32 100644 --- a/src/object/old_object.cpp +++ b/src/object/old_object.cpp @@ -34,6 +34,7 @@ #include "graphics/engine/terrain.h" #include "level/robotmain.h" +#include "level/scoreboard.h" #include "level/parser/parserexceptions.h" #include "level/parser/parserline.h" @@ -202,6 +203,7 @@ void COldObject::DeleteObject(bool bAll) if ( !bAll ) { m_engine->GetPyroManager()->CutObjectLink(this); + m_particle->CutObjectLink(this); if ( m_bSelect ) { @@ -335,7 +337,7 @@ void COldObject::Simplify() } -bool COldObject::DamageObject(DamageType type, float force) +bool COldObject::DamageObject(DamageType type, float force, CObject* killer) { assert(Implements(ObjectInterfaceType::Damageable)); assert(!Implements(ObjectInterfaceType::Destroyable) || Implements(ObjectInterfaceType::Shielded) || Implements(ObjectInterfaceType::Fragile)); @@ -354,8 +356,9 @@ bool COldObject::DamageObject(DamageType type, float force) else if ( Implements(ObjectInterfaceType::Fragile) ) { if ( m_type == OBJECT_BOMB && type != DamageType::Explosive ) return false; // Mine can't be destroyed by shooting + if ( m_type == OBJECT_URANIUM ) return false; // UraniumOre is not destroyable (see #777) - DestroyObject(DestructionType::Explosion); + DestroyObject(DestructionType::Explosion, killer); return true; } @@ -400,11 +403,11 @@ bool COldObject::DamageObject(DamageType type, float force) { if (type == DamageType::Fire) { - DestroyObject(DestructionType::Burn); + DestroyObject(DestructionType::Burn, killer); } else { - DestroyObject(DestructionType::Explosion); + DestroyObject(DestructionType::Explosion, killer); } return true; } @@ -425,7 +428,7 @@ bool COldObject::DamageObject(DamageType type, float force) return false; } -void COldObject::DestroyObject(DestructionType type) +void COldObject::DestroyObject(DestructionType type, CObject* killer) { assert(Implements(ObjectInterfaceType::Destroyable)); @@ -522,7 +525,17 @@ void COldObject::DestroyObject(DestructionType type) { pyroType = Gfx::PT_DEADW; } + else if ( type == DestructionType::Win ) + { + pyroType = Gfx::PT_WPCHECK; + } assert(pyroType != Gfx::PT_NULL); + if (pyroType == Gfx::PT_FRAGT || + pyroType == Gfx::PT_FRAGO || + pyroType == Gfx::PT_FRAGW) + { + SetDying(DeathType::Exploding); + } m_engine->GetPyroManager()->Create(pyroType, this); if ( Implements(ObjectInterfaceType::Programmable) ) @@ -539,6 +552,10 @@ void COldObject::DestroyObject(DestructionType type) } m_main->RemoveFromSelectionHistory(this); + CScoreboard* scoreboard = m_main->GetScoreboard(); + if (scoreboard) + scoreboard->ProcessKill(this, killer); + m_team = 0; // Back to neutral on destruction if ( m_botVar != nullptr ) @@ -940,6 +957,9 @@ void COldObject::Write(CLevelParserLine* line) if ( GetCameraLock() ) line->AddParam("cameraLock", MakeUnique(GetCameraLock())); + if ( IsBulletWall() ) + line->AddParam("bulletWall", MakeUnique(IsBulletWall())); + if ( GetEnergyLevel() != 0.0f ) line->AddParam("energy", MakeUnique(GetEnergyLevel())); @@ -1035,13 +1055,15 @@ void COldObject::Read(CLevelParserLine* line) if (line->GetParam("pyro")->IsDefined()) m_engine->GetPyroManager()->Create(line->GetParam("pyro")->AsPyroType(), this); + SetBulletWall(line->GetParam("bulletWall")->AsBool(IsBulletWallByDefault(m_type))); + SetProxyActivate(line->GetParam("proxyActivate")->AsBool(false)); SetProxyDistance(line->GetParam("proxyDistance")->AsFloat(15.0f)*g_unit); SetCollisions(line->GetParam("clip")->AsBool(true)); SetAnimateOnReset(line->GetParam("reset")->AsBool(false)); if (Implements(ObjectInterfaceType::Controllable)) { - SetSelectable(line->GetParam("selectable")->AsBool(true)); + SetSelectable(line->GetParam("selectable")->AsBool(IsSelectableByDefault(m_type))); } if (Implements(ObjectInterfaceType::JetFlying)) { @@ -2654,7 +2676,10 @@ bool COldObject::IsDying() bool COldObject::GetActive() { - return !GetLock() && !(Implements(ObjectInterfaceType::Destroyable) && IsDying()) && !m_bFlat; + // Dying astronaut (m_dying == DeathType::Dead) should be treated as active + // This is for EndMissionTake to not detect him as actually dead until the animation is finished + + return !GetLock() && !(Implements(ObjectInterfaceType::Destroyable) && IsDying() && GetDying() != DeathType::Dead) && !m_bFlat; } bool COldObject::GetDetectable() @@ -3171,3 +3196,37 @@ float COldObject::GetLightningHitProbability() } return 0.0f; } + +bool COldObject::IsSelectableByDefault(ObjectType type) +{ + if ( type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_MOBILEtg ) + { + return false; + } + return true; +} + +void COldObject::SetBulletWall(bool bulletWall) +{ + m_bulletWall = bulletWall; +} + +bool COldObject::IsBulletWall() +{ + return m_bulletWall; +} + +bool COldObject::IsBulletWallByDefault(ObjectType type) +{ + if ( type == OBJECT_BARRICADE0 || + type == OBJECT_BARRICADE1 ) + { + return true; + } + return false; +} diff --git a/src/object/old_object.h b/src/object/old_object.h index 5b85c286..697e05db 100644 --- a/src/object/old_object.h +++ b/src/object/old_object.h @@ -110,8 +110,8 @@ public: void Simplify() override; - bool DamageObject(DamageType type, float force = std::numeric_limits::infinity()) override; - void DestroyObject(DestructionType type) override; + bool DamageObject(DamageType type, float force = std::numeric_limits::infinity(), CObject* killer = nullptr) override; + void DestroyObject(DestructionType type, CObject* killer = nullptr) override; bool EventProcess(const Event& event) override; void UpdateMapping(); @@ -288,6 +288,9 @@ public: float GetLightningHitProbability() override; + void SetBulletWall(bool bulletWall); + bool IsBulletWall() override; + protected: bool EventFrame(const Event &event); void VirusFrame(float rTime); @@ -302,6 +305,18 @@ protected: void TransformCrashSphere(Math::Sphere &crashSphere) override; void TransformCameraCollisionSphere(Math::Sphere& collisionSphere) override; + /** + * \brief Check if given object type should be selectable by default + * \note This is a default value for the selectable= parameter and can still be overriden in the scene file or using the \a selectinsect cheat + */ + static bool IsSelectableByDefault(ObjectType type); + + /** + * \brief Check if given object type should have bulletWall enabled by default + * \note This is a default value for the bulletWall= parameter and can still be overriden in the scene file + */ + static bool IsBulletWallByDefault(ObjectType type); + protected: Gfx::CEngine* m_engine; Gfx::CLightManager* m_lightMan; @@ -370,4 +385,6 @@ protected: bool m_traceDown; TraceColor m_traceColor; float m_traceWidth; + + bool m_bulletWall = false; }; diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index de37d1a1..5d6cd475 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -1072,7 +1072,45 @@ Error CTaskGoto::IsEnded() CObject* CTaskGoto::SearchTarget(Math::Vector pos, float margin) { - return CObjectManager::GetInstancePointer()->FindNearest(nullptr, pos, OBJECT_NULL, margin/g_unit); + //return CObjectManager::GetInstancePointer()->FindNearest(nullptr, pos, OBJECT_NULL, margin/g_unit); + + /* + * TODO: FindNearest() can't be used here. Reverted to code from before 4fef3af9ef1fbe61a0c4c3f5c176f56257428efb + * + * The reason is that in the case of multiple objects being placed at the same position, + * this function needs to return the last one in order of creation. FindNearest() does the opposite. + * + * Whoever designed goto() so that it has to guess which object the user wants based only on position - thanks + * for making it so confusing :/ + * + * This works well enough assuming that portable objects from the level file are always created after the objects + * they are placed on, for example BlackBox is created after GoalArea, TitaniumOre is created after Converter etc. + * This is probably required anyway to prevent them from sinking into the ground. + * + * User-created objects don't make a difference because there is no way you can place them precisely enough + * for floats to compare with ==. + * + * See issue #732 + */ + + CObject* pBest = nullptr; + float min = 1000000.0f; + for ( CObject* pObj : CObjectManager::GetInstancePointer()->GetAllObjects() ) + { + if ( !pObj->GetActive() ) continue; + if ( IsObjectBeingTransported(pObj) ) continue; // object transtorted? + + Math::Vector oPos = pObj->GetPosition(); + float dist = Math::DistanceProjected(pos, oPos); + + if ( dist <= margin && dist <= min ) + { + min = dist; + pBest = pObj; + } + } + + return pBest; } // Adjusts the target as a function of the object. diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 8fcc08c3..1e801d11 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -2688,19 +2688,21 @@ bool CPhysics::ExploOther(ObjectType iType, { JostleObject(pObj, 1.0f); // shakes the object - if (pObj->Implements(ObjectInterfaceType::Fragile)) + if (pObj->Implements(ObjectInterfaceType::Damageable)) { // TODO: CFragileObject::GetDestructionForce (I can't do this now because you can't inherit both in COldObject ~krzys_h) DamageType damageType = DamageType::Collision; - float destructionForce = 50.0f; // Titanium, PowerCell, NuclearCell, default + float destructionForce = pObj->Implements(ObjectInterfaceType::Fragile) ? 50.0f : -1.0f; // Titanium, PowerCell, NuclearCell, default if (pObj->GetType() == OBJECT_STONE ) { destructionForce = 25.0f; } // TitaniumOre if (pObj->GetType() == OBJECT_URANIUM ) { destructionForce = 25.0f; } // UraniumOre - if (pObj->GetType() == OBJECT_MOBILEtg) { destructionForce = 10.0f; damageType = DamageType::Explosive; } // TargetBot + if (pObj->GetType() == OBJECT_MOBILEtg) { destructionForce = 10.0f; damageType = DamageType::Explosive; } // TargetBot (something running into it) + if (iType == OBJECT_MOBILEtg) { destructionForce = 10.0f; damageType = DamageType::Explosive; } // TargetBot (it running into something) if (pObj->GetType() == OBJECT_TNT ) { destructionForce = 10.0f; damageType = DamageType::Explosive; } // TNT if (pObj->GetType() == OBJECT_BOMB ) { destructionForce = 0.0f; damageType = DamageType::Explosive; } // Mine - if ( force > destructionForce ) + if ( force > destructionForce && destructionForce >= 0.0f ) { + // TODO: implement "killer"? dynamic_cast(pObj)->DamageObject(damageType); } } @@ -2726,6 +2728,7 @@ bool CPhysics::ExploOther(ObjectType iType, oType == OBJECT_HUSTON ) // building? { assert(pObj->Implements(ObjectInterfaceType::Damageable)); + // TODO: implement "killer"? dynamic_cast(pObj)->DamageObject(DamageType::Collision, force/400.0f); } @@ -2756,6 +2759,7 @@ bool CPhysics::ExploOther(ObjectType iType, oType == OBJECT_MOBILEit ) // vehicle? { assert(pObj->Implements(ObjectInterfaceType::Damageable)); + // TODO: implement "killer"? dynamic_cast(pObj)->DamageObject(DamageType::Collision, force/200.0f); } } @@ -2775,11 +2779,13 @@ int CPhysics::ExploHimself(ObjectType iType, ObjectType oType, float force) // TODO: CExplosiveObject? derrives from CFragileObject float destructionForce = -1.0f; // minimal force required to destroy an object using this explosive, default: not explosive if ( oType == OBJECT_TNT ) destructionForce = 10.0f; // TNT - if ( oType == OBJECT_MOBILEtg ) destructionForce = 10.0f; // TargetBot + if ( oType == OBJECT_MOBILEtg ) destructionForce = 10.0f; // TargetBot (something running into it) + if ( iType == OBJECT_MOBILEtg ) destructionForce = 10.0f; // TargetBot (it running into something) if ( oType == OBJECT_BOMB ) destructionForce = 0.0f; // Mine if ( force > destructionForce && destructionForce >= 0.0f ) { + // TODO: implement "killer"? dynamic_cast(m_object)->DamageObject(DamageType::Explosive); return 2; } @@ -2860,6 +2866,7 @@ int CPhysics::ExploHimself(ObjectType iType, ObjectType oType, float force) force /= 200.0f; } + // TODO: implement "killer"? if ( dynamic_cast(m_object)->DamageObject(DamageType::Collision, force) ) return 2; } } diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index 7f6cb97b..5fc97564 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -413,9 +413,9 @@ bool CScriptFunctions::rDestroy(CBotVar* thisclass, CBotVar* var, CBotVar* resul else err = ERR_WRONG_OBJ; + result->SetValInt(err); // indicates the error or ok if ( err != ERR_OK ) { - result->SetValInt(err); // return error if ( script->m_errMode == ERM_STOP ) { exception = err; @@ -506,9 +506,9 @@ bool CScriptFunctions::rFactory(CBotVar* thisclass, CBotVar* var, CBotVar* resul else err = ERR_WRONG_OBJ; + result->SetValInt(err); // indicates the error or ok if ( err != ERR_OK ) { - result->SetValInt(err); // return error if ( script->m_errMode == ERM_STOP ) { exception = err; @@ -581,9 +581,9 @@ bool CScriptFunctions::rResearch(CBotVar* thisclass, CBotVar* var, CBotVar* resu else err = ERR_WRONG_OBJ; + result->SetValInt(err); // indicates the error or ok if ( err != ERR_OK ) { - result->SetValInt(err); // return error if( script->m_errMode == ERM_STOP ) { exception = err; @@ -591,7 +591,7 @@ bool CScriptFunctions::rResearch(CBotVar* thisclass, CBotVar* var, CBotVar* resu } return true; } - + return true; } @@ -621,9 +621,9 @@ bool CScriptFunctions::rTakeOff(CBotVar* thisclass, CBotVar* var, CBotVar* resul else err = ERR_WRONG_OBJ; + result->SetValInt(err); // indicates the error or ok if ( err != ERR_OK ) { - result->SetValInt(err); // return error if ( script->m_errMode == ERM_STOP ) { exception = err; @@ -682,6 +682,16 @@ bool CScriptFunctions::rDelete(CBotVar* var, CBotVar* result, int& exception, vo } else { + if (obj->Implements(ObjectInterfaceType::Old)) + { + COldObject* oldobj = dynamic_cast(obj); + if (oldobj->GetPower() != nullptr) + CObjectManager::GetInstancePointer()->DeleteObject(oldobj->GetPower()); + if (oldobj->GetCargo() != nullptr) + CObjectManager::GetInstancePointer()->DeleteObject(oldobj->GetCargo()); + oldobj->SetPower(nullptr); + oldobj->SetCargo(nullptr); + } CObjectManager::GetInstancePointer()->DeleteObject(obj); } } @@ -1229,10 +1239,9 @@ bool CScriptFunctions::rBuild(CBotVar* var, CBotVar* result, int& exception, voi } } } - + result->SetValInt(err); // indicates the error or ok if ( err != ERR_OK ) { - result->SetValInt(err); // return error if ( script->m_errMode == ERM_STOP ) { exception = err; @@ -1393,6 +1402,7 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v CProgramStorageObject* programStorage = dynamic_cast(object); Program* program = programStorage->AddProgram(); programStorage->ReadProgram(program, name2.c_str()); + program->readOnly = true; program->filename = name; dynamic_cast(object)->RunProgram(program); } @@ -2378,11 +2388,15 @@ bool CScriptFunctions::rFire(CBotVar* var, CBotVar* result, int& exception, void if ( delay < 0.0f ) delay = -delay; err = script->m_taskExecutor->StartTaskFire(delay); } - + result->SetValInt(err); // indicates the error or ok if ( err != ERR_OK ) { script->m_taskExecutor->StopForegroundTask(); - result->SetValInt(err); // shows the error + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return false; + } return true; } } diff --git a/src/sound/sound_type.cpp b/src/sound/sound_type.cpp index 5eb4c53e..964782d2 100644 --- a/src/sound/sound_type.cpp +++ b/src/sound/sound_type.cpp @@ -109,7 +109,8 @@ const std::unordered_map SOUND_STRINGS = {"mushroom", SOUND_MUSHROOM}, {"firep", SOUND_FIREp}, {"explog1", SOUND_EXPLOg1}, - {"explog2", SOUND_EXPLOg2} + {"explog2", SOUND_EXPLOg2}, + {"alarms", SOUND_ALARMs} }; } // anonymous namespace diff --git a/src/sound/sound_type.h b/src/sound/sound_type.h index 360708dd..6985622f 100644 --- a/src/sound/sound_type.h +++ b/src/sound/sound_type.h @@ -116,6 +116,7 @@ enum SoundType SOUND_EXPLOg1 = 79, /*!< ShooterBots damaging objects. */ SOUND_EXPLOg2 = 80, /*!< OrgaShooterBots damaging objects. */ SOUND_MOTORd = 81, /*!< Scribbler/ToyBot engine. */ + SOUND_ALARMs = 82, /*!< Bot shield alarm. */ SOUND_MAX /** number of items in enum */ }; diff --git a/src/ui/controls/edit.cpp b/src/ui/controls/edit.cpp index f453c8fd..7d08b260 100644 --- a/src/ui/controls/edit.cpp +++ b/src/ui/controls/edit.cpp @@ -84,7 +84,17 @@ bool IsSep(int character) return !IsWord(character); } +bool IsBreaker(char c) +{ + return ( c == '.' || c == '{' || c == '}' || + c == ';' || c == ':' || c == '[' || c == ']' || + c == '(' || c == ')' || c == '=' || c == '"' || c == '\'' ); +} +bool IsDelimiter(char c) +{ + return IsSpace( c ) || IsBreaker( c ); +} //! Object's constructor. CEdit::CEdit() @@ -138,7 +148,7 @@ CEdit::~CEdit() if (m_bFocus) { - CApplication::GetInstancePointer()->SetTextInput(false); + CApplication::GetInstancePointer()->SetTextInput(false, m_eventType); } } @@ -449,6 +459,19 @@ bool CEdit::EventProcess(const Event &event) return true; } + if ( data->key == KEY(BACKSPACE) && bControl ) + { + DeleteWord(-1); + SendModifEvent(); + return true; + } + if ( data->key == KEY(DELETE) && bControl ) + { + DeleteWord(1); + SendModifEvent(); + return true; + } + if ( data->key == KEY(RETURN) && !bControl ) { Insert('\n'); @@ -497,13 +520,20 @@ bool CEdit::EventProcess(const Event &event) MouseClick(event.mousePos); if ( m_bEdit || m_bHilite ) m_bCapture = true; } - m_bFocus = true; - UpdateFocus(); + + if (!m_bFocus) + { + m_bFocus = true; + UpdateFocus(); + } } else { - m_bFocus = false; - UpdateFocus(); + if (m_bFocus) + { + m_bFocus = false; + UpdateFocus(); + } } } @@ -707,7 +737,7 @@ int CEdit::MouseDetect(Math::Point mouse) if ( i >= m_lineFirst+m_lineVisible ) break; - pos.x = m_pos.x+(10.0f/640.0f); + pos.x = m_pos.x+(7.5f/640.0f)*(m_fontSize/Gfx::FONT_SIZE_SMALL); if ( m_bAutoIndent ) { pos.x += indentLength*m_lineIndent[i]; @@ -922,7 +952,7 @@ void CEdit::Draw() if ( i >= m_lineFirst+m_lineVisible ) break; - pos.x = m_pos.x+(10.0f/640.0f); + pos.x = m_pos.x+(7.5f/640.0f)*(m_fontSize/Gfx::FONT_SIZE_SMALL); if ( m_bAutoIndent ) { const char *s = "\t"; // line | dotted @@ -1084,7 +1114,7 @@ void CEdit::Draw() { if ( i == m_lineTotal-1 || m_cursor1 < m_lineOffset[i+1] ) { - pos.x = m_pos.x+(10.0f/640.0f); + pos.x = m_pos.x+(7.5f/640.0f)*(m_fontSize/Gfx::FONT_SIZE_SMALL); if ( m_bAutoIndent ) { pos.x += indentLength*m_lineIndent[i]; @@ -2746,6 +2776,60 @@ void CEdit::DeleteOne(int dir) m_cursor2 = m_cursor1; } +// Delete word + +void CEdit::DeleteWord(int dir) +{ + if ( !m_bEdit ) return; + + if ( dir < 0 ) + { + if ( m_cursor1 > 0) m_cursor2 = --m_cursor1; + else m_cursor2 = m_cursor1; + + if ( IsBreaker(m_text[m_cursor1]) ) + { + Delete(1); + return; + } + else ++m_cursor1; + + while ( m_cursor1 < m_len && !IsDelimiter(m_text[m_cursor1]) ) ++m_cursor1; + + while ( m_cursor2 > 0 && IsSpace(m_text[m_cursor2]) ) --m_cursor2; + + if ( !IsDelimiter(m_text[m_cursor2]) ) + { + while ( m_cursor2 > 0 && !IsDelimiter(m_text[m_cursor2]) ) --m_cursor2; + if ( IsBreaker(m_text[m_cursor2]) ) ++m_cursor2; + } + + Delete(-1); + } + else + { + m_cursor2 = m_cursor1; + + while ( m_cursor1 < m_len && IsSpace(m_text[m_cursor1]) ) ++m_cursor1; + + if ( IsBreaker(m_text[m_cursor1]) ) + { + ++m_cursor1; + Delete(1); + return; + } + + while ( m_cursor1 < m_len && !IsDelimiter(m_text[m_cursor1]) ) ++m_cursor1; + + if ( !IsDelimiter(m_text[m_cursor2]) ) + { + while ( m_cursor2 > 0 && !IsDelimiter(m_text[m_cursor2]) ) --m_cursor2; + if ( IsBreaker(m_text[m_cursor2]) ) ++m_cursor2; + } + + Delete(-1); + } +} // Calculates the indentation level of brackets {and}. @@ -2926,7 +3010,7 @@ void CEdit::Justif() { bDual = false; - width = m_dim.x-(10.0f/640.0f)*2.0f-(m_bMulti?MARGX*2.0f+SCROLL_WIDTH:0.0f); + width = m_dim.x-(7.5f/640.0f)*(m_fontSize/Gfx::FONT_SIZE_SMALL)*2.0f-(m_bMulti?MARGX*2.0f+SCROLL_WIDTH:0.0f); if ( m_bAutoIndent ) { width -= indentLength*m_lineIndent[m_lineTotal-1]; @@ -3199,7 +3283,7 @@ void CEdit::SetFocus(CControl* control) void CEdit::UpdateFocus() { - CApplication::GetInstancePointer()->SetTextInput(m_bFocus); + CApplication::GetInstancePointer()->SetTextInput(m_bFocus, m_eventType); } } diff --git a/src/ui/controls/edit.h b/src/ui/controls/edit.h index 4192a133..380fbf5e 100644 --- a/src/ui/controls/edit.h +++ b/src/ui/controls/edit.h @@ -27,6 +27,7 @@ #include "ui/controls/control.h" +#include #include namespace Ui @@ -206,6 +207,7 @@ protected: void InsertOne(char character); void Delete(int dir); void DeleteOne(int dir); + void DeleteWord(int dir); int IndentCompute(); int IndentTabCount(); void IndentTabAdjust(int number); diff --git a/src/ui/controls/map.cpp b/src/ui/controls/map.cpp index 9bc35b91..b22453c5 100644 --- a/src/ui/controls/map.cpp +++ b/src/ui/controls/map.cpp @@ -1164,12 +1164,20 @@ void CMap::UpdateObject(CObject* pObj) if ( !m_bEnable ) return; if ( m_totalFix >= m_totalMove ) return; // full table? + type = pObj->GetType(); if ( !pObj->GetDetectable() ) return; - if ( pObj->Implements(ObjectInterfaceType::Controllable) && !dynamic_cast(pObj)->GetSelectable() ) return; + if ( type != OBJECT_MOTHER && + type != OBJECT_ANT && + type != OBJECT_SPIDER && + type != OBJECT_BEE && + type != OBJECT_WORM && + type != OBJECT_MOBILEtg ) + { + if (pObj->Implements(ObjectInterfaceType::Controllable) && !dynamic_cast(pObj)->GetSelectable()) return; + } if ( pObj->GetProxyActivate() ) return; if (IsObjectBeingTransported(pObj)) return; - type = pObj->GetType(); pos = pObj->GetPosition(); dir = -(pObj->GetRotationY()+Math::PI/2.0f); @@ -1281,7 +1289,7 @@ void CMap::UpdateObject(CObject* pObj) if ( color == MAPCOLOR_NULL ) return; - if (!m_fixImage.empty() && !m_bDebug) // map with still image? + /*if (!m_fixImage.empty() && !m_bDebug) // map with still image? { if ( (type == OBJECT_TEEN28 || type == OBJECT_TEEN34 ) && @@ -1290,7 +1298,7 @@ void CMap::UpdateObject(CObject* pObj) if ( type != OBJECT_TEEN28 && type != OBJECT_TEEN34 && color != MAPCOLOR_MOVE ) return; - } + }*/ if ( pObj->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(pObj)->GetSelect() ) { diff --git a/src/ui/controls/target.cpp b/src/ui/controls/target.cpp index 9ba4f4e0..dd18bd38 100644 --- a/src/ui/controls/target.cpp +++ b/src/ui/controls/target.cpp @@ -149,7 +149,7 @@ CObject* CTarget::DetectFriendObject(Math::Point pos) if ( !target->GetDetectable() ) continue; if ( target->GetProxyActivate() ) continue; if ( target->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(target)->GetSelect() ) continue; - if ( !m_main->IsSelectable(target) ) continue; + if ( !target->Implements(ObjectInterfaceType::Controllable) || !dynamic_cast(target)->GetSelectable() ) continue; if (!target->Implements(ObjectInterfaceType::Old)) continue; // TODO: To be removed after COldObjectInterface is gone diff --git a/src/ui/debug_menu.cpp b/src/ui/debug_menu.cpp index 10aa9397..a6eb6f63 100644 --- a/src/ui/debug_menu.cpp +++ b/src/ui/debug_menu.cpp @@ -19,7 +19,10 @@ #include "ui/debug_menu.h" +#include "app/app.h" + #include "common/event.h" +#include "common/stringutils.h" #include "graphics/engine/lightning.h" #include "graphics/engine/terrain.h" @@ -34,8 +37,11 @@ #include "ui/controls/button.h" #include "ui/controls/check.h" #include "ui/controls/interface.h" +#include "ui/controls/label.h" #include "ui/controls/window.h" +#include + namespace Ui { @@ -55,10 +61,17 @@ CDebugMenu::~CDebugMenu() void CDebugMenu::ToggleInterface() { - if (m_interface->SearchControl(EVENT_WINDOW7) == nullptr) + if (!IsActive()) + { CreateInterface(); + CLabel* pl = m_interface->CreateLabel(Math::Point(0.0f, 0.9f), Math::Point(1.0f, 0.1f), -1, EVENT_LABEL19, "??"); + pl->SetFontType(Gfx::FONT_COURIER); + } else + { + m_interface->DeleteControl(EVENT_LABEL19); DestroyInterface(); + } } const Math::Point dim = Math::Point(33.0f/640.0f, 33.0f/480.0f); @@ -359,6 +372,18 @@ bool CDebugMenu::EventProcess(const Event &event) } break; + case EVENT_FRAME: + HandleFrameUpdate(event); + break; + + case EVENT_KEY_DOWN: + if (event.GetData()->key == KEY(c) && (event.kmodState & KMOD_CTRL) != 0) + { + if (IsActive()) + { + return !HandleCopy(event.mousePos); + } + } default: break; @@ -427,4 +452,38 @@ bool CDebugMenu::HandleTeleport(Math::Point mousePos) return true; } +void CDebugMenu::HandleFrameUpdate(const Event &event) +{ + std::string str = "-"; + Math::Vector pos; + int obj; + if ((obj = m_engine->DetectObject(event.mousePos, pos, true)) != -1) + str = StrUtils::Format("pos=% 3.2f; % 3.2f height=% 3.2f objId=% 4d", pos.x, pos.z, pos.y, obj); + + CLabel* pl = static_cast(m_interface->SearchControl(EVENT_LABEL19)); + if (pl == nullptr) return; + pl->SetName(str.c_str()); +} + +bool CDebugMenu::HandleCopy(Math::Point mousePos) +{ + Math::Vector pos; + if (m_engine->DetectObject(mousePos, pos, true) == -1) + { + m_sound->Play(SOUND_CLICK, 1.0f, 0.5f); + return false; + } + + std::string str = StrUtils::Format("pos=%.2f;%.2f", pos.x, pos.z); + + GetLogger()->Debug("%s\n", str.c_str()); + SDL_SetClipboardText(str.c_str()); + return true; +} + +bool CDebugMenu::IsActive() +{ + return m_interface->SearchControl(EVENT_WINDOW7) != nullptr; +} + } diff --git a/src/ui/debug_menu.h b/src/ui/debug_menu.h index a45623ac..67e183fd 100644 --- a/src/ui/debug_menu.h +++ b/src/ui/debug_menu.h @@ -37,24 +37,55 @@ namespace Ui { class CInterface; +/** + * \class CDebugMenu + * \brief Handles debug menu (F11) + * + * There should always be only one instance of this class for each associated CRobotMain class. + */ class CDebugMenu { public: + //! Creates the CDebugMenu instance CDebugMenu(CRobotMain* main, Gfx::CEngine* engine, CObjectManager* objMan, CSoundInterface* sound); + //! Destroys the CDebugMenu instance + //! \note Does not clean up the interface, should be called only when CRobotMain is destroyed virtual ~CDebugMenu(); + //! Toggle the debug interface void ToggleInterface(); + //! Check if the debug interface is open + bool IsActive(); + + //! Event processing bool EventProcess(const Event& event); protected: + //! Create the main page of debug interface void CreateInterface(); + //! Create the spawn object interface void CreateSpawnInterface(); + //! Update controls in the debug interface void UpdateInterface(); + //! Destroy the debug interface window void DestroyInterface(); + //! Handle frame update + //! This is used to update the cursor coordinates overlay + void HandleFrameUpdate(const Event &event); + + //! Handle spawning a new object at mouse position + //! \return true on success, false on error bool HandleSpawnObject(ObjectType type, Math::Point mousePos); + //! Handle lightning at position + //! \return true on success, false on error bool HandleLightning(Math::Point mousePos); + //! Handle teleport to position + //! \return true on success, false on error bool HandleTeleport(Math::Point mousePos); + //! Handle ctrl+c (copy coordinates under cursor to clipboard) + //! \return true on success, false on error + bool HandleCopy(Math::Point mousePos); protected: CRobotMain* m_main; diff --git a/src/ui/mainshort.cpp b/src/ui/mainshort.cpp index 0ec5f8a5..d9fffa26 100644 --- a/src/ui/mainshort.cpp +++ b/src/ui/mainshort.cpp @@ -280,9 +280,9 @@ void CMainShort::SelectNext() CObject* pPrev = m_main->DeselectAll(); - if(m_shortcuts.size() == 0) + if (m_shortcuts.size() == 0) { - m_main->SelectHuman(); + m_main->SelectObject(m_main->SearchHuman()); return; } diff --git a/src/ui/object_interface.cpp b/src/ui/object_interface.cpp index a435c825..c819540a 100644 --- a/src/ui/object_interface.cpp +++ b/src/ui/object_interface.cpp @@ -1484,7 +1484,25 @@ void CObjectInterface::UpdateInterface(float rTime) if ( pg != nullptr ) { assert(m_object->Implements(ObjectInterfaceType::Shielded)); - pg->SetLevel(dynamic_cast(m_object)->GetShield()); + icon = 3; // orange/gray + float shield = dynamic_cast(m_object)->GetShield(); + + if ( shield < 0.4f && shield != 0.0f) // low but not zero? + { + if ( m_lastAlarmTime >= 0.8f ) // blinks? + { + shield = 1.0f; + icon = 2; // red + } + if ( m_lastAlarmTime >= 1.0f ) + { + m_sound->Play(SOUND_ALARMs, 0.5f); // beep-beep + m_lastAlarmTime = 0.0f; + } + } + + pg->SetLevel(shield); + pg->SetIcon(icon); } pg = static_cast< CGauge* >(pw->SearchControl(EVENT_OBJECT_GRANGE)); @@ -1494,7 +1512,7 @@ void CObjectInterface::UpdateInterface(float rTime) icon = 2; // blue/red range = dynamic_cast(m_object)->GetReactorRange(); - if ( range < 0.2f && range != 0.0f && !m_physics->GetLand() ) + if ( range < 0.2f && range != 0.0f && !m_physics->GetLand() ) // low but not zero/landed? { if ( Math::Mod(m_time, 0.5f) >= 0.2f ) // blinks? { diff --git a/src/ui/screen/screen_apperance.cpp b/src/ui/screen/screen_apperance.cpp index 3e8cdd0d..7ca200f1 100644 --- a/src/ui/screen/screen_apperance.cpp +++ b/src/ui/screen/screen_apperance.cpp @@ -28,6 +28,8 @@ #include "level/player_profile.h" #include "level/robotmain.h" +#include "math/geometry.h" + #include "ui/controls/button.h" #include "ui/controls/color.h" #include "ui/controls/interface.h" @@ -331,6 +333,10 @@ bool CScreenApperance::EventProcess(const Event &event) break; } + case EVENT_UPDINTERFACE: + CameraPerso(); + break; + case EVENT_INTERFACE_PHEAD: m_apperanceTab = 0; UpdatePerso(); @@ -617,18 +623,13 @@ void CScreenApperance::UpdatePerso() void CScreenApperance::CameraPerso() { - Gfx::CCamera* camera = m_main->GetCamera(); - - camera->SetType(Gfx::CAM_TYPE_SCRIPT); if ( m_apperanceTab == 0 ) { - camera->SetScriptCamera(Math::Vector(6.0f, 0.0f, 0.0f), - Math::Vector(0.0f, 0.2f, 1.5f)); + SetCamera(0.325f, -0.15f, 5.0f); } else { - camera->SetScriptCamera(Math::Vector(18.0f, 0.0f, 4.5f), - Math::Vector(0.0f, 1.6f, 4.5f)); + SetCamera(0.325f, 0.3f, 18.0f); } } @@ -696,4 +697,34 @@ void CScreenApperance::ColorPerso() else apperance.colorBand = color; } +void CScreenApperance::SetCamera(float x, float y, float cameraDistance) +{ + Gfx::CCamera* camera = m_main->GetCamera(); + Gfx::CEngine* engine = Gfx::CEngine::GetInstancePointer(); + + camera->SetType(Gfx::CAM_TYPE_SCRIPT); + + Math::Vector p2D(x, y, cameraDistance); + Math::Vector p3D; + Math::Matrix matView; + Math::Matrix matProj = engine->GetMatProj(); + + Math::LoadViewMatrix(matView, Math::Vector(0.0f, 0.0f, -cameraDistance), + Math::Vector(0.0f, 0.0f, 0.0f), + Math::Vector(0.0f, 0.0f, 1.0f)); + + p2D.x = p2D.x * 2.0f - 1.0f; // [0..1] -> [-1..1] + p2D.y = p2D.y * 2.0f - 1.0f; + + p3D.x = p2D.x * p2D.z / matProj.Get(1,1); + p3D.y = p2D.y * p2D.z / matProj.Get(2,2); + p3D.z = p2D.z; + + p3D = Math::Transform(matView.Inverse(), p3D); + p3D = -p3D; + + camera->SetScriptCamera(Math::Vector(cameraDistance, p3D.y, p3D.x), + Math::Vector(0.0f, p3D.y, p3D.x)); +} + } // namespace Ui diff --git a/src/ui/screen/screen_apperance.h b/src/ui/screen/screen_apperance.h index 1aa6c8ac..b5faf08d 100644 --- a/src/ui/screen/screen_apperance.h +++ b/src/ui/screen/screen_apperance.h @@ -40,6 +40,9 @@ protected: void FixPerso(int rank, int index); void ColorPerso(); + //! Move camera in that way that astronaut's origin is in (x, y) point on window + void SetCamera(float x, float y, float cameraDistance); + protected: int m_apperanceTab; // perso: tab selected float m_apperanceAngle; // perso: angle of presentation diff --git a/src/ui/screen/screen_player_select.cpp b/src/ui/screen/screen_player_select.cpp index 4bcd139b..e3a9c2f3 100644 --- a/src/ui/screen/screen_player_select.cpp +++ b/src/ui/screen/screen_player_select.cpp @@ -40,6 +40,8 @@ #include "ui/controls/list.h" #include "ui/controls/window.h" +#include + namespace Ui { @@ -245,6 +247,7 @@ void CScreenPlayerSelect::UpdateNameControl() total = pl->GetTotal(); sel = pl->GetSelect(); name = pe->GetText(100); + boost::trim(name); pb = static_cast(pw->SearchControl(EVENT_INTERFACE_NDELETE)); if ( pb != nullptr ) @@ -379,6 +382,7 @@ bool CScreenPlayerSelect::NameCreate() std::string name; name = pe->GetText(100); + boost::trim(name); if ( name.empty() ) { m_sound->Play(SOUND_TZOING); diff --git a/test/unit/CBot/CBotToken_test.cpp b/test/unit/CBot/CBotToken_test.cpp index b51ca93e..a36841a5 100644 --- a/test/unit/CBot/CBotToken_test.cpp +++ b/test/unit/CBot/CBotToken_test.cpp @@ -60,7 +60,7 @@ protected: ASSERT_EQ(token->GetType(), correct.type) << "type mismatch at token #" << (i+1); i++; } - while((token = token->GetNext()) != nullptr); + while((token = token->GetNext()) != nullptr && !IsOfType(token, TokenTypNone)); ASSERT_EQ(i, data.size()) << "not enough tokens processed"; } }; diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 797a674e..9e264e1e 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -216,15 +216,15 @@ protected: if (error != CBotNoErr) { ADD_FAILURE() << "Compile error - " << error << " (" << cursor1 << "-" << (cursor2 >= 0 ? cursor2 : cursor1) << ")" << std::endl << GetFormattedLineInfo(code, cursor1); // TODO: Error messages are on Colobot side - return std::move(program); + return program; } else { ADD_FAILURE() << "No compile error, expected " << expectedCompileError; // TODO: Error messages are on Colobot side - return std::move(program); + return program; } } - if (expectedCompileError != CBotNoErr) return std::move(program); + if (expectedCompileError != CBotNoErr) return program; for (const std::string& test : tests) { @@ -285,7 +285,7 @@ protected: ADD_FAILURE() << ss.str(); } } - return std::move(program); // Take it if you want, destroy on exit otherwise + return program; // Take it if you want, destroy on exit otherwise } }; @@ -298,6 +298,96 @@ TEST_F(CBotUT, EmptyTest) ); } +TEST_F(CBotUT, FunctionCompileErrors) +{ + ExecuteTest( + "public", + CBotErrNoType + ); + + ExecuteTest( + "extern", + CBotErrNoType + ); + + ExecuteTest( + "public void", + CBotErrNoFunc + ); + + ExecuteTest( + "extern void", + CBotErrNoFunc + ); + + ExecuteTest( + "extern void MissingParameterType(", + CBotErrNoType + ); + + ExecuteTest( + "extern void MissingParamName(int", + CBotErrNoVar + ); + + ExecuteTest( + "extern void MissingCloseParen(int i", + CBotErrClosePar + ); + + ExecuteTest( + "extern void ParamTrailingComma(int i, ) {\n" + "}\n", + CBotErrNoType + ); + + ExecuteTest( + "extern void MissingOpenBlock(int i)", + CBotErrOpenBlock + ); + + ExecuteTest( + "extern void MissingCloseBlock()\n" + "{\n", + CBotErrCloseBlock + ); + +} + +TEST_F(CBotUT, ClassCompileErrors) +{ + ExecuteTest( + "public class", + CBotErrNoClassName + ); + + ExecuteTest( + "public class 1234", + CBotErrNoClassName + ); + + ExecuteTest( + "public class TestClass", + CBotErrOpenBlock + ); + + ExecuteTest( + "public class TestClass\n" + "{\n", + CBotErrCloseBlock + ); + + ExecuteTest( + "public class TestClass extends", + CBotErrNoClassName + ); + + ExecuteTest( + "public class TestClass extends 1234", + CBotErrNoClassName + ); +} + TEST_F(CBotUT, DivideByZero) { ExecuteTest( @@ -711,8 +801,7 @@ TEST_F(CBotUT, FunctionBadReturn) ); } -// TODO: Doesn't work -TEST_F(CBotUT, DISABLED_FunctionNoReturn) +TEST_F(CBotUT, FunctionNoReturn) { ExecuteTest( "int func()\n" @@ -722,7 +811,49 @@ TEST_F(CBotUT, DISABLED_FunctionNoReturn) "{\n" " func();\n" "}\n", - static_cast(-1) // TODO: no error for that + CBotErrNoReturn + ); + + ExecuteTest( + "int FuncDoesNotReturnAValue()\n" + "{\n" + " if (false) return 1;\n" + " while (false) return 1;\n" + " if (true) ; else return 1;\n" + " do { break; return 1; } while (false);\n" + " do { continue; return 1; } while (false);\n" + "}\n", + CBotErrNoReturn + ); + + ExecuteTest( + "int FuncHasReturn()\n" + "{\n" + " return 1;\n" + "}\n" + "int BlockHasReturn()\n" + "{\n" + " {\n" + " {\n" + " }\n" + " return 2;\n" + " }\n" + "}\n" + "int IfElseHasReturn()\n" + "{\n" + " if (false) {\n" + " return 3;\n" + " } else {\n" + " if (false) return 3;\n" + " else return 3;\n" + " }\n" + "}\n" + "extern void Test()\n" + "{\n" + " ASSERT(1 == FuncHasReturn());\n" + " ASSERT(2 == BlockHasReturn());\n" + " ASSERT(3 == IfElseHasReturn());\n" + "}\n" ); } @@ -867,14 +998,13 @@ TEST_F(CBotUT, ClassNullPointer) ); } -// TODO: This doesn't work -TEST_F(CBotUT, DISABLED_ClassDestructorNaming) +TEST_F(CBotUT, ClassDestructorNaming) { ExecuteTest( "public class TestClass {\n" " public void ~SomethingElse() {}\n" "}\n", - static_cast(-1) // TODO: no error for that + CBotErrNoFunc ); ExecuteTest( "public class SomethingElse {\n" @@ -882,7 +1012,26 @@ TEST_F(CBotUT, DISABLED_ClassDestructorNaming) "public class TestClass2 {\n" " public void ~SomethingElse() {}\n" "}\n", - static_cast(-1) // TODO: no error for that + CBotErrNoFunc + ); +} + +TEST_F(CBotUT, ClassDestructorSyntax) +{ + ExecuteTest( + "public class TestClass {\n" + " void ~TestClass(int i) {}\n" + "}\n" + "extern void DestructorNoParams() {}\n", + CBotErrClosePar + ); + + ExecuteTest( + "public class TestClass {\n" + " int ~TestClass() {}\n" + "}\n" + "extern void DestructorReturnTypeVoid() {}\n", + CBotErrFuncNotVoid ); } @@ -930,10 +1079,54 @@ TEST_F(CBotUT, ClassMethodRedefined) "}\n", CBotErrRedefFunc ); + + ExecuteTest( + "public class TestClass {\n" + " void ~TestClass() {}\n" + " void ~TestClass() {}\n" + "}\n", + CBotErrRedefFunc + ); } -// TODO: Not only doesn't work but segfaults -TEST_F(CBotUT, DISABLED_ClassRedefined) +TEST_F(CBotUT, ClassFieldNaming) +{ + ExecuteTest( + "public class TestClass {\n" + " int ~i = 1;\n" + "}\n", + CBotErrNoVar + ); + + ExecuteTest( + "public class TestClass {\n" + " int TestClass = 1;\n" + "}\n", + CBotErrNoVar + ); + + ExecuteTest( + "public class TestClass {\n" + " int i = 1;\n" + " int i = 2;\n" + "}\n", + CBotErrRedefVar + ); +} + +TEST_F(CBotUT, ClassRedefinedInDifferentPrograms) +{ + auto publicProgram = ExecuteTest( + "public class TestClass {}\n" + ); + + ExecuteTest( + "public class TestClass {}\n", + CBotErrRedefClass + ); +} + +TEST_F(CBotUT, ClassRedefinedInOneProgram) { ExecuteTest( "public class TestClass {}\n" @@ -942,6 +1135,15 @@ TEST_F(CBotUT, DISABLED_ClassRedefined) ); } +TEST_F(CBotUT, ClassMissingCloseBlock) +{ + ExecuteTest( + "public class Something\n" + "{\n", + CBotErrCloseBlock + ); +} + // TODO: NOOOOOO!!! Nononononono :/ TEST_F(CBotUT, DISABLED_PublicClasses) { @@ -1012,7 +1214,7 @@ TEST_F(CBotUT, ClassInheritanceAssignment) "public class BaseClass {}\n" "public class MidClass extends BaseClass {}\n" "public class SubClass extends MidClass {}\n" - "extern void ClassInheritanceVars()\n" + "extern void ClassInheritanceAssignment()\n" "{\n" " BaseClass bc = new MidClass();\n" " MidClass mc = bc;\n" @@ -1392,11 +1594,12 @@ TEST_F(CBotUT, StringFunctions) ); } -TEST_F(CBotUT, DISABLED_TestNANParam_Issue642) +TEST_F(CBotUT, TestNANParam_Issue642) { ExecuteTest( "float test(float x) {\n" - " return x;\n" + " ASSERT(x == nan);\n" + " return x;\n" "}\n" "extern void TestNANParam() {\n" " ASSERT(nan == nan);\n" // TODO: Shouldn't it be nan != nan ?? @@ -2053,3 +2256,149 @@ TEST_F(CBotUT, ClassTestPrivateMember) CBotErrPrivate ); } + +TEST_F(CBotUT, IncrementDecrementSyntax) +{ + auto publicProgram = ExecuteTest( + "public class TestClass {\n" + " int GetInt() { return 1; }\n" + "}\n" + "extern void TestIncrementDecrement()\n" + "{\n" + " int i = 1;\n" + " ASSERT(2 == ++i);\n" + " ASSERT(2 == i++);\n" + " ASSERT(3 == i );\n" + " ASSERT(2 == --i);\n" + " ASSERT(2 == i--);\n" + " ASSERT(1 == i );\n" + "}\n" + ); + + ExecuteTest( + "extern void PreIncrementMethodCall()\n" + "{\n" + " TestClass tc();\n" + " ++tc.GetInt();\n" + "}\n", + CBotErrBadType1 + ); + + ExecuteTest( + "extern void PostIncrementMethodCall()\n" + "{\n" + " TestClass tc();\n" + " tc.GetInt()++;\n" + "}\n", + CBotErrBadType1 + ); + + ExecuteTest( + "extern void BadPreIncrementEmpty()\n" + "{\n" + " ++;\n" + "}\n", + CBotErrBadType1 + ); + + ExecuteTest( + "extern void BadPreIncrementNotAVar()\n" + "{\n" + " ++123;\n" + "}\n", + CBotErrBadType1 + ); +} + +TEST_F(CBotUT, ParametersWithDefaultValues) +{ + ExecuteTest( + "extern void ParametersWithDefaultValues() {\n" + " ASSERT(true == Test());\n" + " ASSERT(true == Test(1));\n" + " ASSERT(true == Test(1, 2));\n" + "}\n" + "bool Test(int i = 1, float f = 2.0) {\n" + " return (i == 1) && (f == 2.0);\n" + "}\n" + ); + + ExecuteTest( + "extern void NotUsingDefaultValues() {\n" + " ASSERT(true == Test(2, 4.0));\n" + "}\n" + "bool Test(int i = 1, float f = 2.0) {\n" + " return (i == 2) && (f == 4.0);\n" + "}\n" + ); + + ExecuteTest( + "extern void NextParamNeedsDefaultValue() {\n" + "}\n" + "void Test(int i = 1, float f) {}\n" + "\n", + CBotErrDefaultValue + ); + + ExecuteTest( + "extern void ParamMissingExpression() {\n" + "}\n" + "void Test(int i = 1, float f = ) {}\n" + "\n", + CBotErrNoExpression + ); + + ExecuteTest( + "extern void ParamDefaultBadType() {\n" + "}\n" + "void Test(int i = 1, float f = null) {}\n" + "\n", + CBotErrBadType1 + ); + + ExecuteTest( + "extern void DefaultValuesAmbiguousCall() {\n" + " Test();\n" + "}\n" + "void Test(int i = 1) {}\n" + "void Test(float f = 2.0) {}\n" + "\n", + CBotErrAmbiguousCall + ); + + ExecuteTest( + "extern void AmbiguousCallOneDefault() {\n" + " Test(1);\n" + "}\n" + "void Test(int i, float f = 2) {}\n" + "void Test(int i) {}\n" + "\n", + CBotErrAmbiguousCall + ); + + ExecuteTest( + "extern void DifferentNumberOfDefaultValues() {\n" + " Test(1, 2.0);\n" + "}\n" + "void Test(int i, float f = 2.0) {}\n" + "void Test(int i, float f = 2.0, int ii = 1) {}\n" + "\n", + CBotErrAmbiguousCall + ); + + ExecuteTest( + "extern void DefaultValueUnaryExpression() {\n" + " TestNumbers();\n" + " TestBool();\n" + "}\n" + "void TestNumbers(int i = -1, float f = -1, int ii = ~1) {\n" + " ASSERT(i == -1);\n" + " ASSERT(f == -1.0);\n" + " ASSERT(ii == ~1);\n" + "}\n" + "void TestBool(bool b = !false, bool b2 = not false) {\n" + " ASSERT(true == b);\n" + " ASSERT(true == b2);\n" + "}\n" + ); +}