diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f97d9b60..cfed63b1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: target_os: [linux] - host_os: [ubuntu-16.04, ubuntu-18.04, ubuntu-20.04] + host_os: [ubuntu-18.04, ubuntu-20.04] container: [''] include: - target_os: windows diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 1fbf323e..00000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,207 +0,0 @@ -name: Linter - -on: [push, pull_request] - -jobs: - lint: - # it's easiest if it matches the version that was used to build colobot-lint, newer versions don't have llvm-3.6-dev in repo... - runs-on: ubuntu-16.04 - env: - CC: /usr/lib/llvm-3.6/bin/clang - CXX: /usr/lib/llvm-3.6/bin/clang++ - CLANG_PREFIX: /usr/lib/llvm-3.6 - steps: - - name: Download Colobot dependencies - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends build-essential cmake libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsndfile1-dev libvorbis-dev libogg-dev libpng-dev libglew-dev libopenal-dev libboost-dev libboost-system-dev libboost-filesystem-dev libboost-regex-dev libphysfs-dev gettext git po4a vorbis-tools librsvg2-bin xmlstarlet - # TODO: migrate colobot-lint to GitHub Actions - - name: Download colobot-lint - run: | - sudo apt-get install -y --no-install-recommends clang-3.6 libtinyxml2.6.2v5 - mkdir -p /tmp/colobot-lint - cd /tmp/colobot-lint - wget -O colobot-lint.zip "https://compiled.colobot.info/job/colobot/job/colobot-lint/job/dev/lastSuccessfulBuild/artifact/*zip*/archive.zip" - - # Unzip the archive - unzip ./colobot-lint.zip - # Workaround for Clang not finding system headers - mkdir ./bin - mv ./archive/build/colobot-lint ./bin/ - chmod +x ./bin/colobot-lint - ln -s ${CLANG_PREFIX}/lib ./lib - # Unpack HtmlReport - tar -zxf ./archive/build/html_report.tar.gz - # Clean up - rm -r ./archive - - uses: actions/checkout@v2 - - name: Checkout the Google Test submodule - run: git submodule update --init -- lib/googletest - - name: Create build directory - run: cmake -E make_directory build - - name: Run CMake - working-directory: build - run: cmake -DCOLOBOT_LINT_BUILD=1 -DTESTS=1 -DTOOLS=1 -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .. - - name: Run linter - shell: bash - run: | - set -e +x - WORKSPACE="$GITHUB_WORKSPACE" - COLOBOT_DIR="$WORKSPACE" - COLOBOT_BUILD_DIR="$WORKSPACE/build" - COLOBOT_LINT_REPORT_FILE="$WORKSPACE/build/colobot_lint_report.xml" - - cd "/tmp/colobot-lint" - find "$WORKSPACE" \( -wholename "$COLOBOT_DIR/src/*.cpp" \ - -or -wholename "$COLOBOT_DIR/test/unit/*.cpp" \ - -or -wholename "$COLOBOT_BUILD_DIR/fake_header_sources/src/*.cpp" \ - -or -wholename "$COLOBOT_BUILD_DIR/fake_header_sources/test/unit/*.cpp" \) \ - -exec ./bin/colobot-lint \ - -verbose \ - -output-format xml \ - -output-file "$COLOBOT_LINT_REPORT_FILE" \ - -p "$COLOBOT_BUILD_DIR" \ - -project-local-include-path "$COLOBOT_DIR/src" -project-local-include-path "$COLOBOT_BUILD_DIR/src" \ - -license-template-file "$COLOBOT_DIR/LICENSE-HEADER.txt" \ - {} + - - name: Upload results (XML) - uses: actions/upload-artifact@v2 - with: - name: XML results - path: build/colobot_lint_report.xml - - name: Generate HTML report - shell: bash - run: /tmp/colobot-lint/HtmlReport/generate.py --xml-report "build/colobot_lint_report.xml" --output-dir "build/html_report" - - name: Upload results (HTML) - uses: actions/upload-artifact@v2 - with: - name: HTML results - path: build/html_report - - run: pip install requests - - name: Send linter results to GitHub - shell: python - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - import os - import sys - import requests - import xml.etree.ElementTree as ET - - OVERALL_STABLE_RULES=[ - "class naming", - "code block placement", - "compile error", - # "compile warning", - # "enum naming", - # "function naming", - "header file not self-contained", - # "implicit bool cast", - # "include style", - # "inconsistent declaration parameter name", - "license header", - # "naked delete", - # "naked new", - # "old style function", - "old-style null pointer", - # "possible forward declaration", - "undefined function", - # "uninitialized field", - # "uninitialized local variable", - # "unused forward declaration", - # "variable naming", - "whitespace", - ] - - STABLE_RULES_WITHOUT_CBOT=[ - "class naming", - "code block placement", - "compile error", - "compile warning", - # "enum naming", - # "function naming", - "header file not self-contained", - # "implicit bool cast", - "include style", - "inconsistent declaration parameter name", - "license header", - "naked delete", - "naked new", - # "old style function", - "old-style null pointer", - # "possible forward declaration", - "undefined function", - "uninitialized field", - # "uninitialized local variable", - "unused forward declaration", - # "variable naming", - "whitespace", - ] - - # None of the available actions seem to do what I want, they all do stupid things like adding another check... let's just do it manually - # GitHub also doesn't seem to provide you with the check suite or check run ID, so we have to get it from the action ID via the API - s = requests.Session() - s.headers.update({ - 'Authorization': 'token ' + os.environ['GITHUB_TOKEN'], - 'Accept': 'application/vnd.github.antiope-preview+json' # Annotations are still technically a preview feature of the API - }) - action_run = s.get(os.environ['GITHUB_API_URL'] + "/repos/" + os.environ['GITHUB_REPOSITORY'] + "/actions/runs/" + os.environ['GITHUB_RUN_ID']).json() - check_suite = s.get(action_run['check_suite_url']).json() - check_suite_runs = s.get(check_suite['check_runs_url']).json() - check_run = check_suite_runs['check_runs'][0] # NOTE: This assumes that the 'lint' job is the first one in the workflow. You could find it by name if you really wanted. - - def we_care_about(file_name, type): - if 'CBot' in file_name: - return type in OVERALL_STABLE_RULES - else: - return type in STABLE_RULES_WITHOUT_CBOT - - results = ET.parse('build/colobot_lint_report.xml') - annotations = [] - for error in results.find('errors').findall('error'): - location = error.find('location') - file_name = os.path.relpath(location.get('file'), os.environ['GITHUB_WORKSPACE']) - line_num = int(location.get('line')) - type = error.get('id') - severity = error.get('severity') - msg = error.get('msg') - - gh_severity = 'warning' - if severity == 'error': - gh_severity = 'failure' - elif severity == 'information': - gh_severity = 'notice' - - if not we_care_about(file_name, type): - # don't send the unstable rules to github at all as there are way too many of them and it would overload the API rate limit - continue - - print('{}:{}: [{}] {}'.format(file_name, line_num, type, msg)) - - annotations.append({ - 'path': file_name, - 'start_line': line_num, - 'end_line': line_num, - 'annotation_level': gh_severity, - 'title': type, - 'message': msg - }) - - summary = 'colobot-lint found {} issues'.format(len(annotations)) - all_ok = len(annotations) == 0 - - # Annotations have to be sent in batches of 50 - first = True - while first or len(annotations) > 0: - first = False - to_send = annotations[:50] - annotations = annotations[50:] - data = { - 'output': { - 'title': summary, - 'summary': summary, - 'annotations': to_send - } - } - r = s.patch(check_run['url'], json=data) - r.raise_for_status() - - sys.exit(0 if all_ok else 1) diff --git a/.github/workflows/verify-pr-target.yml b/.github/workflows/verify-pr-target.yml index 09cadb19..c5f388d7 100644 --- a/.github/workflows/verify-pr-target.yml +++ b/.github/workflows/verify-pr-target.yml @@ -1,14 +1,21 @@ name: Verify pull request target -on: [pull_request] +on: [pull_request_target] jobs: check_pr_target: runs-on: ubuntu-latest steps: - - name: Wrong pull request target - run: echo "This pull request targets the master branch. Please edit the pull request to target dev." && exit 1 + - name: Send comment if wrong pull request target if: github.base_ref == 'master' + uses: peter-evans/create-or-update-comment@v1 + with: + issue-number: ${{ github.event.number }} + body: | + Hey! This pull request targets the `master` branch. You should probably target `dev` instead. Make sure to read the [contributing guidelines](https://github.com/colobot/colobot/blob/master/CONTRIBUTING.md#submitting-pull-requests) and [edit the target branch if necessary](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-base-branch-of-a-pull-request). + - name: Wrong pull request target + if: github.base_ref == 'master' + run: echo "This pull request targets the master branch. Please edit the pull request to target dev." && exit 1 - name: Correct pull request target + if: github.base_ref != 'master' run: echo "This pull request targets the correct branch." && exit 0 - if: github.base_ref != 'master' \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ef580ba..1c367b7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,8 @@ set(CMAKE_CXX_EXTENSIONS NO) ## set(COLOBOT_VERSION_CODENAME "Gold") set(COLOBOT_VERSION_MAJOR 0) -set(COLOBOT_VERSION_MINOR 1) -set(COLOBOT_VERSION_REVISION 12) +set(COLOBOT_VERSION_MINOR 2) +set(COLOBOT_VERSION_REVISION 0) # Used on official releases #set(COLOBOT_VERSION_RELEASE_CODENAME "-alpha") diff --git a/INSTALL-MSYS2.md b/INSTALL-MSYS2.md index f35634f3..bc5f90d1 100644 --- a/INSTALL-MSYS2.md +++ b/INSTALL-MSYS2.md @@ -110,7 +110,7 @@ Easy, isn't it? If you are lazy, you can just use this one-line command, although there is no guarantee it will work or install everything (might be out of date): ```sh -pacman -S mingw-w64-i686-boost mingw-w64-i686-glew mingw-w64-i686-libpng gettext mingw-w64-i686-gettext mingw-w64-i686-libpng mingw-w64-i686-libsndfile mingw-w64-i686-libvorbis mingw-w64-i686-libogg mingw-w64-i686-openal mingw-w64-i686-SDL2 mingw-w64-i686-SDL2_image mingw-w64-i686-SDL2_ttf +pacman -S mingw-w64-i686-boost mingw-w64-i686-glew mingw-w64-i686-libpng gettext mingw-w64-i686-gettext mingw-w64-i686-libpng mingw-w64-i686-libsndfile mingw-w64-i686-libvorbis mingw-w64-i686-libogg mingw-w64-i686-openal mingw-w64_i686-physfs mingw-w64-i686-SDL2 mingw-w64-i686-SDL2_image mingw-w64-i686-SDL2_ttf ``` You should now have everything set up and working. You can close all instances of MSYS2 and autorebase to ensure everything installed properly. diff --git a/README.md b/README.md index 353dbbf5..4bb5e62a 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ On some Linux distributions there are also distribution packages available: * Arch Linux (AUR): https://aur.archlinux.org/packages/colobot-gold * openSUSE: http://software.opensuse.org/download.html?project=games&package=colobot * Fedora: https://src.fedoraproject.org/rpms/colobot + * Guix https://guix.gnu.org/en/packages/colobot-0.1.12-alpha/ ## Compiling and running the game @@ -43,9 +44,4 @@ If you want to contribute to the project, see [CONTRIBUTING.md](CONTRIBUTING.md) ## Contact -If you want to help in the project, please contact us on our IRC channels or [our forum](http://colobot.info/forum/). - -### IRC channels - -* [#colobot on Freenode](irc://freenode.net#colobot) - main development channel (English); -* [#colobot on pirc.pl](irc://pirc.pl#colobot) - Polish community channel; +If you want to help in the project, please contact us on our [Discord server](https://discord.gg/56Fm9kb). diff --git a/data b/data index 611cbfdd..0ac8197b 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 611cbfdd079e97a71f97810636f2ab2358cb4eeb +Subproject commit 0ac8197b7a8a005c714b7696d36c642cf0e81474 diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt index cb7056f2..6f165f06 100644 --- a/po/CMakeLists.txt +++ b/po/CMakeLists.txt @@ -12,7 +12,7 @@ add_custom_command(OUTPUT ${_potFile} COMMAND ${XGETTEXT_CMD} ${colobot_SOURCE_DIR}/src/app/app.cpp --output=${_potFile} --no-wrap COMMAND ${XGETTEXT_CMD} ${colobot_SOURCE_DIR}/src/common/restext.cpp --output=${_potFile} --no-wrap --join-existing --no-location --keyword=TR COMMAND ${XGETTEXT_CMD} ${colobot_SOURCE_DIR}/src/script/script.cpp --output=${_potFile} --no-wrap --join-existing --no-location - COMMAND sed -i -e "s|^\\(\"POT-Creation-Date:\\).*$|\\1 DATE\\\\n\"|" ${_potFile} + COMMAND sed -bi -e "s/^\\(\"POT-Creation-Date:\\).*$/\\1 DATE\\\\n\"/" ${_potFile} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Extract translatable messages to ${_potFile}" diff --git a/po/colobot.pot b/po/colobot.pot index 5614aaac..442eafd6 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -84,6 +84,12 @@ msgstr "" msgid "Load a saved mission" msgstr "" +msgid "Missions+" +msgstr "" + +msgid "Mods" +msgstr "" + msgid "Chapters:" msgstr "" @@ -166,6 +172,23 @@ msgstr "" msgid "This menu is for userlevels from mods, but you didn't install any" msgstr "" +msgid "Could not open the file explorer!" +msgstr "" + +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "Keyword help(\\key cbot;)" msgstr "" @@ -280,6 +303,42 @@ msgstr "" msgid "%s: %d pts" msgstr "" +msgid "Mods:" +msgstr "" + +msgid "Information:" +msgstr "" + +msgid "Description:" +msgstr "" + +msgid "Enable\\Enable the selected mod" +msgstr "" + +msgid "Disable\\Disable the selected mod" +msgstr "" + +msgid "Unknown author" +msgstr "" + +msgid "by" +msgstr "" + +msgid "Version" +msgstr "" + +msgid "Website" +msgstr "" + +msgid "Changes" +msgstr "" + +msgid "No description." +msgstr "" + +msgid "No changes." +msgstr "" + msgid "Code battle" msgstr "" @@ -316,6 +375,9 @@ msgstr "" msgid "SatCom" msgstr "" +msgid "Mods\\Mod manager" +msgstr "" + msgid "Change player\\Change player" msgstr "" @@ -340,9 +402,30 @@ msgstr "" msgid "<< Back \\Back to the previous screen" msgstr "" +msgid "+\\Missions with bonus content and optional challenges" +msgstr "" + msgid "Play\\Start mission!" msgstr "" +msgid "Workshop\\Open the workshop to search for mods" +msgstr "" + +msgid "Open Directory\\Open the mods directory" +msgstr "" + +msgid "Apply\\Apply the current mod configuration" +msgstr "" + +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Device\\Driver and resolution settings" msgstr "" @@ -415,6 +498,9 @@ msgstr "" msgid "Pause in background\\Pause the game when the window is unfocused" msgstr "" +msgid "Mute sounds in background\\Mute all game sounds when the window is unfocused" +msgstr "" + msgid "Automatic indent\\When program editing" msgstr "" @@ -580,6 +666,15 @@ msgstr "" msgid "Invert\\Invert values on this axis" msgstr "" +msgid "Space Programmer\\Disables radio-control" +msgstr "" + +msgid "Space Researcher\\Disables using all previously researched technologies" +msgstr "" + +msgid "Space Explorer\\Disables astronaut abilities" +msgstr "" + msgid "\\New player name" msgstr "" @@ -751,7 +846,7 @@ msgstr "" msgid "Build a exchange post" msgstr "" -msgid "Build a destroyer" +msgid "Build a vault" msgstr "" msgid "Show if the ground is flat" diff --git a/po/cs.po b/po/cs.po index 9111811b..453ded95 100644 --- a/po/cs.po +++ b/po/cs.po @@ -32,6 +32,9 @@ msgstr "Chybí \"]\"" msgid "%s: %d pts" msgstr "%s: %d bodů" +msgid "+\\Missions with bonus content and optional challenges" +msgstr "" + msgid "..behind" msgstr "...za sebou" @@ -125,6 +128,9 @@ msgstr "Vzhled\\Upravte svůj vzhled" msgid "Apply changes\\Activates the changed settings" msgstr "Uložit změny\\Aktivovat změny nastavení" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Chybí vhodný konstruktor" @@ -197,9 +203,6 @@ msgstr "Postavit obrannou věž" msgid "Build a derrick" msgstr "Postavit vrtnou věž" -msgid "Build a destroyer" -msgstr "Postavit drtič" - msgid "Build a exchange post" msgstr "Postavit komunikační stanici" @@ -272,6 +275,9 @@ msgstr "Vyrobit pásový kanón" msgid "Build a tracked sniffer" msgstr "Vyrobit pásový detektor" +msgid "Build a vault" +msgstr "" + msgid "Build a wheeled builder" msgstr "" @@ -371,6 +377,9 @@ msgstr "Změnit kameru\\Přepíná mezi kamerou na robotu a za robotem" msgid "Change player\\Change player" msgstr "Změnit hráče\\Změnit hráče" +msgid "Changes" +msgstr "" + msgid "Chapters:" msgstr "Kapitoly:" @@ -440,6 +449,12 @@ msgstr "Kopírovat" msgid "Copy (Ctrl+C)" msgstr "Kopírovat (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Současná mise uložena" @@ -473,6 +488,9 @@ msgstr "Vrtná věž" msgid "Descend\\Reduces the power of the jet" msgstr "Klesat\\Snížit tah tryskového motoru" +msgid "Description:" +msgstr "" + msgid "Destroy" msgstr "Zbourat" @@ -485,6 +503,9 @@ msgstr "Drtič" msgid "Device\\Driver and resolution settings" msgstr "Obrazovka\\Nastavení grafické karty a rozlišení" +msgid "Disable\\Disable the selected mod" +msgstr "" + msgid "Dividing by zero" msgstr "Dělení nulou" @@ -501,6 +522,9 @@ msgstr "Dveře blokuje robot nebo jiný objekt" msgid "Down (\\key gdown;)" msgstr "Dolů (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Tužkobot" @@ -528,6 +552,9 @@ msgstr "Vejce" msgid "Empty character constant" msgstr "" +msgid "Enable\\Enable the selected mod" +msgstr "" + msgid "End of block missing" msgstr "Chybí konec bloku" @@ -754,6 +781,9 @@ msgstr "Infikováno virem; dočasně mimo provoz" msgid "Information exchange post" msgstr "Komunikační stanice" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Příkaz \"break\" mimo cyklus" @@ -901,9 +931,21 @@ msgstr "Mise" msgid "Missions on this planet:" msgstr "Mise na této planetě:" +msgid "Missions+" +msgstr "Mise+" + msgid "Missions\\Select mission" msgstr "Mise\\Vyberte misi" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Vodorovné převrácení posunu\\Při vodorovném posunu kamery myší pousouvat opačným směrem" @@ -916,6 +958,9 @@ msgstr "Posunout vybraný program níže" msgid "Move selected program up" msgstr "Posunout vybraný program výše" +msgid "Mute sounds in background\\Mute all game sounds when the window is unfocused" +msgstr "" + msgid "Mute\\No sound" msgstr "Ticho\\Bez zvuku" @@ -934,6 +979,9 @@ msgstr "Nový" msgid "New ..." msgstr "Nový..." +msgid "New Folder" +msgstr "" + msgid "New bot available" msgstr "Robot vyroben" @@ -946,6 +994,12 @@ msgstr "Další objekt\\Vybere následující objekt" msgid "No" msgstr "Ne" +msgid "No changes." +msgstr "" + +msgid "No description." +msgstr "" + msgid "No energy in the subsoil" msgstr "Pod povrchem není zdroj energie" @@ -1066,6 +1120,9 @@ msgstr "Otevřít" msgid "Open (Ctrl+O)" msgstr "Otevřít (Ctrl+O)" +msgid "Open Directory\\Open the mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Chybí levá složená závorka" @@ -1090,6 +1147,9 @@ msgstr "Zdroj poslední zprávy\\Zobrazí objekt, který poslal poslední zpráv msgid "Original game developed by:" msgstr "Vývojáři původní hry:" +msgid "Overwrite existing file?" +msgstr "" + msgid "Parameters missing" msgstr "Některé parametry nejsou vyplněné" @@ -1273,6 +1333,9 @@ msgstr "Červená vlajka" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Odlesky na tlačítkách\\Blyštivá tlačítka" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Pozůstatky mise Apollo" @@ -1387,6 +1450,9 @@ msgstr "Uložit\\Uložit současnou misi" msgid "Save\\Saves the current mission" msgstr "Uložit\\Uloží současnou misi" +msgid "Select Folder" +msgstr "" + msgid "Select the astronaut\\Selects the astronaut" msgstr "Vybrat kosmonauta\\Vybere kosmonauta" @@ -1453,6 +1519,15 @@ msgstr "Zvukové efekty:\\Hlasitost motorů, hlasů, střelby, atd." msgid "Sound\\Music and game sound volume" msgstr "Zvuk\\Hlasitost hudby a zvukových efektů" +msgid "Space Explorer\\Disables astronaut abilities" +msgstr "" + +msgid "Space Programmer\\Disables radio-control" +msgstr "" + +msgid "Space Researcher\\Disables using all previously researched technologies" +msgstr "" + msgid "Spaceship" msgstr "Raketa" @@ -1531,6 +1606,10 @@ msgstr "Filtrování textur\\Filtrování textur" msgid "Textures" msgstr "Textury" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "Souboj skončil" @@ -1543,9 +1622,16 @@ msgstr "Funkce nevrátila žádnou hodnotu" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "Mise ještě nebyla splněna (pro podrobnosti stiskněte \\key help;)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Operaci nelze provést s operandy těchto dvou typů" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Tato třída již existuje" @@ -1670,6 +1756,9 @@ msgstr "Jednotka" msgid "Unknown Object" msgstr "Neznámý objekt" +msgid "Unknown author" +msgstr "" + msgid "Unknown command" msgstr "Neznámý příkaz" @@ -1682,6 +1771,9 @@ msgstr "Neznámá funkce" msgid "Up (\\key gup;)" msgstr "Vzhůru (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Uranové ložisko (místo pro vrtnou věž)" @@ -1703,6 +1795,9 @@ msgstr "Proměnná nebyla nastavena" msgid "Vault" msgstr "Trezor" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "" @@ -1721,6 +1816,9 @@ msgstr "Vosa byla smrtelně raněna" msgid "Waste" msgstr "Odpad" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -1754,6 +1852,9 @@ msgstr "Létající detektor" msgid "Withdraw shield (\\key action;)" msgstr "Vypnout štít (\\key action;)" +msgid "Workshop\\Open the workshop to search for mods" +msgstr "" + msgid "Worm" msgstr "Červ" @@ -1904,8 +2005,14 @@ msgstr "\\Fialové vlajky" msgid "\\Yellow flags" msgstr "\\Žluté vlajky" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" msgid "epsitec.com" msgstr "epsitec.com" + +#~ msgid "Build a destroyer" +#~ msgstr "Postavit drtič" diff --git a/po/de.po b/po/de.po index d9c9b2e1..7bcffe5b 100644 --- a/po/de.po +++ b/po/de.po @@ -33,6 +33,9 @@ msgstr "Es fehlt eine geschlossene eckige Klammer \" ] \"" msgid "%s: %d pts" msgstr "" +msgid "+\\Missions with bonus content and optional challenges" +msgstr "" + msgid "..behind" msgstr "..hinten" @@ -126,6 +129,9 @@ msgstr "Aussehen\\Erscheinungsbild des Astronauten einstellen" msgid "Apply changes\\Activates the changed settings" msgstr "Änderungen anwenden\\Getätigte Einstellungen anwenden" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Es gibt keinen geeigneten Konstruktor" @@ -198,9 +204,6 @@ msgstr "Baut einen Geschützturm" msgid "Build a derrick" msgstr "Baut einen Bohrturm" -msgid "Build a destroyer" -msgstr "Baue einen Zerstörer" - msgid "Build a exchange post" msgstr "Baut einen Infoserver" @@ -273,6 +276,9 @@ msgstr "Baut einen Kettenshooter" msgid "Build a tracked sniffer" msgstr "Baut einen Kettenschnüffler" +msgid "Build a vault" +msgstr "" + msgid "Build a wheeled builder" msgstr "" @@ -372,6 +378,9 @@ msgstr "Andere Kamera\\Sichtpunkt einstellen" msgid "Change player\\Change player" msgstr "Anderer Spieler\\Spielername ändern" +msgid "Changes" +msgstr "" + msgid "Chapters:" msgstr "Liste der Kapitel:" @@ -441,6 +450,12 @@ msgstr "Kopieren" msgid "Copy (Ctrl+C)" msgstr "Kopieren (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Mission gespeichert" @@ -474,6 +489,9 @@ msgstr "Bohrturm" msgid "Descend\\Reduces the power of the jet" msgstr "Sinken\\Leistung des Triebwerks drosseln" +msgid "Description:" +msgstr "" + msgid "Destroy" msgstr "Zerstören" @@ -486,6 +504,9 @@ msgstr "Einstampfer" msgid "Device\\Driver and resolution settings" msgstr "Bildschirm\\Driver und Bildschirmauflösung" +msgid "Disable\\Disable the selected mod" +msgstr "" + msgid "Dividing by zero" msgstr "Division durch Null" @@ -502,6 +523,9 @@ msgstr "Die Türen werden von einem Gegenstand blockiert" msgid "Down (\\key gdown;)" msgstr "Sinkt (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Zeichner" @@ -529,6 +553,9 @@ msgstr "Ei" msgid "Empty character constant" msgstr "" +msgid "Enable\\Enable the selected mod" +msgstr "" + msgid "End of block missing" msgstr "Es fehlt eine geschlossene geschweifte Klammer \"}\" (Ende des Blocks)" @@ -756,6 +783,9 @@ msgstr "Von Virus infiziert, zeitweise außer Betrieb" msgid "Information exchange post" msgstr "Infoserver" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Anweisung \"break\" außerhalb einer Schleife" @@ -917,9 +947,21 @@ msgstr "Missionen" msgid "Missions on this planet:" msgstr "Liste der Missionen des Planeten:" +msgid "Missions+" +msgstr "Missionen+" + msgid "Missions\\Select mission" msgstr "Missionen\\Aufbruch ins Weltall" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Umkehr X\\Umkehr der Kameradrehung X-Achse" @@ -932,6 +974,9 @@ msgstr "Gewähltes Programm nach unten" msgid "Move selected program up" msgstr "Gewähltes Programm nach oben" +msgid "Mute sounds in background\\Mute all game sounds when the window is unfocused" +msgstr "" + msgid "Mute\\No sound" msgstr "Kein Ton\\Keine Geräusche und Geräuschkulisse" @@ -965,6 +1010,12 @@ msgstr "Nächstes auswählen\\Nächstes Objekt auswählen" msgid "No" msgstr "Nein" +msgid "No changes." +msgstr "" + +msgid "No description." +msgstr "" + msgid "No energy in the subsoil" msgstr "Kein unterirdisches Energievorkommen" @@ -1085,6 +1136,9 @@ msgstr "Öffnen" msgid "Open (Ctrl+O)" msgstr "Öffnen (Ctrl+O)" +msgid "Open Directory\\Open the mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Es fehlt eine offene geschweifte Klammer\"{\"" @@ -1296,6 +1350,9 @@ msgstr "Rote Fahne" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Glänzende Tasten\\Glänzende Tasten in den Menüs" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Überreste einer Apollo-Mission" @@ -1479,6 +1536,15 @@ msgstr "Geräusche:\\Lautstärke Motoren, Stimmen, usw." msgid "Sound\\Music and game sound volume" msgstr "Geräusche\\Lautstärke Geräusche und Musik" +msgid "Space Explorer\\Disables astronaut abilities" +msgstr "" + +msgid "Space Programmer\\Disables radio-control" +msgstr "" + +msgid "Space Researcher\\Disables using all previously researched technologies" +msgstr "" + msgid "Spaceship" msgstr "Raumschiff" @@ -1557,6 +1623,10 @@ msgstr "Texturfilterung\\Texturfilterung" msgid "Textures" msgstr "Texturen" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "" @@ -1569,9 +1639,16 @@ msgstr "Die Funktion hat kein Ergebnis zurückgegeben" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "Mission noch nicht beendet (Drücken Sie auf \\key help; für weitere Informationen)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Die zwei Operanden sind nicht kompatibel" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Diese Klasse gibt es schon" @@ -1696,6 +1773,9 @@ msgstr "Einheit" msgid "Unknown Object" msgstr "Das Objekt existiert nicht" +msgid "Unknown author" +msgstr "" + msgid "Unknown command" msgstr "Befehl unbekannt" @@ -1708,6 +1788,9 @@ msgstr "Unbekannte Funktion" msgid "Up (\\key gup;)" msgstr "Steigt (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Markierung für unterirdisches Platinvorkommen" @@ -1729,6 +1812,9 @@ msgstr "Der Wert dieser Variable wurde nicht definiert" msgid "Vault" msgstr "Bunker" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "" @@ -1747,6 +1833,9 @@ msgstr "Wespe tödlich verwundet" msgid "Waste" msgstr "Abfall" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -1780,6 +1869,9 @@ msgstr "Schnüffler" msgid "Withdraw shield (\\key action;)" msgstr "Schutzschild einholen (\\key action;)" +msgid "Workshop\\Open the workshop to search for mods" +msgstr "" + msgid "Worm" msgstr "Wurm" @@ -1928,6 +2020,9 @@ msgstr "\\Violette Fahne" msgid "\\Yellow flags" msgstr "\\Gelbe Fahne" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" @@ -1949,6 +2044,9 @@ msgstr "epsitec.com" #~ msgid "3D sound\\3D positioning of the sound" #~ msgstr "3D-Geräusche\\Orten der Geräusche im Raum" +#~ msgid "Build a destroyer" +#~ msgstr "Baue einen Zerstörer" + #~ msgid "Building too close" #~ msgstr "Gebäude zu nahe" diff --git a/po/fr.po b/po/fr.po index 415a813b..a6375ce7 100644 --- a/po/fr.po +++ b/po/fr.po @@ -32,6 +32,9 @@ msgstr "\" ] \" manquant" msgid "%s: %d pts" msgstr "%s: %d points" +msgid "+\\Missions with bonus content and optional challenges" +msgstr "" + msgid "..behind" msgstr "..derrière" @@ -125,6 +128,9 @@ msgstr "Aspect\\Choisir votre aspect" msgid "Apply changes\\Activates the changed settings" msgstr "Appliquer les changements\\Active les changements effectués" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Constructeur approprié manquant" @@ -200,9 +206,6 @@ msgstr "Construire une tour" msgid "Build a derrick" msgstr "Construire un derrick" -msgid "Build a destroyer" -msgstr "Construire un destructeur" - msgid "Build a exchange post" msgstr "Construire une station relais" @@ -275,6 +278,9 @@ msgstr "Fabriquer un tireur à chenilles" msgid "Build a tracked sniffer" msgstr "Fabriquer un renifleur à chenilles" +msgid "Build a vault" +msgstr "" + msgid "Build a wheeled builder" msgstr "" @@ -374,6 +380,9 @@ msgstr "Changement de caméra\\Autre de point de vue" msgid "Change player\\Change player" msgstr "Autre joueur\\Choix du nom du joueur" +msgid "Changes" +msgstr "" + msgid "Chapters:" msgstr "Liste des chapitres :" @@ -443,6 +452,12 @@ msgstr "Copier" msgid "Copy (Ctrl+C)" msgstr "Copier (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Enregistrement effectué" @@ -476,6 +491,9 @@ msgstr "Derrick" msgid "Descend\\Reduces the power of the jet" msgstr "Descendre\\Diminuer la puissance du réacteur" +msgid "Description:" +msgstr "" + msgid "Destroy" msgstr "Détruire" @@ -488,6 +506,9 @@ msgstr "Destructeur" msgid "Device\\Driver and resolution settings" msgstr "Affichage\\Pilote et résolution d'affichage" +msgid "Disable\\Disable the selected mod" +msgstr "" + msgid "Dividing by zero" msgstr "Division par zéro" @@ -504,6 +525,9 @@ msgstr "Portes bloquées par un robot ou un objet" msgid "Down (\\key gdown;)" msgstr "Descend (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Robot dessinateur" @@ -531,6 +555,9 @@ msgstr "Oeuf" msgid "Empty character constant" msgstr "" +msgid "Enable\\Enable the selected mod" +msgstr "" + msgid "End of block missing" msgstr "Il manque la fin du bloc" @@ -758,6 +785,9 @@ msgstr "Infecté par un virus; ne fonctionne plus temporairement" msgid "Information exchange post" msgstr "Station relais" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Instruction \"break\" en dehors d'une boucle" @@ -919,9 +949,21 @@ msgstr "Missions" msgid "Missions on this planet:" msgstr "Liste des missions du chapitre :" +msgid "Missions+" +msgstr "Missions+" + msgid "Missions\\Select mission" msgstr "Missions\\La grande aventure" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Inversion souris X\\Inversion de la rotation lorsque la souris touche un bord" @@ -934,6 +976,9 @@ msgstr "Déplace le programme sélectionné vers le bas" msgid "Move selected program up" msgstr "Déplace le programme sélectionné vers le haut" +msgid "Mute sounds in background\\Mute all game sounds when the window is unfocused" +msgstr "" + msgid "Mute\\No sound" msgstr "Silencieux\\Totalement silencieux" @@ -967,6 +1012,12 @@ msgstr "Sélectionner l'objet suivant\\Sélectionner l'objet suivant" msgid "No" msgstr "Non" +msgid "No changes." +msgstr "" + +msgid "No description." +msgstr "" + msgid "No energy in the subsoil" msgstr "Pas d'énergie en sous-sol" @@ -1087,6 +1138,9 @@ msgstr "Ouvrir" msgid "Open (Ctrl+O)" msgstr "Ouvrir (Ctrl+O)" +msgid "Open Directory\\Open the mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Début d'un bloc attendu" @@ -1298,6 +1352,9 @@ msgstr "Drapeau rouge" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Reflets sur les boutons\\Boutons brillants" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Vestige d'une mission Apollo" @@ -1481,6 +1538,15 @@ msgstr "Sons :\\Volume des moteurs, voix, etc." msgid "Sound\\Music and game sound volume" msgstr "Son\\Volumes des sons & musiques" +msgid "Space Explorer\\Disables astronaut abilities" +msgstr "" + +msgid "Space Programmer\\Disables radio-control" +msgstr "" + +msgid "Space Researcher\\Disables using all previously researched technologies" +msgstr "" + msgid "Spaceship" msgstr "Vaisseau spatial" @@ -1559,6 +1625,10 @@ msgstr "Filtrage de textures\\Filtrage de textures" msgid "Textures" msgstr "Textures" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "La bataille est terminée" @@ -1571,9 +1641,16 @@ msgstr "La fonction n'a pas retourné de résultat" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "La mission n'est pas terminée (appuyez sur \\key help; pour plus de détails)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Les deux opérandes ne sont pas de types compatibles" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Cette classe existe déjà" @@ -1698,6 +1775,9 @@ msgstr "Unité" msgid "Unknown Object" msgstr "Objet inconnu" +msgid "Unknown author" +msgstr "" + msgid "Unknown command" msgstr "Commande inconnue" @@ -1710,6 +1790,9 @@ msgstr "Routine inconnue" msgid "Up (\\key gup;)" msgstr "Monte (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Emplacement pour un derrick (minerai d'uranium)" @@ -1731,6 +1814,9 @@ msgstr "Variable non initialisée" msgid "Vault" msgstr "Coffre-fort" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "Synchronisation verticale :\\Réduit la fréquence d'images par seconde à afficher." @@ -1749,6 +1835,9 @@ msgstr "Guêpe mortellement touchée" msgid "Waste" msgstr "Déchet" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -1782,6 +1871,9 @@ msgstr "Robot renifleur volant" msgid "Withdraw shield (\\key action;)" msgstr "Refermer le bouclier (\\key action;)" +msgid "Workshop\\Open the workshop to search for mods" +msgstr "" + msgid "Worm" msgstr "Ver" @@ -1930,6 +2022,9 @@ msgstr "\\Drapeaux violets" msgid "\\Yellow flags" msgstr "\\Drapeaux jaunes" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" @@ -1948,6 +2043,9 @@ msgstr "epsitec.com" #~ msgid "3D sound\\3D positioning of the sound" #~ msgstr "Bruitages 3D\\Positionnement sonore dans l'espace" +#~ msgid "Build a destroyer" +#~ msgstr "Construire un destructeur" + #~ msgid "Building too close" #~ msgstr "Bâtiment trop proche" diff --git a/po/pl.po b/po/pl.po index 31905276..90e9f65f 100644 --- a/po/pl.po +++ b/po/pl.po @@ -31,6 +31,9 @@ msgstr "Brak \" ] \"" msgid "%s: %d pts" msgstr "%s: %d pkt" +msgid "+\\Missions with bonus content and optional challenges" +msgstr "+\\Misje z dodatkową zawartością i opcjonalnymi wyzwaniami" + msgid "..behind" msgstr "..za" @@ -124,6 +127,9 @@ msgstr "Wygląd\\Wybierz swoją postać" msgid "Apply changes\\Activates the changed settings" msgstr "Zastosuj zmiany\\Aktywuje zmienione ustawienia" +msgid "Apply\\Apply the current mod configuration" +msgstr "Zastosuj\\Zastosuj obecną konfigurację modów" + msgid "Appropriate constructor missing" msgstr "Brak odpowiedniego konstruktora" @@ -182,7 +188,7 @@ msgid "Bot factory" msgstr "Fabryka robotów" msgid "Build (\\key action;)" -msgstr "" +msgstr "Buduj (\\key action;)" msgid "Build a bot factory" msgstr "Zbuduj fabrykę robotów" @@ -196,14 +202,11 @@ msgstr "Zbuduj wieżę obronną" msgid "Build a derrick" msgstr "Zbuduj kopalnię" -msgid "Build a destroyer" -msgstr "Zbuduj niszczarkę" - msgid "Build a exchange post" msgstr "Zbuduj stację przekaźnikową" msgid "Build a legged builder" -msgstr "" +msgstr "Zbuduj budowniczego na nogach" msgid "Build a legged grabber" msgstr "Zbuduj transporter na nogach" @@ -251,13 +254,13 @@ msgid "Build a subber" msgstr "Zbuduj robota nurka" msgid "Build a target bot" -msgstr "" +msgstr "Zbuduj robota-cel" msgid "Build a thumper" msgstr "Zbuduj robota uderzacza" msgid "Build a tracked builder" -msgstr "" +msgstr "Zbuduj budowniczego na gąsienicach" msgid "Build a tracked grabber" msgstr "Zbuduj transporter na gąsienicach" @@ -271,8 +274,11 @@ msgstr "Zbuduj działo na gąsienicach" msgid "Build a tracked sniffer" msgstr "Zbuduj szperacz na gąsienicach" +msgid "Build a vault" +msgstr "Zbuduj skrytkę" + msgid "Build a wheeled builder" -msgstr "" +msgstr "Zbuduj budowniczego na kołach" msgid "Build a wheeled grabber" msgstr "Zbuduj transporter na kołach" @@ -287,7 +293,7 @@ msgid "Build a wheeled sniffer" msgstr "Zbuduj szperacz na kołach" msgid "Build a winged builder" -msgstr "" +msgstr "Zbuduj latającego budowniczego" msgid "Build a winged grabber" msgstr "Zbuduj transporter latający" @@ -370,6 +376,9 @@ msgstr "Zmień kamerę\\Przełącza pomiędzy kamerą pokładową i śledzącą" msgid "Change player\\Change player" msgstr "Zmień gracza\\Zmień gracza" +msgid "Changes" +msgstr "Zmiany" + msgid "Chapters:" msgstr "Rozdziały:" @@ -439,6 +448,12 @@ msgstr "Kopiuj" msgid "Copy (Ctrl+C)" msgstr "Kopiuj (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "Nie udało się otworzyć przeglądarki plików!" + +msgid "Could not open the web browser!" +msgstr "Nie udało się otworzyć przeglądarki internetowej!" + msgid "Current mission saved" msgstr "Bieżąca misja zapisana" @@ -472,6 +487,9 @@ msgstr "Kopalnia" msgid "Descend\\Reduces the power of the jet" msgstr "W dół\\Zmniejsza moc silnika" +msgid "Description:" +msgstr "Opis:" + msgid "Destroy" msgstr "Zniszcz" @@ -484,6 +502,9 @@ msgstr "Destroyer" msgid "Device\\Driver and resolution settings" msgstr "Urządzenie\\Ustawienia sterownika i rozdzielczości" +msgid "Disable\\Disable the selected mod" +msgstr "Wyłącz\\Wyłącza zaznaczonego moda" + msgid "Dividing by zero" msgstr "Dzielenie przez zero" @@ -500,11 +521,14 @@ msgstr "Drzwi zablokowane przez robota lub inny obiekt" msgid "Down (\\key gdown;)" msgstr "Dół (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "W dół\\Przenieś zaznaczonego moda w dół, aby był załadowany później (mody mogą nadpisywać pliki modów wyżej)" + msgid "Drawer bot" msgstr "Robot rysownik" msgid "Duplicate label in switch" -msgstr "" +msgstr "Zduplikowana wartość w instrukcji switch" msgid "Dust\\Dust and dirt on bots and buildings" msgstr "Kurz\\Kurz i bród na robotach i budynkach" @@ -525,7 +549,10 @@ msgid "Egg" msgstr "Jajo" msgid "Empty character constant" -msgstr "" +msgstr "Stała będąca pustym znakiem" + +msgid "Enable\\Enable the selected mod" +msgstr "Włącz\\Włącza zaznaczonego moda" msgid "End of block missing" msgstr "Brak końca bloku" @@ -742,7 +769,7 @@ msgid "Inappropriate object" msgstr "Nieodpowiedni obiekt" msgid "Inappropriate sample" -msgstr "" +msgstr "Nieprawidłowa próbka" msgid "Incorrect index type" msgstr "Nieprawidłowy typ indeksu" @@ -753,6 +780,9 @@ msgstr "Zainfekowane wirusem, chwilowo niesprawne" msgid "Information exchange post" msgstr "Stacja przekaźnikowa informacji" +msgid "Information:" +msgstr "Informacje:" + msgid "Instruction \"break\" outside a loop" msgstr "Polecenie \"break\" na zewnątrz pętli" @@ -784,7 +814,7 @@ msgid "Internal error - tell the developers" msgstr "Błąd wewnętrzny - powiadom twórców gry" msgid "Invalid universal character name" -msgstr "" +msgstr "Nieprawidłowy znak Unicode" msgid "Invert\\Invert values on this axis" msgstr "Odwróć\\Odwróć wartości na tej osi" @@ -814,7 +844,7 @@ msgid "LOADING" msgstr "WCZYTYWANIE" msgid "Legged builder" -msgstr "" +msgstr "Budowniczy na nogach" msgid "Legged grabber" msgstr "Transporter na nogach" @@ -900,9 +930,21 @@ msgstr "Misje" msgid "Missions on this planet:" msgstr "Misje na tej planecie:" +msgid "Missions+" +msgstr "Misje+" + msgid "Missions\\Select mission" msgstr "Misje\\Wybierz misję" +msgid "Mods" +msgstr "Mody" + +msgid "Mods:" +msgstr "Mody:" + +msgid "Mods\\Mod manager" +msgstr "Mody\\Zarządzanie modami" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Odwrócenie myszy X\\Odwrócenie kierunków przewijania w poziomie" @@ -915,6 +957,9 @@ msgstr "Przenieś zaznaczony program w dół" msgid "Move selected program up" msgstr "Przenieś zaznaczony program w górę" +msgid "Mute sounds in background\\Mute all game sounds when the window is unfocused" +msgstr "Cisza, gdy okno jest w tle\\Wycisza wszystkie dźwięki, gdy okno gry stanie się nieaktywne" + msgid "Mute\\No sound" msgstr "Cisza\\Brak dźwięków" @@ -934,7 +979,7 @@ msgid "New ..." msgstr "Nowy ..." msgid "New Folder" -msgstr "" +msgstr "Nowy folder" msgid "New bot available" msgstr "Dostępny nowy robot" @@ -948,6 +993,12 @@ msgstr "Następny obiekt\\Zaznacza następny obiekt" msgid "No" msgstr "Nie" +msgid "No changes." +msgstr "Brak zmian." + +msgid "No description." +msgstr "Brak opisu." + msgid "No energy in the subsoil" msgstr "Brak energii w ziemi" @@ -1068,6 +1119,9 @@ msgstr "Otwórz" msgid "Open (Ctrl+O)" msgstr "Otwórz (Ctrl+O)" +msgid "Open Directory\\Open the mods directory" +msgstr "Otwórz katalog\\Otwórz katalog z modami" + msgid "Opening brace missing" msgstr "Brak klamry otwierającej" @@ -1093,7 +1147,7 @@ msgid "Original game developed by:" msgstr "Twórcy oryginalnej gry:" msgid "Overwrite existing file?" -msgstr "" +msgstr "Nadpisać istniejący plik?" msgid "Parameters missing" msgstr "Brak wymaganego parametru" @@ -1129,7 +1183,7 @@ msgid "Planets:" msgstr "Planety:" msgid "Plans for builder available" -msgstr "" +msgstr "Dostępne plany robota budowniczego" msgid "Plans for defense tower available" msgstr "Dostępne plany wieży obronnej" @@ -1278,6 +1332,9 @@ msgstr "Czerwona flaga" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Odbicia na przyciskach \\Świecące przyciski" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "Odśwież\\Odśwież listę obecnie zainstalowanych modów" + msgid "Remains of Apollo mission" msgstr "Pozostałości z misji Apollo" @@ -1336,7 +1393,7 @@ msgid "Ruin" msgstr "Ruiny" msgid "Run research program for builder" -msgstr "" +msgstr "Rozpocznij prace badawcze nad robotem budowniczym" msgid "Run research program for defense tower" msgstr "Rozpocznij prace badawcze nad wieżą obronną" @@ -1360,7 +1417,7 @@ msgid "Run research program for shooter" msgstr "Rozpocznij prace badawcze nad działem" msgid "Run research program for target bot" -msgstr "" +msgstr "Rozpocznij prace badawcze nad robotem-celem" msgid "Run research program for thumper" msgstr "Rozpocznij prace badawcze nad robotem uderzaczem" @@ -1393,7 +1450,7 @@ msgid "Save\\Saves the current mission" msgstr "Zapisz\\Zapisuje bieżącą misję" msgid "Select Folder" -msgstr "" +msgstr "Wybierz folder" msgid "Select the astronaut\\Selects the astronaut" msgstr "Zaznacz astronautę\\Zaznacza astronautę" @@ -1461,6 +1518,15 @@ msgstr "Efekty dźwiękowe:\\Głośność silników, głosów, strzałów, itp." msgid "Sound\\Music and game sound volume" msgstr "Dźwięk\\Głośność muzyki i dźwięków gry" +msgid "Space Explorer\\Disables astronaut abilities" +msgstr "Kosmiczny odkrywca\\Wyłącza umiejętności astronauty" + +msgid "Space Programmer\\Disables radio-control" +msgstr "Kosmiczny koder\\Wyłącza zdalną kontrolę" + +msgid "Space Researcher\\Disables using all previously researched technologies" +msgstr "Kosmiczny badacz\\Blokuje dostęp do poprzednio wynalezionych technologii" + msgid "Spaceship" msgstr "Statek kosmiczny" @@ -1539,6 +1605,10 @@ msgstr "Filtrowanie tekstur\\Filtrowanie tekstur" msgid "Textures" msgstr "Tekstury" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "Nie udało się otworzyć adresu %s w przeglądarce internetowej." + msgid "The battle has ended" msgstr "Bitwa zakończyła się" @@ -1551,9 +1621,16 @@ msgstr "Funkcja nie zwróciła żadnej wartości" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "Misja nie jest wypełniona (naciśnij \\key help; aby uzyskać szczegóły)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "Nie udało się otworzyć ścieżki %s w przeglądarce plików." + msgid "The types of the two operands are incompatible" msgstr "Niezgodne typy operatorów" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "Są niezapisane zmiany. Czy chcesz je zapisać przed wyjściem?" + msgid "This class already exists" msgstr "Taka klasa już istnieje" @@ -1628,7 +1705,7 @@ msgid "Too many parameters" msgstr "Za dużo parametrów" msgid "Tracked builder" -msgstr "" +msgstr "Budowniczy na gąsienicach" msgid "Tracked grabber" msgstr "Transporter na gąsienicach" @@ -1678,11 +1755,14 @@ msgstr "Jednostka" msgid "Unknown Object" msgstr "Obiekt nieznany" +msgid "Unknown author" +msgstr "Nieznany autor" + msgid "Unknown command" msgstr "Nieznane polecenie" msgid "Unknown escape sequence" -msgstr "" +msgstr "Nieznany znak ucieczki" msgid "Unknown function" msgstr "Funkcja nieznana" @@ -1690,6 +1770,9 @@ msgstr "Funkcja nieznana" msgid "Up (\\key gup;)" msgstr "Góra (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "W górę\\Przenieś zaznaczonego moda w górę, aby był załadowany wcześniej (mody mogą nadpisywać pliki modów wyżej)" + msgid "Uranium deposit (site for derrick)" msgstr "Złoże uranu (miejsce na kopalnię)" @@ -1711,6 +1794,9 @@ msgstr "Zmienna nie została zainicjalizowana" msgid "Vault" msgstr "Skrytka" +msgid "Version" +msgstr "Wersja" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "Synchronizacja pionowa\\Ogranicza ilość klatek na sekundę do wartości odświeżania ekranu" @@ -1729,8 +1815,11 @@ msgstr "Osa śmiertelnie raniona" msgid "Waste" msgstr "Odpady" +msgid "Website" +msgstr "Strona internetowa" + msgid "Wheeled builder" -msgstr "" +msgstr "Budowniczy na kołach" msgid "Wheeled grabber" msgstr "Transporter na kołach" @@ -1745,7 +1834,7 @@ msgid "Wheeled sniffer" msgstr "Szperacz na kołach" msgid "Winged builder" -msgstr "" +msgstr "Budowniczy latający" msgid "Winged grabber" msgstr "Transporter latający" @@ -1762,6 +1851,9 @@ msgstr "Szperacz latający" msgid "Withdraw shield (\\key action;)" msgstr "Wyłącz osłonę (\\key action;)" +msgid "Workshop\\Open the workshop to search for mods" +msgstr "Warsztat\\Otwórz warsztat, aby poszukać modów" + msgid "Worm" msgstr "Robal" @@ -1912,6 +2004,9 @@ msgstr "\\Fioletowe flagi" msgid "\\Yellow flags" msgstr "\\Żółte flagi" +msgid "by" +msgstr "autorstwa" + msgid "colobot.info" msgstr "colobot.info" @@ -1927,6 +2022,9 @@ msgstr "epsitec.com" #~ msgid "3D sound\\3D positioning of the sound" #~ msgstr "Dźwięk 3D\\Przestrzenne pozycjonowanie dźwięków" +#~ msgid "Build a destroyer" +#~ msgstr "Zbuduj niszczarkę" + #~ msgid "Building too close" #~ msgstr "Budynek za blisko" diff --git a/po/pt.po b/po/pt.po index 3c0c0bd9..8c152823 100644 --- a/po/pt.po +++ b/po/pt.po @@ -29,6 +29,9 @@ msgstr "\" ] \" faltando" msgid "%s: %d pts" msgstr "" +msgid "+\\Missions with bonus content and optional challenges" +msgstr "" + msgid "..behind" msgstr "..atrás" @@ -122,6 +125,9 @@ msgstr "Aparência\\Escolha sua aparência" msgid "Apply changes\\Activates the changed settings" msgstr "Aplicar mudanças\\Ativa as configurações alteradas" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Construtor apropriado faltando" @@ -194,9 +200,6 @@ msgstr "Construir uma torre de defesa" msgid "Build a derrick" msgstr "Construir um extrator" -msgid "Build a destroyer" -msgstr "Construir um destruidor" - msgid "Build a exchange post" msgstr "Construir um posto de troca" @@ -269,6 +272,9 @@ msgstr "Construir um atirador com esteiras" msgid "Build a tracked sniffer" msgstr "Construir um farejador com esteiras" +msgid "Build a vault" +msgstr "" + msgid "Build a wheeled builder" msgstr "" @@ -368,6 +374,9 @@ msgstr "Mudar câmera\\Alterna entre câmera incorporada e câmera seguidora" msgid "Change player\\Change player" msgstr "Mudar jogador\\Mudar jogador" +msgid "Changes" +msgstr "" + msgid "Chapters:" msgstr "Capítulos:" @@ -438,6 +447,12 @@ msgstr "Copiar" msgid "Copy (Ctrl+C)" msgstr "Copiar (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Missão atual salva" @@ -471,6 +486,9 @@ msgstr "Extrator" msgid "Descend\\Reduces the power of the jet" msgstr "Descer\\Diminui o poder do jato" +msgid "Description:" +msgstr "" + msgid "Destroy" msgstr "Destruir" @@ -483,6 +501,9 @@ msgstr "Destruidor" msgid "Device\\Driver and resolution settings" msgstr "Dispositivo\\Configurações de driver e resolução" +msgid "Disable\\Disable the selected mod" +msgstr "" + msgid "Dividing by zero" msgstr "Dividindo por zero" @@ -499,6 +520,9 @@ msgstr "Portas bloqueadas por um robô ou outro objeto" msgid "Down (\\key gdown;)" msgstr "Baixo (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Robô cartoonista" @@ -526,6 +550,9 @@ msgstr "Ovo" msgid "Empty character constant" msgstr "" +msgid "Enable\\Enable the selected mod" +msgstr "" + msgid "End of block missing" msgstr "Fim do bloco ausente" @@ -753,6 +780,9 @@ msgstr "Infectado por vírus; temporariamento fora de serviço" msgid "Information exchange post" msgstr "Posto de troca de informação" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Intrução \"break\" fora de um laço" @@ -914,9 +944,21 @@ msgstr "Missões" msgid "Missions on this planet:" msgstr "Lista de missões neste planeta:" +msgid "Missions+" +msgstr "Missões+" + msgid "Missions\\Select mission" msgstr "Missões\\Selecione uma missão" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Inversão de mouse X\\Inverte a direção da rolagem no eixo X" @@ -929,6 +971,9 @@ msgstr "Move o programa selecionado para baixo" msgid "Move selected program up" msgstr "Move o programa selecionado para cima" +msgid "Mute sounds in background\\Mute all game sounds when the window is unfocused" +msgstr "" + msgid "Mute\\No sound" msgstr "Mudo\\Sem som" @@ -947,6 +992,9 @@ msgstr "Novo" msgid "New ..." msgstr "Novo ..." +msgid "New Folder" +msgstr "" + msgid "New bot available" msgstr "Novo robô disponível" @@ -959,6 +1007,12 @@ msgstr "Próximo objeto\\Selecionar o próximo objeto" msgid "No" msgstr "Não" +msgid "No changes." +msgstr "" + +msgid "No description." +msgstr "" + msgid "No energy in the subsoil" msgstr "Nenhuma energia no subsolo" @@ -1079,6 +1133,9 @@ msgstr "Abrir" msgid "Open (Ctrl+O)" msgstr "Abrir (Ctrl+O)" +msgid "Open Directory\\Open the mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Chave de abertura ausente" @@ -1103,6 +1160,9 @@ msgstr "Origem da última mensagem\\Mostra de onde a última mensagem foi enviad msgid "Original game developed by:" msgstr "Jogo original desenvolvido por:" +msgid "Overwrite existing file?" +msgstr "" + msgid "Parameters missing" msgstr "Parâmetros ausentes" @@ -1287,6 +1347,9 @@ msgstr "Bandeira vermelha" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Reflexões nos botões\\Botões brilhantes" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Restos da missão Apollo" @@ -1401,6 +1464,9 @@ msgstr "Salvar\\Salve a missão atual" msgid "Save\\Saves the current mission" msgstr "Salvar\\Salva a missão atual" +msgid "Select Folder" +msgstr "" + msgid "Select the astronaut\\Selects the astronaut" msgstr "Selecione o astronauta\\Seleciona o astronauta" @@ -1467,6 +1533,15 @@ msgstr "Efeitos sonoros:\\Volume dos motores, voz, tiros, etc." msgid "Sound\\Music and game sound volume" msgstr "Som\\Volume do som das músicas e do jogo" +msgid "Space Explorer\\Disables astronaut abilities" +msgstr "" + +msgid "Space Programmer\\Disables radio-control" +msgstr "" + +msgid "Space Researcher\\Disables using all previously researched technologies" +msgstr "" + msgid "Spaceship" msgstr "Nave espacial" @@ -1545,6 +1620,10 @@ msgstr "Filtragem de textura\\Filtragem de textura" msgid "Textures" msgstr "Texturas" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "A batalha acabou" @@ -1557,9 +1636,16 @@ msgstr "A função não retornou nenhum valor" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "A missão não foi completada ainda (pressione \\key help; para mais detalhes)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Os tipos dos dois operandos são incompativeis" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Esta classe já existe" @@ -1684,6 +1770,9 @@ msgstr "Unidade" msgid "Unknown Object" msgstr "Objeto desconhecido" +msgid "Unknown author" +msgstr "" + msgid "Unknown command" msgstr "Comando desconhecido" @@ -1696,6 +1785,9 @@ msgstr "Função desconhecida" msgid "Up (\\key gup;)" msgstr "Cima (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Depósito de urânio (local para extrator)" @@ -1717,6 +1809,9 @@ msgstr "Variável não inicializada" msgid "Vault" msgstr "Cofre" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "" @@ -1735,6 +1830,9 @@ msgstr "Vespa fatalmente ferida" msgid "Waste" msgstr "Desperdício" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -1768,6 +1866,9 @@ msgstr "Farejador alado" msgid "Withdraw shield (\\key action;)" msgstr "Retirar escudo (\\key action;)" +msgid "Workshop\\Open the workshop to search for mods" +msgstr "" + msgid "Worm" msgstr "Verme" @@ -1916,6 +2017,9 @@ msgstr "\\Bandeiras violetas" msgid "\\Yellow flags" msgstr "\\Bandeiras amarelas" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" @@ -1937,6 +2041,9 @@ msgstr "epsitec.com" #~ msgid "3D sound\\3D positioning of the sound" #~ msgstr "Bruitages 3D\\Positionnement sonore dans l'espace" +#~ msgid "Build a destroyer" +#~ msgstr "Construir um destruidor" + #~ msgid "Building too close" #~ msgstr "Bâtiment trop proche" diff --git a/po/ru.po b/po/ru.po index 5266520d..09d3d571 100644 --- a/po/ru.po +++ b/po/ru.po @@ -31,6 +31,9 @@ msgstr "Отсутствует \"]\" " msgid "%s: %d pts" msgstr "" +msgid "+\\Missions with bonus content and optional challenges" +msgstr "" + msgid "..behind" msgstr "Сзади" @@ -124,6 +127,9 @@ msgstr "Внешность\\Настройка внешности" msgid "Apply changes\\Activates the changed settings" msgstr "Принять\\Принять изменения настроек" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Соответствующий конструктор отсутствует" @@ -197,9 +203,6 @@ msgstr "Построить защитную башню" msgid "Build a derrick" msgstr "Построить буровую вышку" -msgid "Build a destroyer" -msgstr "Построить уничтожитель" - msgid "Build a exchange post" msgstr "Построить пост по обмену сообщениями" @@ -272,6 +275,9 @@ msgstr "Собрать гусеничного стрелка" msgid "Build a tracked sniffer" msgstr "Собрать гусеничного искателя" +msgid "Build a vault" +msgstr "" + msgid "Build a wheeled builder" msgstr "" @@ -375,6 +381,9 @@ msgstr "Изменить вид\\Переключение между борто msgid "Change player\\Change player" msgstr "Новый игрок\\Выберите имя для игрока" +msgid "Changes" +msgstr "" + msgid "Chapters:" msgstr "Разделы:" @@ -446,6 +455,12 @@ msgstr "Копировать" msgid "Copy (Ctrl+C)" msgstr "Копировать (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Текущая миссия сохранена" @@ -480,6 +495,9 @@ msgstr "Космический корабль" msgid "Descend\\Reduces the power of the jet" msgstr "Снижение и посадка\\Понижение мощности реактивного двигателя" +msgid "Description:" +msgstr "" + msgid "Destroy" msgstr "Уничтожить" @@ -492,6 +510,9 @@ msgstr "Уничтожитель" msgid "Device\\Driver and resolution settings" msgstr "Устройство\\Драйвер и настройки разрешения" +msgid "Disable\\Disable the selected mod" +msgstr "" + msgid "Dividing by zero" msgstr "Деление на ноль (запрещено!)" @@ -508,6 +529,9 @@ msgstr "Двери заблокированы роботом или другим msgid "Down (\\key gdown;)" msgstr "Вниз (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Рисовальщик" @@ -535,6 +559,9 @@ msgstr "Яйцо" msgid "Empty character constant" msgstr "" +msgid "Enable\\Enable the selected mod" +msgstr "" + msgid "End of block missing" msgstr "Отсутствует конец блока" @@ -762,6 +789,9 @@ msgstr "Заражено вирусом. Временно вышел из стр msgid "Information exchange post" msgstr "Пост обмена информацией" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Инструкция \"break\" вне цикла" @@ -923,9 +953,21 @@ msgstr "Миссии" msgid "Missions on this planet:" msgstr "Миссии на этой планете:" +msgid "Missions+" +msgstr "Миссии+" + msgid "Missions\\Select mission" msgstr "Миссии\\Выбор миссии" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Инверсия мыши по оси X\\Инверсия прокрутки по оси Х" @@ -940,6 +982,9 @@ msgstr "Изменить выбранную программу" msgid "Move selected program up" msgstr "Изменить выбранную программу" +msgid "Mute sounds in background\\Mute all game sounds when the window is unfocused" +msgstr "" + msgid "Mute\\No sound" msgstr "Без звука\\Без звука" @@ -973,6 +1018,12 @@ msgstr "Следующий объект\\Выбор следующего объ msgid "No" msgstr "Нет" +msgid "No changes." +msgstr "" + +msgid "No description." +msgstr "" + msgid "No energy in the subsoil" msgstr "Под землей нет запасов энергии" @@ -1093,6 +1144,9 @@ msgstr "Открыть" msgid "Open (Ctrl+O)" msgstr "Открыть (Ctrl+O)" +msgid "Open Directory\\Open the mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Открывающая скобка отсутствует" @@ -1305,6 +1359,9 @@ msgstr "Красный флаг" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Отражения на кнопках \\Блестящие кнопки" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Остатки миссии Аполлон" @@ -1491,6 +1548,15 @@ msgstr "Общий звук:\\Гормкость двигателя, голос msgid "Sound\\Music and game sound volume" msgstr "Звук\\Громкость музыки и звуков" +msgid "Space Explorer\\Disables astronaut abilities" +msgstr "" + +msgid "Space Programmer\\Disables radio-control" +msgstr "" + +msgid "Space Researcher\\Disables using all previously researched technologies" +msgstr "" + msgid "Spaceship" msgstr "Космический корабль" @@ -1570,6 +1636,10 @@ msgstr "Фильтрация текстур\\Фильтрация текстур msgid "Textures" msgstr "Текстуры" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "" @@ -1582,9 +1652,16 @@ msgstr "Функция не возвратила значения" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "Миссия еще не выполнена (нажмите \\key help; для более подробной информации)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Типы операндов несовместимы" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Этот класс уже существует" @@ -1709,6 +1786,9 @@ msgstr "Юнит" msgid "Unknown Object" msgstr "Неизвестный объект" +msgid "Unknown author" +msgstr "" + msgid "Unknown command" msgstr "Неизвестная команда" @@ -1721,6 +1801,9 @@ msgstr "Неизвестная функция" msgid "Up (\\key gup;)" msgstr "Вверх (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Запасы урана (место для буровой вышки)" @@ -1742,6 +1825,9 @@ msgstr "Переменная не инициализирована" msgid "Vault" msgstr "Хранилище" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "" @@ -1760,6 +1846,9 @@ msgstr "Оса смертельно ранена" msgid "Waste" msgstr "Мусор" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -1793,6 +1882,9 @@ msgstr "Летающий искатель" msgid "Withdraw shield (\\key action;)" msgstr "Снять щит (\\key action;)" +msgid "Workshop\\Open the workshop to search for mods" +msgstr "" + msgid "Worm" msgstr "Червь" @@ -1941,6 +2033,9 @@ msgstr "\\Фиолетовый флаг" msgid "\\Yellow flags" msgstr "\\Желтый флаг" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" @@ -1962,6 +2057,9 @@ msgstr "epsitec.com" #~ msgid "3D sound\\3D positioning of the sound" #~ msgstr "3D-звук\\Стерео звук" +#~ msgid "Build a destroyer" +#~ msgstr "Построить уничтожитель" + #~ msgid "Building too close" #~ msgstr "Здание слишком близко" diff --git a/src/CBot/CBotCStack.cpp b/src/CBot/CBotCStack.cpp index 5065ddfa..ba48ebcb 100644 --- a/src/CBot/CBotCStack.cpp +++ b/src/CBot/CBotCStack.cpp @@ -31,107 +31,100 @@ namespace CBot { -//////////////////////////////////////////////////////////////////////////////// -CBotProgram* CBotCStack::m_prog = nullptr; // init the static variable -CBotError CBotCStack::m_error = CBotNoErr; -int CBotCStack::m_end = 0; -CBotTypResult CBotCStack::m_retTyp = CBotTypResult(0); +struct CBotCStack::Data +{ + //! The program currently being compiled + CBotProgram* prog = nullptr; + //! The current error state of the compile stack + CBotError error = CBotNoErr; + int errEnd = 0; + //! The return type of the function currently being compiled + CBotTypResult retTyp = CBotTypResult(CBotTypVoid); +}; -//////////////////////////////////////////////////////////////////////////////// CBotCStack::CBotCStack(CBotCStack* ppapa) { - m_next = nullptr; m_prev = ppapa; if (ppapa == nullptr) { - m_error = CBotNoErr; - m_start = 0; - m_end = 0; + m_data = new CBotCStack::Data; + m_errStart = 0; m_bBlock = true; } else { - m_start = ppapa->m_start; + m_data = ppapa->m_data; + m_errStart = ppapa->m_errStart; m_bBlock = false; } - - m_listVar = nullptr; - m_var = nullptr; } //////////////////////////////////////////////////////////////////////////////// CBotCStack::~CBotCStack() { - if (m_next != nullptr) delete m_next; - if (m_prev != nullptr) m_prev->m_next = nullptr; // removes chain - - delete m_var; - delete m_listVar; + if (m_prev == nullptr) delete m_data; } //////////////////////////////////////////////////////////////////////////////// CBotCStack* CBotCStack::TokenStack(CBotToken* pToken, bool bBlock) { - if (m_next != nullptr) return m_next; // include on an existing stack + if (m_next) return m_next.get(); // include on an existing stack - CBotCStack* p = new CBotCStack(this); - m_next = p; // channel element - p->m_bBlock = bBlock; + m_next.reset(new CBotCStack(this)); + m_next->m_bBlock = bBlock; - if (pToken != nullptr) p->SetStartError(pToken->GetStart()); + if (pToken != nullptr) m_next->SetStartError(pToken->GetStart()); - return p; + return m_next.get(); +} + +void CBotCStack::DeleteNext() +{ + m_next.reset(); } -//////////////////////////////////////////////////////////////////////////////// CBotInstr* CBotCStack::Return(CBotInstr* inst, CBotCStack* pfils) { if ( pfils == this ) return inst; - if (m_var != nullptr) delete m_var; // value replaced? - m_var = pfils->m_var; // result transmitted - pfils->m_var = nullptr; // not to destroy the variable + m_var = std::move(pfils->m_var); // result transmitted - if (m_error) + if (m_data->error != CBotNoErr) { - m_start = pfils->m_start; // retrieves the position of the error - m_end = pfils->m_end; + m_errStart = pfils->m_errStart; // retrieves the position of the error } - delete pfils; + m_next.reset(); return inst; } //////////////////////////////////////////////////////////////////////////////// CBotFunction* CBotCStack::ReturnFunc(CBotFunction* inst, CBotCStack* pfils) { - if (m_var != nullptr) delete m_var; // value replaced? - m_var = pfils->m_var; // result transmitted - pfils->m_var = nullptr; // not to destroy the variable + m_var = std::move(pfils->m_var); // result transmitted - if (m_error) + if (m_data->error != CBotNoErr) { - m_start = pfils->m_start; // retrieves the position of the error - m_end = pfils->m_end; + m_errStart = pfils->m_errStart; // retrieves the position of the error } - delete pfils; + m_next.reset(); return inst; } //////////////////////////////////////////////////////////////////////////////// CBotError CBotCStack::GetError(int& start, int& end) { - start = m_start; - end = m_end; - return m_error; + start = m_errStart; + end = m_data->errEnd; + return m_data->error; } //////////////////////////////////////////////////////////////////////////////// CBotError CBotCStack::GetError() { - return m_error; + return m_data->error; } //////////////////////////////////////////////////////////////////////////////// @@ -171,18 +164,13 @@ void CBotCStack::SetType(CBotTypResult& type) CBotVar* CBotCStack::FindVar(CBotToken* &pToken) { CBotCStack* p = this; - std::string name = pToken->GetString(); + const auto& name = pToken->GetString(); while (p != nullptr) { - CBotVar* pp = p->m_listVar; - while ( pp != nullptr) + if (p->m_bBlock) for (auto& var : p->m_listVar) { - if (name == pp->GetName()) - { - return pp; - } - pp = pp->m_next; + if (name == var->GetName()) return var.get(); } p = p->m_prev; } @@ -211,39 +199,39 @@ CBotVar* CBotCStack::CopyVar(CBotToken& Token) //////////////////////////////////////////////////////////////////////////////// bool CBotCStack::IsOk() { - return (m_error == 0); + return (m_data->error == CBotNoErr); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetStartError( int pos ) { - if ( m_error != 0) return; // does not change existing error - m_start = pos; + if (m_data->error != CBotNoErr) return; // does not change existing error + m_errStart = pos; } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetError(CBotError n, int pos) { - if ( n!= 0 && m_error != 0) return; // does not change existing error - m_error = n; - m_end = pos; + if (n != CBotNoErr && m_data->error != CBotNoErr) return; // does not change existing error + m_data->error = n; + m_data->errEnd = pos; } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetError(CBotError n, CBotToken* p) { - if (m_error) return; // does not change existing error - m_error = n; - m_start = p->GetStart(); - m_end = p->GetEnd(); + if (m_data->error != CBotNoErr) return; // does not change existing error + m_data->error = n; + m_errStart = p->GetStart(); + m_data->errEnd = p->GetEnd(); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::ResetError(CBotError n, int start, int end) { - m_error = n; - m_start = start; - m_end = end; + m_data->error = n; + m_errStart = start; + m_data->errEnd = end; } //////////////////////////////////////////////////////////////////////////////// @@ -261,48 +249,47 @@ bool CBotCStack::NextToken(CBotToken* &p) //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetProgram(CBotProgram* p) { - m_prog = p; + m_data->prog = p; } //////////////////////////////////////////////////////////////////////////////// CBotProgram* CBotCStack::GetProgram() { - return m_prog; + return m_data->prog; } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetRetType(CBotTypResult& type) { - m_retTyp = type; + m_data->retTyp = type; } //////////////////////////////////////////////////////////////////////////////// CBotTypResult CBotCStack::GetRetType() { - return m_retTyp; + return m_data->retTyp; } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetVar( CBotVar* var ) { - if (m_var) delete m_var; // replacement of a variable - m_var = var; + m_var.reset(var); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetCopyVar( CBotVar* var ) { - if (m_var) delete m_var; // replacement of a variable + m_var.reset(); if ( var == nullptr ) return; - m_var = CBotVar::Create("", var->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC)); + m_var.reset(CBotVar::Create("", var->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC))); m_var->Copy( var ); } //////////////////////////////////////////////////////////////////////////////// CBotVar* CBotCStack::GetVar() { - return m_var; + return m_var.get(); } //////////////////////////////////////////////////////////////////////////////// @@ -310,15 +297,12 @@ void CBotCStack::AddVar(CBotVar* pVar) { CBotCStack* p = this; - // returns to the father element + // find the level of the current block while (p != nullptr && p->m_bBlock == 0) p = p->m_prev; - if ( p == nullptr ) return; + if (p == nullptr || pVar == nullptr) return; - CBotVar** pp = &p->m_listVar; - while ( *pp != nullptr ) pp = &(*pp)->m_next; - - *pp = pVar; // added after + p->m_listVar.emplace_back(pVar); } //////////////////////////////////////////////////////////////////////////////// @@ -369,19 +353,14 @@ void CBotCStack::CreateMemberVars(CBotClass* pClass, bool setDefined) bool CBotCStack::CheckVarLocal(CBotToken* &pToken) { CBotCStack* p = this; - std::string name = pToken->GetString(); + const auto& name = pToken->GetString(); - while (p != nullptr) + // find the level of the current block + while (p != nullptr && p->m_bBlock == 0) p = p->m_prev; + + if (p != nullptr) for (auto& var : p->m_listVar) { - CBotVar* pp = p->m_listVar; - while ( pp != nullptr) - { - if (name == pp->GetName()) - return true; - pp = pp->m_next; - } - if ( p->m_bBlock ) return false; - p = p->m_prev; + if (name == var->GetName()) return true; } return false; } @@ -392,10 +371,10 @@ CBotTypResult CBotCStack::CompileCall(CBotToken* &p, CBotVar** ppVars, long& nId nIdent = 0; CBotTypResult val(-1); - val = m_prog->GetExternalCalls()->CompileCall(p, nullptr, ppVars, this); + val = GetProgram()->GetExternalCalls()->CompileCall(p, nullptr, ppVars, this); if (val.GetType() < 0) { - val = CBotFunction::CompileCall(p->GetString(), ppVars, nIdent, m_prog); + val = CBotFunction::CompileCall(p->GetString(), ppVars, nIdent, GetProgram()); if ( val.GetType() < 0 ) { // pVar = nullptr; // the error is not on a particular parameter @@ -410,13 +389,13 @@ CBotTypResult CBotCStack::CompileCall(CBotToken* &p, CBotVar** ppVars, long& nId //////////////////////////////////////////////////////////////////////////////// bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam, const std::string& className) { - std::string name = pToken->GetString(); + const auto& name = pToken->GetString(); - if ( m_prog->GetExternalCalls()->CheckCall(name) ) return true; + if ( GetProgram()->GetExternalCalls()->CheckCall(name) ) return true; - for (CBotFunction* pp : m_prog->GetFunctions()) + for (CBotFunction* pp : GetProgram()->GetFunctions()) { - if ( pToken->GetString() == pp->GetName() ) + if ( name == pp->GetName() ) { // ignore methods for a different class if ( className != pp->GetClassName() ) @@ -429,7 +408,7 @@ bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam, const std:: for (CBotFunction* pp : CBotFunction::m_publicFunctions) { - if ( pToken->GetString() == pp->GetName() ) + if ( name == pp->GetName() ) { // ignore methods for a different class if ( className != pp->GetClassName() ) @@ -443,4 +422,4 @@ bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam, const std:: return false; } -} +} // namespace CBot diff --git a/src/CBot/CBotCStack.h b/src/CBot/CBotCStack.h index 983dcf42..adcbee59 100644 --- a/src/CBot/CBotCStack.h +++ b/src/CBot/CBotCStack.h @@ -22,6 +22,9 @@ #include "CBot/CBotVar/CBotVar.h" #include "CBot/CBotProgram.h" +#include +#include + namespace CBot { @@ -157,6 +160,11 @@ public: */ CBotCStack* TokenStack(CBotToken* pToken = nullptr, bool bBlock = false); + /*! + * \brief Deletes all subsequent stack frames created by TokenStack. + */ + void DeleteNext(); + /*! * \brief Return Transmits the result upper. * \param p @@ -269,21 +277,20 @@ public: bool NextToken(CBotToken* &p); private: - CBotCStack* m_next; + std::unique_ptr m_next; CBotCStack* m_prev; - static CBotError m_error; - static int m_end; - int m_start; + int m_errStart = 0; + + struct Data; + + CBotCStack::Data* m_data; //! Result of the operations. - CBotVar* m_var; + std::unique_ptr m_var; //! Is part of a block (variables are local to this block). bool m_bBlock; - CBotVar* m_listVar; - //! List of compiled functions. - static CBotProgram* m_prog; - static CBotTypResult m_retTyp; + std::list> m_listVar; }; } // namespace CBot diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index ccf47104..a7993a9a 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -605,8 +605,8 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) // return a method precompiled in pass 1 CBotCStack* pStk = pStack->TokenStack(nullptr, true); CBotDefParam* params = CBotDefParam::Compile(p, pStk ); - delete pStk; - std::list::iterator pfIter = std::find_if(m_pMethod.begin(), m_pMethod.end(), [&pp, ¶ms](CBotFunction* x) + pStack->DeleteNext(); + auto pfIter = std::find_if(m_pMethod.begin(), m_pMethod.end(), [&pp, ¶ms](CBotFunction* x) { return x->GetName() == pp && x->CheckParam( params ); }); @@ -626,7 +626,7 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) f->m_pProg = pStack->GetProgram(); f->m_bSynchro = bSynchro; } - pStack->Return(nullptr, pile); + pStack->DeleteNext(); } return pStack->IsOk(); diff --git a/src/CBot/CBotDefParam.cpp b/src/CBot/CBotDefParam.cpp index de3fd503..d25f9bda 100644 --- a/src/CBot/CBotDefParam.cpp +++ b/src/CBot/CBotDefParam.cpp @@ -94,7 +94,7 @@ CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack) prevHasDefault = true; } else pStack->SetError(CBotErrNoExpression, p); - delete pStk; + pStack->DeleteNext(); } else if (prevHasDefault) pStack->SetError(CBotErrDefaultValue, p->GetPrev()); @@ -137,6 +137,7 @@ bool CBotDefParam::Execute(CBotVar** ppVars, CBotStack* &pj) while ( p != nullptr ) { pile = pile->AddStack(); + if (pile->StackOver()) return pj->Return(pile); if (pile->GetState() == 1) // already done? { if (ppVars != nullptr && ppVars[i] != nullptr) ++i; diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index 9ed38000..963ca261 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -100,6 +100,7 @@ enum TokenId ID_STATIC, ID_PROTECTED, ID_PRIVATE, + ID_REPEAT, ID_INT, ID_FLOAT, ID_BOOLEAN, diff --git a/src/CBot/CBotExternalCall.cpp b/src/CBot/CBotExternalCall.cpp index 8862212b..5b1b4c73 100644 --- a/src/CBot/CBotExternalCall.cpp +++ b/src/CBot/CBotExternalCall.cpp @@ -82,19 +82,23 @@ int CBotExternalCallList::DoCall(CBotToken* token, CBotVar* thisVar, CBotVar** p CBotExternalCall* pt = m_list[token->GetString()].get(); - if (pStack->IsCallFinished()) return true; - CBotStack* pile = pStack->AddStackExternalCall(pt); + if (thisVar == nullptr && pStack->IsCallFinished()) return true; // only for non-method external call - // lists the parameters depending on the contents of the stack (pStackVar) - CBotVar* pVar = MakeListVars(ppVar, true); + // if this is a method call we need to use AddStack() + CBotStack* pile = (thisVar != nullptr) ? pStack->AddStack() : pStack->AddStackExternalCall(pt); - // creates a variable to the result - CBotVar* pResult = rettype.Eq(CBotTypVoid) ? nullptr : CBotVar::Create("", rettype); + if (pile->GetState() == 0) // the first time? + { + // lists the parameters depending on the contents of the stack + CBotVar* pVar = MakeListVars(ppVar, true); + pile->SetVar(pVar); - pile->SetVar(pVar); - - CBotStack* pile2 = pile->AddStack(); - pile2->SetVar(pResult); + CBotStack* pile2 = pile->AddStack(); + // creates a variable to the result + CBotVar* pResult = rettype.Eq(CBotTypVoid) ? nullptr : CBotVar::Create("", rettype); + pile2->SetVar(pResult); + pile->IncState(); // increment state to mark this step done + } pile->SetError(CBotNoErr, token); // save token for the position in case of error return pt->Run(thisVar, pStack); @@ -107,7 +111,8 @@ bool CBotExternalCallList::RestoreCall(CBotToken* token, CBotVar* thisVar, CBotV CBotExternalCall* pt = m_list[token->GetString()].get(); - CBotStack* pile = pStack->RestoreStackEOX(pt); + // if this is a method call we need to use RestoreStack() + CBotStack* pile = (thisVar != nullptr) ? pStack->RestoreStack() : pStack->RestoreStackEOX(pt); if (pile == nullptr) return true; pile->RestoreStack(); @@ -163,8 +168,7 @@ bool CBotExternalCallDefault::Run(CBotVar* thisVar, CBotStack* pStack) return false; } - if (result != nullptr) pStack->SetCopyVar(result); - + pStack->Return(pile2); // return 'result' and clear extra stack return true; } @@ -187,8 +191,8 @@ CBotTypResult CBotExternalCallClass::Compile(CBotVar* thisVar, CBotVar* args, vo bool CBotExternalCallClass::Run(CBotVar* thisVar, CBotStack* pStack) { - if (pStack->IsCallFinished()) return true; - CBotStack* pile = pStack->AddStackExternalCall(this); + assert(thisVar != nullptr); + CBotStack* pile = pStack->AddStack(); CBotVar* args = pile->GetVar(); CBotStack* pile2 = pile->AddStack(); @@ -207,9 +211,8 @@ bool CBotExternalCallClass::Run(CBotVar* thisVar, CBotStack* pStack) return false; } - if (result != nullptr) pStack->SetCopyVar(result); - + pStack->Return(pile2); // return 'result' and clear extra stack return true; } -} +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotDefClass.cpp b/src/CBot/CBotInstr/CBotDefClass.cpp index c7711703..f50196ed 100644 --- a/src/CBot/CBotInstr/CBotDefClass.cpp +++ b/src/CBot/CBotInstr/CBotDefClass.cpp @@ -135,7 +135,7 @@ CBotInstr* CBotDefClass::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* p // the constructor is there? // std::string noname; CBotTypResult r = pClass->CompileMethode(&token, var, ppVars, pStk, inst->m_nMethodeIdent); - delete pStk->TokenStack(); // releases the supplement stack + pStk->DeleteNext(); // releases the supplement stack int typ = r.GetType(); if (typ == CBotErrUndefCall) @@ -160,7 +160,7 @@ CBotInstr* CBotDefClass::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* p if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStk, true))) { inst->m_exprRetVar->SetToken(vartoken); - delete pStk->TokenStack(); + pStk->DeleteNext(); } pStk->SetVar(nullptr); @@ -360,6 +360,7 @@ bool CBotDefClass::Execute(CBotStack* &pj) if ( p != nullptr) while ( true ) { pile2 = pile2->AddStack(); // place on the stack for the results + if (pile2->StackOver()) return pj->Return(pile2); if ( pile2->GetState() == 0 ) { if (!p->Execute(pile2)) return false; // interrupted here? diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp index 478b9196..e4a62e95 100644 --- a/src/CBot/CBotInstr/CBotFunction.cpp +++ b/src/CBot/CBotInstr/CBotFunction.cpp @@ -445,7 +445,7 @@ bool CBotFunction::Execute(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInstance) pile->IncState(); } - if ( !m_block->Execute(pile) ) + if (!pile->GetRetVar(m_block->Execute(pile))) { if ( pile->GetError() < 0 ) pile->SetError( CBotNoErr ); diff --git a/src/CBot/CBotInstr/CBotInstr.cpp b/src/CBot/CBotInstr/CBotInstr.cpp index b6fbbc66..0115ba28 100644 --- a/src/CBot/CBotInstr/CBotInstr.cpp +++ b/src/CBot/CBotInstr/CBotInstr.cpp @@ -30,6 +30,7 @@ #include "CBot/CBotInstr/CBotExpression.h" #include "CBot/CBotInstr/CBotFor.h" #include "CBot/CBotInstr/CBotIf.h" +#include "CBot/CBotInstr/CBotRepeat.h" #include "CBot/CBotInstr/CBotReturn.h" #include "CBot/CBotInstr/CBotSwitch.h" #include "CBot/CBotInstr/CBotThrow.h" @@ -176,7 +177,7 @@ CBotInstr* CBotInstr::Compile(CBotToken* &p, CBotCStack* pStack) { type = pp->GetType(); // Allow only instructions that accept a label - if (!IsOfTypeList(pp, ID_WHILE, ID_FOR, ID_DO, 0)) + if (!IsOfTypeList(pp, ID_WHILE, ID_FOR, ID_DO, ID_REPEAT, 0)) { pStack->SetError(CBotErrLabel, pp->GetStart()); return nullptr; @@ -195,6 +196,9 @@ CBotInstr* CBotInstr::Compile(CBotToken* &p, CBotCStack* pStack) case ID_DO: return CBotDo::Compile(p, pStack); + case ID_REPEAT: + return CBotRepeat::Compile(p, pStack); + case ID_BREAK: case ID_CONTINUE: return CBotBreak::Compile(p, pStack); diff --git a/src/CBot/CBotInstr/CBotInstrCall.cpp b/src/CBot/CBotInstr/CBotInstrCall.cpp index 60689cb1..2274f3ac 100644 --- a/src/CBot/CBotInstr/CBotInstrCall.cpp +++ b/src/CBot/CBotInstr/CBotInstrCall.cpp @@ -78,12 +78,12 @@ CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack) { // if (pVar2!=nullptr) pp = pVar2->RetToken(); pStack->SetError( static_cast(inst->m_typRes.GetType()), pp ); - delete pStack->TokenStack(); + pStack->DeleteNext(); delete inst; return nullptr; } - delete pStack->TokenStack(); + pStack->DeleteNext(); if ( inst->m_typRes.GetType() > 0 ) { CBotVar* pRes = CBotVar::Create("", inst->m_typRes); @@ -94,7 +94,7 @@ CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack) if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack))) { inst->m_exprRetVar->SetToken(&inst->m_token); - delete pStack->TokenStack(); + pStack->DeleteNext(); } if ( !pStack->IsOk() ) { @@ -105,7 +105,7 @@ CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack) return inst; } p = pp; - delete pStack->TokenStack(); + pStack->DeleteNext(); return nullptr; } @@ -138,6 +138,7 @@ bool CBotInstrCall::Execute(CBotStack* &pj) if ( p != nullptr) while ( true ) { pile = pile->AddStack(); // place on the stack for the results + if (pile->StackOver()) return pj->Return(pile); if ( pile->GetState() == 0 ) { if (!p->Execute(pile)) return false; // interrupted here? diff --git a/src/CBot/CBotInstr/CBotInstrMethode.cpp b/src/CBot/CBotInstr/CBotInstrMethode.cpp index 12c50466..6b7045e3 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.cpp +++ b/src/CBot/CBotInstr/CBotInstrMethode.cpp @@ -70,7 +70,7 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* CBotClass* pClass = var->GetClass(); // pointer to the class inst->m_className = pClass->GetName(); // name of the class CBotTypResult r = pClass->CompileMethode(pp, var, ppVars, pStack, inst->m_MethodeIdent); - delete pStack->TokenStack(); // release parameters on the stack + pStack->DeleteNext(); // release parameters on the stack inst->m_typRes = r; if (inst->m_typRes.GetType() > 20) @@ -95,7 +95,7 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack, bMethodChain))) { inst->m_exprRetVar->SetToken(pp); - delete pStack->TokenStack(); + pStack->DeleteNext(); } if ( pStack->IsOk() ) @@ -164,6 +164,7 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre } ppVars[i++] = pile2->GetVar(); // construct the list of pointers pile2 = pile2->AddStack(); // space on the stack for the result + if (pile2->StackOver()) return pj->Return(pile2); p = p->GetNext(); if ( p == nullptr) break; } diff --git a/src/CBot/CBotInstr/CBotInstrUtils.cpp b/src/CBot/CBotInstr/CBotInstrUtils.cpp index 0b56267c..c4638d60 100644 --- a/src/CBot/CBotInstr/CBotInstrUtils.cpp +++ b/src/CBot/CBotInstr/CBotInstrUtils.cpp @@ -65,7 +65,7 @@ CBotInstr* CompileParams(CBotToken* &p, CBotCStack* pStack, CBotVar** ppVars) { if (pile->GetTypResult().Eq(99)) { - delete pStack->TokenStack(); + pStack->DeleteNext(); pStack->SetError(CBotErrVoid, p->GetStart()); return nullptr; } @@ -78,7 +78,7 @@ CBotInstr* CompileParams(CBotToken* &p, CBotCStack* pStack, CBotVar** ppVars) } pStack->SetError(CBotErrClosePar, p->GetStart()); - delete pStack->TokenStack(); + pStack->DeleteNext(); return nullptr; } } diff --git a/src/CBot/CBotInstr/CBotNew.cpp b/src/CBot/CBotInstr/CBotNew.cpp index 5f7539b1..dd8d04ac 100644 --- a/src/CBot/CBotInstr/CBotNew.cpp +++ b/src/CBot/CBotInstr/CBotNew.cpp @@ -87,7 +87,7 @@ CBotInstr* CBotNew::Compile(CBotToken* &p, CBotCStack* pStack) // constructor exist? CBotTypResult r = pClass->CompileMethode(&inst->m_vartoken, pVar, ppVars, pStk, inst->m_nMethodeIdent); - delete pStk->TokenStack(); // release extra stack + pStk->DeleteNext(); // release extra stack int typ = r.GetType(); // if there is no constructor, and no parameters either, it's ok @@ -115,7 +115,7 @@ CBotInstr* CBotNew::Compile(CBotToken* &p, CBotCStack* pStack) if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStk, true))) { inst->m_exprRetVar->SetToken(pp); - delete pStk->TokenStack(); + pStk->DeleteNext(); } if (pStack->IsOk()) @@ -189,6 +189,7 @@ bool CBotNew::Execute(CBotStack* &pj) if (p != nullptr) while ( true) { pile2 = pile2->AddStack(); // space on the stack for the result + if (pile2->StackOver()) return pj->Return(pile2); if (pile2->GetState() == 0) { if (!p->Execute(pile2)) return false; // interrupted here? diff --git a/src/CBot/CBotInstr/CBotRepeat.cpp b/src/CBot/CBotInstr/CBotRepeat.cpp new file mode 100644 index 00000000..8d8e0553 --- /dev/null +++ b/src/CBot/CBotInstr/CBotRepeat.cpp @@ -0,0 +1,165 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, 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/CBotInstr/CBotRepeat.h" + +#include "CBot/CBotInstr/CBotBlock.h" +#include "CBot/CBotInstr/CBotExpression.h" + +#include "CBot/CBotCStack.h" +#include "CBot/CBotStack.h" + +namespace CBot +{ + +CBotRepeat::CBotRepeat() +{ + m_expr = nullptr; + m_block = nullptr; +} + +CBotRepeat::~CBotRepeat() +{ + delete m_expr; + delete m_block; +} + +CBotInstr* CBotRepeat::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotRepeat* inst = new CBotRepeat(); // creates the object + CBotToken* pp = p; // preserves at the ^ token (starting position) + + if ( IsOfType( p, TokenTypVar ) && IsOfType( p, ID_DOTS ) ) + inst->m_label = pp->GetString(); // register the name of label + + inst->SetToken(p); + if (!IsOfType(p, ID_REPEAT)) return nullptr; // should never happen + + CBotCStack* pStk = pStack->TokenStack(pp); + + if ( IsOfType(p, ID_OPENPAR ) ) + { + CBotToken* ppp = p; // preserves the ^ token (starting position) + if ( nullptr != (inst->m_expr = CBotExpression::Compile( p, pStk )) ) + { + if ( pStk->GetType() < CBotTypLong ) + { + if ( IsOfType(p, ID_CLOSEPAR ) ) + { + IncLvl(inst->m_label); + inst->m_block = CBotBlock::CompileBlkOrInst( p, pStk, true ); + DecLvl(); + + if ( pStk->IsOk() ) // the statement block is ok (it may be empty!) + return pStack->Return(inst, pStk); + } + pStack->SetError(CBotErrClosePar, p->GetStart()); + } + pStk->SetStartError(ppp->GetStart()); + pStk->SetError(CBotErrBadType1, p->GetStart()); + } + pStack->SetError(CBotErrBadNum, p); + } + pStack->SetError(CBotErrOpenPar, p->GetStart()); + + delete inst; + return pStack->Return(nullptr, pStk); +} + +// execution of intruction "repeat" + +bool CBotRepeat::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); // adds an item to the stack + // or find in case of recovery + if ( pile->IfStep() ) return false; + + while( true ) switch( pile->GetState() ) // executes the loop + { // there are two possible states (depending on recovery) + case 0: + // evaluates the number of iterations + if ( !m_expr->Execute(pile) ) return false; // interrupted here ? + + // the result of the condition is on the stack + + // terminates if an error or if the condition is false + int n; + if ( !pile->IsOk() || ( n = pile->GetVal() ) < 1 ) + return pj->Return(pile); // releases the stack + + // puts the number of iterations +1 to the "state" + + if (!pile->SetState(n+1)) return false; // ready for further + continue; // continue as a result + + case 1: + // normal end of the loop + return pj->Return(pile); // releases the stack + + default: + // evaluates the associated statement block + if ( m_block != nullptr && !m_block->Execute(pile) ) + { + if (pile->IfContinue(pile->GetState()-1, m_label)) continue; // if continued, will return to test + return pj->BreakReturn(pile, m_label); // releases the stack + } + + // terminates if there is an error + if (!pile->IsOk()) return pj->Return(pile); // releases the stack + + // returns to the test again + if (!pile->SetState(pile->GetState()-1, 0)) return false; + continue; + } +} + +void CBotRepeat::RestoreState(CBotStack* &pj, bool bMain) +{ + if ( !bMain ) return; + CBotStack* pile = pj->RestoreStack(this); // adds an item to the stack + if ( pile == nullptr ) return; + + switch( pile->GetState() ) + { // there are two possible states (depending on recovery) + case 0: + // evaluates the condition + m_expr->RestoreState(pile, bMain); + return; + + case 1: + // evaluates the associated statement block + if ( m_block != nullptr ) m_block->RestoreState(pile, bMain); + return; + } +} + +std::string CBotRepeat::GetDebugData() +{ + return !m_label.empty() ? "m_label = " + m_label : ""; +} + +std::map CBotRepeat::GetDebugLinks() +{ + auto links = CBotInstr::GetDebugLinks(); + links["m_expr"] = m_expr; + links["m_block"] = m_block; + return links; +} + +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotRepeat.h b/src/CBot/CBotInstr/CBotRepeat.h new file mode 100644 index 00000000..d60f4a09 --- /dev/null +++ b/src/CBot/CBotInstr/CBotRepeat.h @@ -0,0 +1,62 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, 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/CBotInstr/CBotInstr.h" + +namespace CBot +{ + +/** + * \brief The "repeat" loop - repeat (times) { ... } + */ +class CBotRepeat : public CBotInstr +{ +public: + CBotRepeat(); + ~CBotRepeat(); + + /// Static method used for compilation + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + + /// Execute + bool Execute(CBotStack* &pj) override; + + /// Restore state + void RestoreState(CBotStack* &pj, bool bMain) override; + +protected: + virtual const std::string GetDebugName() override { return "CBotRepeat"; } + virtual std::string GetDebugData() override; + virtual std::map GetDebugLinks() override; + +private: + /// Number of iterations + CBotInstr* m_expr; + + /// Instructions + CBotInstr* m_block; + + /// Label + std::string m_label; // a label if there is + +}; + +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotSwitch.cpp b/src/CBot/CBotInstr/CBotSwitch.cpp index 9436407b..c75e9aa1 100644 --- a/src/CBot/CBotInstr/CBotSwitch.cpp +++ b/src/CBot/CBotInstr/CBotSwitch.cpp @@ -71,7 +71,7 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack) { if ( p->GetType() == ID_CASE || p->GetType() == ID_DEFAULT) { - delete pStk2; + pStk->DeleteNext(); pStk2 = pStk->TokenStack(p, true); // some space for a stack, plz caseInst = static_cast(CBotCase::Compile(p, pStk2, inst->m_labels)); diff --git a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp index a1339724..f57633d5 100644 --- a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp +++ b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp @@ -357,6 +357,7 @@ bool CBotTwoOpExpr::Execute(CBotStack* &pStack) CBotStack* pStk2 = pStk1->AddStack(); // adds an item to the stack // or return in case of recovery + if (pStk2->StackOver()) return pStack->Return(pStk2); // 2nd state, evalute right operand if ( pStk2->GetState() == 0 ) @@ -514,7 +515,6 @@ bool CBotTwoOpExpr::Execute(CBotStack* &pStack) pStk2->SetVar(result); // puts the result on the stack if ( err ) pStk2->SetError(err, &m_token); // and the possible error (division by zero) -// pStk1->Return(pStk2); // releases the stack return pStack->Return(pStk2); // transmits the result } diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index 428f1086..4a09eef2 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -34,7 +34,7 @@ namespace CBot { -CBotExternalCallList* CBotProgram::m_externalCalls = new CBotExternalCallList(); +std::unique_ptr CBotProgram::m_externalCalls; CBotProgram::CBotProgram() { @@ -243,13 +243,6 @@ CBotVar* CBotProgram::GetStackVars(std::string& functionName, int level) return m_stack->GetStackVars(functionName, level); } -//////////////////////////////////////////////////////////////////////////////// -void CBotProgram::SetTimer(int n) -{ - CBotStack::SetTimer( n ); -} - -//////////////////////////////////////////////////////////////////////////////// CBotError CBotProgram::GetError() { return m_error; @@ -395,6 +388,8 @@ int CBotProgram::GetVersion() void CBotProgram::Init() { + m_externalCalls.reset(new CBotExternalCallList); + CBotProgram::DefineNum("CBotErrZeroDiv", CBotErrZeroDiv); // division by zero CBotProgram::DefineNum("CBotErrNotInit", CBotErrNotInit); // uninitialized variable CBotProgram::DefineNum("CBotErrBadThrow", CBotErrBadThrow); // throw a negative value @@ -420,9 +415,10 @@ void CBotProgram::Free() CBotToken::ClearDefineNum(); m_externalCalls->Clear(); CBotClass::ClearPublic(); + m_externalCalls.reset(); } -CBotExternalCallList* CBotProgram::GetExternalCalls() +const std::unique_ptr& CBotProgram::GetExternalCalls() { return m_externalCalls; } diff --git a/src/CBot/CBotProgram.h b/src/CBot/CBotProgram.h index 8d7576f2..41ef6b8b 100644 --- a/src/CBot/CBotProgram.h +++ b/src/CBot/CBotProgram.h @@ -19,11 +19,12 @@ #pragma once -#include "CBot/CBotTypResult.h" #include "CBot/CBotEnums.h" -#include #include +#include +#include +#include namespace CBot { @@ -31,6 +32,7 @@ namespace CBot class CBotFunction; class CBotClass; class CBotStack; +class CBotTypResult; class CBotVar; class CBotExternalCallList; @@ -200,14 +202,6 @@ public: */ void Stop(); - /** - * \brief Sets the number of steps (parts of instructions) to execute in Run() before suspending the program execution - * \param n new timer value - * - * FIXME: Seems to be currently kind of broken (see issue #410) - */ - static void SetTimer(int n); - /** * \brief Add a function that can be called from CBot * @@ -335,11 +329,11 @@ public: /** * \brief Returns static list of all registered external calls */ - static CBotExternalCallList* GetExternalCalls(); + static const std::unique_ptr& GetExternalCalls(); private: //! All external calls - static CBotExternalCallList* m_externalCalls; + static std::unique_ptr m_externalCalls; //! All user-defined functions std::list m_functions{}; //! The entry point function diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp index d6377c7b..7de0670b 100644 --- a/src/CBot/CBotStack.cpp +++ b/src/CBot/CBotStack.cpp @@ -39,16 +39,24 @@ namespace CBot const int DEFAULT_TIMER = 100; -int CBotStack::m_initimer = DEFAULT_TIMER; -int CBotStack::m_timer = 0; -CBotVar* CBotStack::m_retvar = nullptr; -CBotError CBotStack::m_error = CBotNoErr; -int CBotStack::m_start = 0; -int CBotStack::m_end = 0; -std::string CBotStack::m_labelBreak=""; -void* CBotStack::m_pUser = nullptr; +struct CBotStack::Data +{ + int initimer = DEFAULT_TIMER; + int timer = 0; + + CBotError error = CBotNoErr; + int errStart = 0; + int errEnd = 0; + + std::string labelBreak = ""; + + CBotProgram* baseProg = nullptr; + CBotStack* topStack = nullptr; + void* pUser = nullptr; + + std::unique_ptr retvar; +}; -//////////////////////////////////////////////////////////////////////////////// CBotStack* CBotStack::AllocateStack() { CBotStack* p; @@ -73,7 +81,8 @@ CBotStack* CBotStack::AllocateStack() pp ++; } - m_error = CBotNoErr; // avoids deadlocks because m_error is static + p->m_data = new CBotStack::Data; + p->m_data->topStack = p; return p; } @@ -97,6 +106,7 @@ void CBotStack::Delete() CBotStack* p = m_prev; bool bOver = m_bOver; + if ( m_prev == nullptr ) delete m_data; // clears the freed block memset(this, 0, sizeof(CBotStack)); @@ -123,6 +133,7 @@ CBotStack* CBotStack::AddStack(CBotInstr* instr, BlockVisibilityType bBlock) while ( p->m_prev != nullptr ); m_next = p; // chain an element + p->m_data = m_data; p->m_block = bBlock; p->m_instr = instr; p->m_prog = m_prog; @@ -166,6 +177,7 @@ CBotStack* CBotStack::AddStack2(BlockVisibilityType bBlock) while ( p->m_prev != nullptr ); m_next2 = p; // chain an element + p->m_data = m_data; p->m_prev = this; p->m_block = bBlock; p->m_prog = m_prog; @@ -220,18 +232,32 @@ bool CBotStack::ReturnKeep(CBotStack* pfils) bool CBotStack::StackOver() { if (!m_bOver) return false; - m_error = CBotErrStackOver; + m_data->error = CBotErrStackOver; return true; } -//////////////////////////////////////////////////////////////////////////////// +CBotError CBotStack::GetError(int& start, int& end) +{ + start = m_data->errStart; + end = m_data->errEnd; + return m_data->error; +} + +CBotError CBotStack::GetError() +{ + return m_data->error; +} + +bool CBotStack::IsOk() +{ + return m_data->error == CBotNoErr; +} + void CBotStack::Reset() { - m_timer = m_initimer; // resets the timer - m_error = CBotNoErr; -// m_start = 0; -// m_end = 0; - m_labelBreak.clear(); + m_data->timer = m_data->initimer; // resets the timer + m_data->error = CBotNoErr; + m_data->labelBreak.clear(); } //////////////////////////////////////////////////////////////////////////////// @@ -258,35 +284,35 @@ CBotStack* CBotStack::RestoreStackEOX(CBotExternalCall* instr) // routine for execution step by step bool CBotStack::IfStep() { - if ( m_initimer > 0 || m_step++ > 0 ) return false; + if (m_data->initimer > 0 || m_step++ > 0) return false; return true; } //////////////////////////////////////////////////////////////////////////////// bool CBotStack::BreakReturn(CBotStack* pfils, const std::string& name) { - if ( m_error>=0 ) return false; // normal output - if ( m_error==CBotError(-3) ) return false; // normal output (return current) + if (m_data->error >= CBotNoErr) return false; // normal output + if (m_data->error == CBotError(-3)) return false; // normal output (return current) - if (!m_labelBreak.empty() && (name.empty() || m_labelBreak != name)) + if (!m_data->labelBreak.empty() && (name.empty() || m_data->labelBreak != name)) return false; // it's not for me - m_error = CBotNoErr; - m_labelBreak.clear(); + m_data->error = CBotNoErr; + m_data->labelBreak.clear(); return Return(pfils); } //////////////////////////////////////////////////////////////////////////////// bool CBotStack::IfContinue(int state, const std::string& name) { - if ( m_error != CBotError(-2) ) return false; + if (m_data->error != CBotError(-2)) return false; - if (!m_labelBreak.empty() && (name.empty() || m_labelBreak != name)) + if (!m_data->labelBreak.empty() && (name.empty() || m_data->labelBreak != name)) return false; // it's not for me m_state = state; // where again? - m_error = CBotNoErr; - m_labelBreak.clear(); + m_data->error = CBotNoErr; + m_data->labelBreak.clear(); if (m_next != nullptr) m_next->Delete(); // purge above stack return true; } @@ -294,11 +320,11 @@ bool CBotStack::IfContinue(int state, const std::string& name) //////////////////////////////////////////////////////////////////////////////// void CBotStack::SetBreak(int val, const std::string& name) { - m_error = static_cast(-val); // reacts as an Exception - m_labelBreak = name; + m_data->error = static_cast(-val); // reacts as an Exception + m_data->labelBreak = name; if (val == 3) // for a return { - m_retvar = m_var; + m_data->retvar.reset(m_var); m_var = nullptr; } } @@ -307,12 +333,11 @@ void CBotStack::SetBreak(int val, const std::string& name) //////////////////////////////////////////////////////////////////////////////// bool CBotStack::GetRetVar(bool bRet) { - if (m_error == CBotError(-3)) + if (m_data->error == CBotError(-3)) { if ( m_var ) delete m_var; - m_var = m_retvar; - m_retvar = nullptr; - m_error = CBotNoErr; + m_var = m_data->retvar.release(); + m_data->error = CBotNoErr; return true; } return bRet; // interrupted by something other than return @@ -322,7 +347,7 @@ bool CBotStack::GetRetVar(bool bRet) CBotVar* CBotStack::FindVar(CBotToken*& pToken, bool bUpdate) { CBotStack* p = this; - std::string name = pToken->GetString(); + const auto& name = pToken->GetString(); while (p != nullptr) { @@ -332,7 +357,7 @@ CBotVar* CBotStack::FindVar(CBotToken*& pToken, bool bUpdate) if (pp->GetName() == name) { if ( bUpdate ) - pp->Update(m_pUser); + pp->Update(m_data->pUser); return pp; } @@ -375,7 +400,7 @@ CBotVar* CBotStack::FindVar(long ident, bool bUpdate) if (pp->GetUniqNum() == ident) { if ( bUpdate ) - pp->Update(m_pUser); + pp->Update(m_data->pUser); return pp; } @@ -410,8 +435,8 @@ bool CBotStack::SetState(int n, int limite) { m_state = n; - m_timer--; // decrement the timer - return ( m_timer > limite ); // interrupted if timer pass + m_data->timer--; // decrement the timer + return (m_data->timer > limite); // interrupted if timer pass } //////////////////////////////////////////////////////////////////////////////// @@ -419,46 +444,46 @@ bool CBotStack::IncState(int limite) { m_state++; - m_timer--; // decrement the timer - return ( m_timer > limite ); // interrupted if timer pass + m_data->timer--; // decrement the timer + return (m_data->timer > limite); // interrupted if timer pass } //////////////////////////////////////////////////////////////////////////////// void CBotStack::SetError(CBotError n, CBotToken* token) { - if (n != CBotNoErr && m_error != CBotNoErr) return; // does not change existing error - m_error = n; + if (n != CBotNoErr && m_data->error != CBotNoErr) return; // does not change existing error + m_data->error = n; if (token != nullptr) { - m_start = token->GetStart(); - m_end = token->GetEnd(); + m_data->errStart = token->GetStart(); + m_data->errEnd = token->GetEnd(); } } //////////////////////////////////////////////////////////////////////////////// void CBotStack::ResetError(CBotError n, int start, int end) { - m_error = n; - m_start = start; - m_end = end; + m_data->error = n; + m_data->errStart = start; + m_data->errEnd = end; } //////////////////////////////////////////////////////////////////////////////// void CBotStack::SetPosError(CBotToken* token) { - m_start = token->GetStart(); - m_end = token->GetEnd(); + m_data->errStart = token->GetStart(); + m_data->errEnd = token->GetEnd(); } //////////////////////////////////////////////////////////////////////////////// void CBotStack::SetTimer(int n) { - m_initimer = n; + m_data->initimer = n; } int CBotStack::GetTimer() { - return m_initimer; + return m_data->initimer; } //////////////////////////////////////////////////////////////////////////////// @@ -542,26 +567,25 @@ void CBotStack::SetProgram(CBotProgram* p) { m_prog = p; m_func = IsFunction::YES; + if (this == m_data->topStack) m_data->baseProg = p; } //////////////////////////////////////////////////////////////////////////////// CBotProgram* CBotStack::GetProgram(bool bFirst) { if ( ! bFirst ) return m_prog; - CBotStack* p = this; - while ( p->m_prev != nullptr ) p = p->m_prev; - return p->m_prog; + return m_data->baseProg; } //////////////////////////////////////////////////////////////////////////////// void* CBotStack::GetUserPtr() { - return m_pUser; + return m_data->pUser; } void CBotStack::SetUserPtr(void* user) { - m_pUser = user; + m_data->pUser = user; } //////////////////////////////////////////////////////////////////////////////// @@ -908,13 +932,18 @@ bool CBotVar::RestoreState(std::istream &istr, CBotVar* &pVar) if (isClass && p == nullptr) // set id for each item in this instance { - CBotVar* pVars = pNew->GetItemList(); - CBotVar* pv = pNew->GetClass()->GetVar(); - while (pVars != nullptr && pv != nullptr) + CBotClass* pClass = pNew->GetClass(); + CBotVar* pVars = (static_cast(pNew))->m_pVar; + while (pClass != nullptr && pVars != nullptr) { - pVars->m_ident = pv->m_ident; - pv = pv->GetNext(); - pVars = pVars->GetNext(); + CBotVar* pv = pClass->GetVar(); + while (pVars != nullptr && pv != nullptr) + { + pVars->m_ident = pv->m_ident; + pVars = pVars->m_next; + pv = pv->m_next; + } + pClass = pClass->GetParent(); } } diff --git a/src/CBot/CBotStack.h b/src/CBot/CBotStack.h index 20e8118d..64a81124 100644 --- a/src/CBot/CBotStack.h +++ b/src/CBot/CBotStack.h @@ -82,9 +82,6 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** \name Error management - * - * BE CAREFUL - errors are stored in static variables! - * \todo Refactor that */ //@{ @@ -94,24 +91,21 @@ public: * \param[out] end Ending position in code of the error * \return Error number */ - CBotError GetError(int& start, int& end) { start = m_start; end = m_end; return m_error; } + CBotError GetError(int& start, int& end); /** * \brief Get last error * \return Error number * \see GetError(int&, int&) for error position in code */ - CBotError GetError() { return m_error; } + CBotError GetError(); /** * \brief Check if there was an error * \return false if an error occurred * \see GetError() */ - bool IsOk() - { - return m_error == CBotNoErr; - } + bool IsOk(); /** * \brief Set execution error unless it's already set unless you are trying to reset it @@ -357,7 +351,7 @@ public: /** * \todo Document * - * Copies the result value from static m_retvar (m_var at a moment of SetBreak(3)) to this stack result + * Copies the result value from m_data->retvar (m_var at a moment of SetBreak(3)) to this stack result */ bool GetRetVar(bool bRet); @@ -446,11 +440,11 @@ public: * * \todo Full documentation of the timer */ - static void SetTimer(int n); + void SetTimer(int n); /** * \brief Get the current configured maximum number of "timer ticks" (parts of instructions) to execute */ - static int GetTimer(); + int GetTimer(); /** * \brief Get current position in the program @@ -476,10 +470,10 @@ private: int m_state; int m_step; - static CBotError m_error; - static int m_start; - static int m_end; - static CBotVar* m_retvar; // result of a return + + struct Data; + + CBotStack::Data* m_data; CBotVar* m_var; // result of the operations CBotVar* m_listVar; // variables declared at this level @@ -489,11 +483,6 @@ private: //! CBotProgram instance the execution is in in this stack level CBotProgram* m_prog; - static int m_initimer; - static int m_timer; - static std::string m_labelBreak; - static void* m_pUser; - //! The corresponding instruction CBotInstr* m_instr; //! If this stack level holds a function call diff --git a/src/CBot/CBotToken.cpp b/src/CBot/CBotToken.cpp index 2b98b582..38402d94 100644 --- a/src/CBot/CBotToken.cpp +++ b/src/CBot/CBotToken.cpp @@ -65,6 +65,7 @@ static const boost::bimap KEYWORDS = makeBimapGetParent() != nullptr ) - { - // also creates an instance of the parent class - m_pParent = new CBotVarClass(name, CBotTypResult(type.GetType(), pClass->GetParent()) ); //, nIdent); - } SetClass( pClass ); @@ -82,11 +76,8 @@ CBotVarClass::~CBotVarClass( ) if ( m_CptUse != 0 ) assert(0); - if ( m_pParent ) delete m_pParent; - m_pParent = nullptr; - // removes the class list - m_instances.erase(this); + if (m_ItemIdent != 0) m_instances.erase(this); delete m_pVar; } @@ -113,10 +104,6 @@ void CBotVarClass::Copy(CBotVar* pSrc, bool bName) m_binit = p->m_binit; //- m_bStatic = p->m_bStatic; m_pClass = p->m_pClass; - if ( p->m_pParent ) - { - assert(0); // "que faire du pParent"; - } // m_next = nullptr; m_pUserPtr = p->m_pUserPtr; @@ -162,9 +149,11 @@ void CBotVarClass::SetClass(CBotClass* pClass)//, int &nIdent) if (pClass == nullptr) return; - CBotVar* pv = pClass->GetVar(); // first on a list - while ( pv != nullptr ) + CBotVar* pv = nullptr; + while (pClass != nullptr) { + if ( pv == nullptr ) pv = pClass->GetVar(); + if ( pv == nullptr ) { pClass = pClass->GetParent(); continue; } // seeks the maximum dimensions of the table CBotInstr* p = pv->m_LimExpr; // the different formulas if ( p != nullptr ) @@ -214,6 +203,7 @@ void CBotVarClass::SetClass(CBotClass* pClass)//, int &nIdent) if ( m_pVar == nullptr) m_pVar = pn; else m_pVar->AddNext( pn ); pv = pv->GetNext(); + if ( pv == nullptr ) pClass = pClass->GetParent(); } } @@ -246,7 +236,6 @@ CBotVar* CBotVarClass::GetItem(const std::string& name) p = p->GetNext(); } - if ( m_pParent != nullptr ) return m_pParent->GetItem(name); return nullptr; } @@ -261,7 +250,6 @@ CBotVar* CBotVarClass::GetItemRef(int nIdent) p = p->GetNext(); } - if ( m_pParent != nullptr ) return m_pParent->GetItemRef(nIdent); return nullptr; } @@ -311,32 +299,46 @@ std::string CBotVarClass::GetValString() { res = m_pClass->GetName() + std::string("( "); - CBotVarClass* my = this; - while ( my != nullptr ) + CBotClass* pClass = m_pClass; + long prevID = 0; { - CBotVar* pv = my->m_pVar; - while ( pv != nullptr ) + CBotVar* pv = m_pVar; + if (pv != nullptr) while (true) { + if (pv->GetUniqNum() < prevID) + { + pClass = pClass->GetParent(); + if (pClass == nullptr) break; + res += " ) extends "; + res += pClass->GetName(); + res += "( "; + if (pClass->GetVar() == nullptr) continue; + } + + prevID = pv->GetUniqNum(); + res += pv->GetName() + std::string("="); if ( pv->IsStatic() ) { - CBotVar* pvv = my->m_pClass->GetItem(pv->GetName()); - res += pvv->GetValString(); + res += pClass->GetItemRef(prevID)->GetValString(); } else { res += pv->GetValString(); } pv = pv->GetNext(); - if ( pv != nullptr ) res += ", "; + if ( pv == nullptr ) break; + if ( pv->GetUniqNum() > prevID ) res += ", "; } - my = my->m_pParent; - if ( my != nullptr ) + + if (pClass != nullptr) while (true) { - res += ") extends "; - res += my->m_pClass->GetName(); - res += " ("; + pClass = pClass->GetParent(); + if (pClass == nullptr) break; + res += " ) extends "; + res += pClass->GetName(); + res += "( "; } } @@ -378,14 +380,7 @@ void CBotVarClass::DecrementUse() { m_CptUse++; // does not return to the destructor - // m_error is static in the stack - // saves the value for return - CBotError err; - int start, end; - CBotStack* pile = nullptr; - err = pile->GetError(start,end); // stack == nullptr it does not bother! - - pile = CBotStack::AllocateStack(); // clears the error + CBotStack* pile = CBotStack::AllocateStack(); CBotVar* ppVars[1]; ppVars[0] = nullptr; @@ -399,8 +394,6 @@ void CBotVarClass::DecrementUse() while ( pile->IsOk() && !m_pClass->ExecuteMethode(ident, pThis, ppVars, CBotTypResult(CBotTypVoid), pile, &token)) ; // waits for the end - pile->ResetError(err, start,end); - pile->Delete(); delete pThis; m_CptUse--; diff --git a/src/CBot/CBotVar/CBotVarClass.h b/src/CBot/CBotVar/CBotVarClass.h index 6e98f62e..ac462d99 100644 --- a/src/CBot/CBotVar/CBotVarClass.h +++ b/src/CBot/CBotVar/CBotVarClass.h @@ -99,8 +99,6 @@ private: static std::set m_instances; //! Class definition CBotClass* m_pClass; - //! Parent class instance - CBotVarClass* m_pParent; //! Class members CBotVar* m_pVar; //! Reference counter diff --git a/src/CBot/CMakeLists.txt b/src/CBot/CMakeLists.txt index 359175f0..0ce74d69 100644 --- a/src/CBot/CMakeLists.txt +++ b/src/CBot/CMakeLists.txt @@ -110,6 +110,8 @@ target_sources(CBot PRIVATE CBotInstr/CBotPostIncExpr.h CBotInstr/CBotPreIncExpr.cpp CBotInstr/CBotPreIncExpr.h + CBotInstr/CBotRepeat.cpp + CBotInstr/CBotRepeat.h CBotInstr/CBotReturn.cpp CBotInstr/CBotReturn.h CBotInstr/CBotSwitch.cpp diff --git a/src/CBot/stdlib/MathFunctions.cpp b/src/CBot/stdlib/MathFunctions.cpp index b51d5748..f6e5b484 100644 --- a/src/CBot/stdlib/MathFunctions.cpp +++ b/src/CBot/stdlib/MathFunctions.cpp @@ -144,13 +144,36 @@ bool rRand(CBotVar* var, CBotVar* result, int& exception, void* user) bool rAbs(CBotVar* var, CBotVar* result, int& exception, void* user) { - float value; + switch (result->GetType()) + { + case CBotTypDouble: + *result = fabs(var->GetValDouble()); + break; + case CBotTypFloat: + *result = fabs(var->GetValFloat()); + break; + case CBotTypLong: + *result = labs(var->GetValLong()); + break; + default: + *result = abs(var->GetValInt()); + break; + } - value = var->GetValFloat(); - result->SetValFloat(fabs(value)); return true; } +CBotTypResult cAbs(CBotVar* &var, void* user) +{ + if ( var == nullptr ) return CBotTypResult(CBotErrLowParam); + if ( var->GetType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + + CBotTypResult returnType(var->GetType()); + var = var->GetNext(); + if ( var != nullptr ) return CBotTypResult(CBotErrOverParam); + return returnType; +} + // Instruction "floor()" bool rFloor(CBotVar* var, CBotVar* result, int& exception, void* user) @@ -209,7 +232,7 @@ void InitMathFunctions() CBotProgram::AddFunction("sqrt", rSqrt, cOneFloat); CBotProgram::AddFunction("pow", rPow, cTwoFloat); CBotProgram::AddFunction("rand", rRand, cNull); - CBotProgram::AddFunction("abs", rAbs, cOneFloat); + CBotProgram::AddFunction("abs", rAbs, cAbs); CBotProgram::AddFunction("floor", rFloor, cOneFloat); CBotProgram::AddFunction("ceil", rCeil, cOneFloat); CBotProgram::AddFunction("round", rRound, cOneFloat); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c578eb14..4560bfcf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,8 @@ add_library(colobotbase STATIC app/controller.h app/input.cpp app/input.h + app/modman.cpp + app/modman.h app/pathman.cpp app/pathman.h app/pausemanager.cpp @@ -297,6 +299,7 @@ add_library(colobotbase STATIC object/object_interface_type.h object/object_manager.cpp object/object_manager.h + object/object_type.cpp object/object_type.h object/old_object.cpp object/old_object.h @@ -446,6 +449,8 @@ add_library(colobotbase STATIC ui/screen/screen_loading.h ui/screen/screen_main_menu.cpp ui/screen/screen_main_menu.h + ui/screen/screen_mod_list.cpp + ui/screen/screen_mod_list.h ui/screen/screen_player_select.cpp ui/screen/screen_player_select.h ui/screen/screen_quit.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index 2d460604..1e4515ed 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -21,6 +21,7 @@ #include "app/controller.h" #include "app/input.h" +#include "app/modman.h" #include "app/pathman.h" #include "common/config_file.h" @@ -113,7 +114,8 @@ CApplication::CApplication(CSystemUtils* systemUtils) m_private(MakeUnique()), m_configFile(MakeUnique()), m_input(MakeUnique()), - m_pathManager(MakeUnique(systemUtils)) + m_pathManager(MakeUnique(systemUtils)), + m_modManager(MakeUnique(this, m_pathManager.get())) { m_exitCode = 0; m_active = false; @@ -220,6 +222,11 @@ CSoundInterface* CApplication::GetSound() return m_sound.get(); } +CModManager* CApplication::GetModManager() +{ + return m_modManager.get(); +} + void CApplication::LoadEnvironmentVariables() { auto dataDir = m_systemUtils->GetEnvVar("COLOBOT_DATA_DIR"); @@ -513,6 +520,10 @@ bool CApplication::Create() GetLogger()->Warn("Config could not be loaded. Default values will be used!\n"); } + m_modManager->FindMods(); + m_modManager->SaveMods(); + m_modManager->MountAllMods(); + // Create the sound instance. #ifdef OPENAL_SOUND if (!m_headless) @@ -538,6 +549,8 @@ bool CApplication::Create() /* SDL initialization sequence */ + // Creating the m_engine now because it holds the vsync flag + m_engine = MakeUnique(this, m_systemUtils); Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_TIMER; @@ -682,8 +695,6 @@ bool CApplication::Create() } // Create the 3D engine - m_engine = MakeUnique(this, m_systemUtils); - m_engine->SetDevice(m_device.get()); if (! m_engine->Create() ) @@ -698,21 +709,7 @@ bool CApplication::Create() // Create the robot application. m_controller = MakeUnique(); - CThread musicLoadThread([this]() - { - GetLogger()->Debug("Cache sounds...\n"); - SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); - m_systemUtils->GetCurrentTimeStamp(musicLoadStart); - - m_sound->CacheAll(); - - SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp(); - 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"); - musicLoadThread.Start(); + StartLoadingMusic(); if (m_runSceneCategory == LevelCategory::Max) m_controller->StartApp(); @@ -726,6 +723,15 @@ bool CApplication::Create() return true; } +void CApplication::ReloadResources() +{ + GetLogger()->Info("Reloading resources\n"); + m_engine->ReloadAllTextures(); + StartLoadingMusic(); + m_controller->GetRobotMain()->UpdateCustomLevelList(); +} + + bool CApplication::CreateVideoSurface() { Uint32 videoFlags = SDL_WINDOW_OPENGL; @@ -844,24 +850,9 @@ bool CApplication::CreateVideoSurface() int vsync = 0; if (GetConfigFile().GetIntProperty("Setup", "VSync", vsync)) { - while (SDL_GL_SetSwapInterval(vsync) == -1) - { - switch(vsync) - { - case -1: //failed with adaptive sync? - GetLogger()->Warn("Adaptive sync not supported.\n"); - vsync = 1; - break; - case 1: //failed with VSync enabled? - GetLogger()->Warn("Couldn't enable VSync.\n"); - vsync = 0; - break; - case 0: //failed with VSync disabled? - GetLogger()->Warn("Couldn't disable VSync.\n"); - vsync = 1; - break; - } - } + m_engine->SetVSync(vsync); + TryToSetVSync(); + vsync = m_engine->GetVSync(); GetConfigFile().SetIntProperty("Setup", "VSync", vsync); GetLogger()->Info("Using Vsync: %s\n", (vsync == -1 ? "adaptive" : (vsync ? "true" : "false"))); @@ -870,6 +861,32 @@ bool CApplication::CreateVideoSurface() return true; } +void CApplication::TryToSetVSync() +{ + int vsync = m_engine->GetVSync(); + int result = SDL_GL_SetSwapInterval(vsync); + if (result == -1) + { + switch (vsync) + { + case -1: + GetLogger()->Warn("Adaptive sync not supported: %s\n", SDL_GetError()); + m_engine->SetVSync(1); + TryToSetVSync(); + break; + case 1: + GetLogger()->Warn("Couldn't enable VSync: %s\n", SDL_GetError()); + m_engine->SetVSync(0); + TryToSetVSync(); + break; + case 0: + GetLogger()->Warn("Couldn't disable VSync: %s\n", SDL_GetError()); + m_engine->SetVSync(SDL_GL_GetSwapInterval()); + break; + } + } +} + bool CApplication::ChangeVideoConfig(const Gfx::DeviceConfig &newConfig) { m_deviceConfig = newConfig; @@ -878,26 +895,7 @@ bool CApplication::ChangeVideoConfig(const Gfx::DeviceConfig &newConfig) SDL_SetWindowSize(m_private->window, m_deviceConfig.size.x, m_deviceConfig.size.y); SDL_SetWindowFullscreen(m_private->window, m_deviceConfig.fullScreen ? SDL_WINDOW_FULLSCREEN : 0); - int vsync = m_engine->GetVSync(); - while (SDL_GL_SetSwapInterval(vsync) == -1) - { - switch(vsync) - { - case -1: //failed with adaptive sync? - GetLogger()->Warn("Adaptive sync not supported.\n"); - vsync = 1; - break; - case 1: //failed with VSync enabled? - GetLogger()->Warn("Couldn't enable VSync.\n"); - vsync = 0; - break; - case 0: //failed with VSync disabled? - GetLogger()->Warn("Couldn't disable VSync.\n"); - vsync = 1; - break; - } - } - m_engine->SetVSync(vsync); + TryToSetVSync(); m_device->ConfigChanged(m_deviceConfig); @@ -1060,6 +1058,10 @@ int CApplication::Run() MoveMouse(Math::Point(0.5f, 0.5f)); // center mouse on start + SystemTimeStamp *previousTimeStamp = m_systemUtils->CreateTimeStamp(); + SystemTimeStamp *currentTimeStamp = m_systemUtils->CreateTimeStamp(); + SystemTimeStamp *interpolatedTimeStamp = m_systemUtils->CreateTimeStamp(); + while (true) { if (m_active) @@ -1158,21 +1160,30 @@ int CApplication::Run() CProfiler::StartPerformanceCounter(PCNT_UPDATE_ALL); - // Prepare and process step simulation event - Event event = CreateUpdateEvent(); - if (event.type != EVENT_NULL && m_controller != nullptr) + // Prepare and process step simulation event(s) + // If game speed is increased then we do extra ticks per loop iteration to improve physics accuracy. + int numTickSlices = static_cast(GetSimulationSpeed()); + if(numTickSlices < 1) numTickSlices = 1; + m_systemUtils->CopyTimeStamp(previousTimeStamp, m_curTimeStamp); + m_systemUtils->GetCurrentTimeStamp(currentTimeStamp); + for(int tickSlice = 0; tickSlice < numTickSlices; tickSlice++) { - LogEvent(event); + m_systemUtils->InterpolateTimeStamp(interpolatedTimeStamp, previousTimeStamp, currentTimeStamp, (tickSlice+1)/static_cast(numTickSlices)); + Event event = CreateUpdateEvent(interpolatedTimeStamp); + if (event.type != EVENT_NULL && m_controller != nullptr) + { + LogEvent(event); - m_sound->FrameMove(m_relTime); + m_sound->FrameMove(m_relTime); - CProfiler::StartPerformanceCounter(PCNT_UPDATE_GAME); - m_controller->ProcessEvent(event); - CProfiler::StopPerformanceCounter(PCNT_UPDATE_GAME); + CProfiler::StartPerformanceCounter(PCNT_UPDATE_GAME); + m_controller->ProcessEvent(event); + CProfiler::StopPerformanceCounter(PCNT_UPDATE_GAME); - CProfiler::StartPerformanceCounter(PCNT_UPDATE_ENGINE); - m_engine->FrameUpdate(); - CProfiler::StopPerformanceCounter(PCNT_UPDATE_ENGINE); + CProfiler::StartPerformanceCounter(PCNT_UPDATE_ENGINE); + m_engine->FrameUpdate(); + CProfiler::StopPerformanceCounter(PCNT_UPDATE_ENGINE); + } } CProfiler::StopPerformanceCounter(PCNT_UPDATE_ALL); @@ -1188,6 +1199,10 @@ int CApplication::Run() } end: + m_systemUtils->DestroyTimeStamp(previousTimeStamp); + m_systemUtils->DestroyTimeStamp(currentTimeStamp); + m_systemUtils->DestroyTimeStamp(interpolatedTimeStamp); + return m_exitCode; } @@ -1520,6 +1535,26 @@ void CApplication::InternalResumeSimulation() m_absTimeBase = m_exactAbsTime; } +void CApplication::StartLoadingMusic() +{ + CThread musicLoadThread([this]() + { + GetLogger()->Debug("Cache sounds...\n"); + SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); + m_systemUtils->GetCurrentTimeStamp(musicLoadStart); + + m_sound->Reset(); + m_sound->CacheAll(); + + SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp(); + 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"); + musicLoadThread.Start(); +} + bool CApplication::GetSimulationSuspended() const { return m_simulationSuspended; @@ -1529,20 +1564,20 @@ void CApplication::SetSimulationSpeed(float speed) { m_simulationSpeed = speed; - m_systemUtils->GetCurrentTimeStamp(m_baseTimeStamp); + m_systemUtils->CopyTimeStamp(m_baseTimeStamp, m_curTimeStamp); m_realAbsTimeBase = m_realAbsTime; m_absTimeBase = m_exactAbsTime; GetLogger()->Info("Simulation speed = %.2f\n", speed); } -Event CApplication::CreateUpdateEvent() +Event CApplication::CreateUpdateEvent(SystemTimeStamp *newTimeStamp) { if (m_simulationSuspended) return Event(EVENT_NULL); m_systemUtils->CopyTimeStamp(m_lastTimeStamp, m_curTimeStamp); - m_systemUtils->GetCurrentTimeStamp(m_curTimeStamp); + m_systemUtils->CopyTimeStamp(m_curTimeStamp, newTimeStamp); long long absDiff = m_systemUtils->TimeStampExactDiff(m_baseTimeStamp, m_curTimeStamp); long long newRealAbsTime = m_realAbsTimeBase + absDiff; diff --git a/src/app/app.h b/src/app/app.h index c0d48a1c..d238bde9 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -42,6 +42,7 @@ class CEventQueue; class CController; class CSoundInterface; class CInput; +class CModManager; class CPathManager; class CConfigFile; class CSystemUtils; @@ -162,6 +163,8 @@ public: CEventQueue* GetEventQueue(); //! Returns the sound subsystem CSoundInterface* GetSound(); + //! Returns the mod manager + CModManager* GetModManager(); public: //! Loads some data from environment variables @@ -170,6 +173,8 @@ public: ParseArgsStatus ParseArguments(int argc, char *argv[]); //! Initializes the application bool Create(); + //! Reloads the application resources, e.g. mods + void ReloadResources(); //! Main event loop int Run(); //! Returns the code to be returned at main() exit @@ -283,13 +288,16 @@ public: protected: //! Creates the window's SDL_Surface bool CreateVideoSurface(); + //! Tries to set the SDL vsync state desired by the 3D engine + //! The final state of SDL vsync is set in the 3D engine afterwards + void TryToSetVSync(); //! Processes the captured SDL event to Event struct Event ProcessSystemEvent(); //! If applicable, creates a virtual event to match the changed state as of new event Event CreateVirtualEvent(const Event& sourceEvent); //! Prepares a simulation update event - TEST_VIRTUAL Event CreateUpdateEvent(); + TEST_VIRTUAL Event CreateUpdateEvent(SystemTimeStamp *newTimeStamp); //! Logs debug data for event void LogEvent(const Event& event); @@ -301,6 +309,9 @@ protected: //! Internal procedure to reset time counters void InternalResumeSimulation(); + //! Loads music in a new thread + void StartLoadingMusic(); + protected: //! System utils instance CSystemUtils* m_systemUtils; @@ -322,6 +333,8 @@ protected: std::unique_ptr m_input; //! Path manager std::unique_ptr m_pathManager; + //! Mod manager + std::unique_ptr m_modManager; //! Code to return at exit int m_exitCode; diff --git a/src/app/modman.cpp b/src/app/modman.cpp new file mode 100644 index 00000000..42ba11be --- /dev/null +++ b/src/app/modman.cpp @@ -0,0 +1,358 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, 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 "app/modman.h" + +#include "common/config.h" + +#include "app/app.h" +#include "app/pathman.h" + +#include "common/config_file.h" +#include "common/logger.h" + +#include "common/resources/resourcemanager.h" + +#include "level/parser/parser.h" + +#include +#include +#include +#include + +using namespace boost::filesystem; + +CModManager::CModManager(CApplication* app, CPathManager* pathManager) + : m_app{app}, + m_pathManager{pathManager} +{ +} + +void CModManager::FindMods() +{ + m_mods.clear(); + m_userChanges = false; + + // Load names from the config file + std::vector savedModNames; + GetConfigFile().GetArrayProperty("Mods", "Names", savedModNames); + std::vector savedEnabled; + GetConfigFile().GetArrayProperty("Mods", "Enabled", savedEnabled); + + // Transform the data into Mod structures + m_mods.reserve(savedModNames.size()); + for (size_t i = 0; i < savedModNames.size(); ++i) + { + Mod mod{}; + mod.name = savedModNames[i]; + if (i < savedEnabled.size()) + { + mod.enabled = savedEnabled[i]; + } + mod.path = ""; // Find the path later + m_mods.push_back(mod); + } + + // Search the folders for mods + auto rawPaths = m_pathManager->FindMods(); + std::map modPaths; + for (const auto& path : rawPaths) + { + auto modName = boost::filesystem::path(path).stem().string(); + modPaths.insert(std::make_pair(modName, path)); + } + + // Find paths for already saved mods + auto it = m_mods.begin(); + while (it != m_mods.end()) + { + auto& mod = *it; + const auto pathsIt = modPaths.find(mod.name); + if (pathsIt != modPaths.end()) + { + mod.path = (*pathsIt).second; + modPaths.erase(pathsIt); + ++it; + } + else + { + GetLogger()->Warn("Could not find mod %s, removing it from the list\n", mod.name.c_str()); + it = m_mods.erase(it); + } + } + + // Add the remaining found mods to the end of the list + for (const auto& newMod : modPaths) + { + Mod mod{}; + mod.name = newMod.first; + mod.path = newMod.second; + m_mods.push_back(mod); + } + + // Load the metadata for each mod + + // Unfortunately, the paths are distinguished by their real paths, not mount points + // So we must unmount mods temporarily + for (const auto& path : m_mountedModPaths) + { + UnmountMod(path); + } + + for (auto& mod : m_mods) + { + MountMod(mod, "/temp/mod"); + LoadModData(mod); + UnmountMod(mod); + } + + // Mount back + for (const auto& path : m_mountedModPaths) + { + MountMod(path); + } +} + +void CModManager::ReloadMods() +{ + UnmountAllMountedMods(); + MountAllMods(); + ReloadResources(); +} + +void CModManager::EnableMod(size_t i) +{ + m_mods[i].enabled = true; + m_userChanges = true; +} + +void CModManager::DisableMod(size_t i) +{ + m_mods[i].enabled = false; + m_userChanges = true; +} + +size_t CModManager::MoveUp(size_t i) +{ + if (i != 0) + { + std::swap(m_mods[i - 1], m_mods[i]); + m_userChanges = true; + return i - 1; + } + else + { + return i; + } +} + +size_t CModManager::MoveDown(size_t i) +{ + if (i != m_mods.size() - 1) + { + std::swap(m_mods[i], m_mods[i + 1]); + m_userChanges = true; + return i + 1; + } + else + { + return i; + } +} + +bool CModManager::Changes() +{ + std::vector paths; + for (const auto& mod : m_mods) + { + if (mod.enabled) + { + paths.push_back(mod.path); + } + } + return paths != m_mountedModPaths || m_userChanges; +} + +void CModManager::MountAllMods() +{ + for (const auto& mod : m_mods) + { + if (mod.enabled) + { + MountMod(mod); + m_mountedModPaths.push_back(mod.path); + } + } +} + +void CModManager::ReloadResources() +{ + m_app->ReloadResources(); +} + +void CModManager::SaveMods() +{ + std::vector savedNames; + savedNames.reserve(m_mods.size()); + std::transform(m_mods.begin(), m_mods.end(), std::back_inserter(savedNames), [](const Mod& mod) { return mod.name; }); + GetConfigFile().SetArrayProperty("Mods", "Names", savedNames); + + std::vector savedEnabled; + savedEnabled.reserve(m_mods.size()); + std::transform(m_mods.begin(), m_mods.end(), std::back_inserter(savedEnabled), [](const Mod& mod) { return mod.enabled; }); + GetConfigFile().SetArrayProperty("Mods", "Enabled", savedEnabled); + + GetConfigFile().Save(); + + m_userChanges = false; +} + +size_t CModManager::CountMods() const +{ + return m_mods.size(); +} + +const Mod& CModManager::GetMod(size_t i) const +{ + return m_mods[i]; +} + +const std::vector& CModManager::GetMods() const +{ + return m_mods; +} + +void CModManager::LoadModData(Mod& mod) +{ + auto& data = mod.data; + + data.displayName = mod.name; + + try + { + CLevelParser levelParser("temp/mod/manifest.txt"); + if (levelParser.Exists()) + { + levelParser.Load(); + + CLevelParserLine* line = nullptr; + + // DisplayName + line = levelParser.GetIfDefined("DisplayName"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.displayName = line->GetParam("text")->AsString(); + } + + // Author + line = levelParser.GetIfDefined("Author"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.author = line->GetParam("text")->AsString(); + } + + // Version + line = levelParser.GetIfDefined("Version"); + if (line != nullptr) + { + if (line->GetParam("text")->IsDefined()) + { + data.version = line->GetParam("text")->AsString(); + } + else if (line->GetParam("major")->IsDefined() && line->GetParam("minor")->IsDefined() && line->GetParam("patch")->IsDefined()) + { + auto major = boost::lexical_cast(line->GetParam("major")->AsInt()); + auto minor = boost::lexical_cast(line->GetParam("minor")->AsInt()); + auto patch = boost::lexical_cast(line->GetParam("patch")->AsInt()); + data.version = boost::algorithm::join(std::vector{ major, minor, patch }, "."); + } + } + + // Website + line = levelParser.GetIfDefined("Website"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.website = line->GetParam("text")->AsString(); + } + + // Summary + line = levelParser.GetIfDefined("Summary"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.summary = line->GetParam("text")->AsString(); + } + } + else + { + GetLogger()->Warn("No manifest file for mod %s\n", mod.name.c_str()); + } + } + catch (CLevelParserException& e) + { + GetLogger()->Warn("Failed parsing manifest for mod %s: %s\n", mod.name.c_str(), e.what()); + } + + // Changes + data.changes = CResourceManager::ListDirectories("temp/mod"); + auto levelsIt = std::find(data.changes.begin(), data.changes.end(), "levels"); + if (levelsIt != data.changes.end()) + { + auto levelsDirs = CResourceManager::ListDirectories("temp/mod/levels"); + if (!levelsDirs.empty()) + { + std::transform(levelsDirs.begin(), levelsDirs.end(), levelsDirs.begin(), [](const std::string& dir) { return "levels/" + dir; }); + levelsIt = data.changes.erase(levelsIt); + data.changes.insert(levelsIt, levelsDirs.begin(), levelsDirs.end()); + } + } +} + +void CModManager::MountMod(const Mod& mod, const std::string& mountPoint) +{ + MountMod(mod.path, mountPoint); +} + +void CModManager::MountMod(const std::string& path, const std::string& mountPoint) +{ + GetLogger()->Debug("Mounting mod: '%s' at path %s\n", path.c_str(), mountPoint.c_str()); + CResourceManager::AddLocation(path, true, mountPoint); +} + +void CModManager::UnmountMod(const Mod& mod) +{ + UnmountMod(mod.path); +} + +void CModManager::UnmountMod(const std::string& path) +{ + if (CResourceManager::LocationExists(path)) + { + GetLogger()->Debug("Unmounting mod: '%s'\n", path.c_str()); + CResourceManager::RemoveLocation(path); + } +} + +void CModManager::UnmountAllMountedMods() +{ + for (const auto& path : m_mountedModPaths) + { + UnmountMod(path); + } + m_mountedModPaths.clear(); +} diff --git a/src/app/modman.h b/src/app/modman.h new file mode 100644 index 00000000..09f8b0aa --- /dev/null +++ b/src/app/modman.h @@ -0,0 +1,124 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, 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 +#include +#include + +class CApplication; +class CPathManager; + +struct ModData +{ + std::string displayName{}; + std::string author{}; + std::string version{}; + std::string website{}; + std::string summary{}; + std::vector changes{}; +}; + +struct Mod +{ + std::string name{}; + std::string path{}; + bool enabled = false; + ModData data{}; +}; + +/** + * \class CModManager + * \brief This class handles the list of mods. + * + * The order matters since the order in which files are loaded matters, + * because some files can be overwritten. + * + * The changes in the list do not immediately apply. + */ +class CModManager +{ +public: + CModManager(CApplication* app, CPathManager* pathManager); + + //! Finds all the mods along with their metadata + void FindMods(); + + //! Applies the current configuration and reloads the application + void ReloadMods(); + + //! Removes a mod from the list of loaded mods + void EnableMod(size_t i); + + //! Adds a mod to the list of loaded mods + void DisableMod(size_t i); + + //! Moves the selected mod up in the list so that it's loaded sooner than others, returns the new index + size_t MoveUp(size_t i); + + //! Moves the selected mod down in the list so that it's loaded later than others, returns the new index + size_t MoveDown(size_t i); + + //! Checks if the list of currently used mods differs from the current configuration or there were changes made by the user + bool Changes(); + + //! Saves the current configuration of mods to the config file + void SaveMods(); + + //! Number of mods loaded + size_t CountMods() const; + + //! Returns the reference to the mod in given position + const Mod& GetMod(size_t i) const; + + //! Returns the list of mods + const std::vector& GetMods() const; + +private: + // Allow access to MountAllMods() as CApplication doesn't want to reload itself during initialization + friend CApplication; + + //! Reloads application resources so the enabled mods are applied + void ReloadResources(); + + //! Load mod data into mod + void LoadModData(Mod& mod); + + //! Updates the paths in Path Manager according to the current mod configuration + void MountAllMods(); + + void MountMod(const Mod& mod, const std::string& mountPoint = ""); + void MountMod(const std::string& path, const std::string& mountPoint = ""); + void UnmountMod(const Mod& mod); + void UnmountMod(const std::string& path); + void UnmountAllMountedMods(); + +private: + CApplication* m_app; + CPathManager* m_pathManager; + + //! Paths to mods already in the virtual filesystem + std::vector m_mountedModPaths; + + //! List of mods + std::vector m_mods; + + bool m_userChanges = false; +}; diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index 690f6d34..e4c385dd 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -41,8 +41,7 @@ CPathManager::CPathManager(CSystemUtils* systemUtils) : m_dataPath(systemUtils->GetDataPath()) , m_langPath(systemUtils->GetLangPath()) , m_savePath(systemUtils->GetSaveDir()) - , m_modAutoloadDir{} - , m_mods{} + , m_modSearchDirs{} { } @@ -65,16 +64,6 @@ void CPathManager::SetSavePath(const std::string &savePath) m_savePath = savePath; } -void CPathManager::AddModAutoloadDir(const std::string &modAutoloadDirPath) -{ - m_modAutoloadDir.push_back(modAutoloadDirPath); -} - -void CPathManager::AddMod(const std::string &modPath) -{ - m_mods.push_back(modPath); -} - const std::string& CPathManager::GetDataPath() { return m_dataPath; @@ -131,40 +120,18 @@ void CPathManager::InitPaths() GetLogger()->Info("Data path: %s\n", m_dataPath.c_str()); GetLogger()->Info("Save path: %s\n", m_savePath.c_str()); - m_modAutoloadDir.push_back(m_dataPath + "/mods"); - m_modAutoloadDir.push_back(m_savePath + "/mods"); + m_modSearchDirs.push_back(m_dataPath + "/mods"); + m_modSearchDirs.push_back(m_savePath + "/mods"); - if (!m_modAutoloadDir.empty()) + if (!m_modSearchDirs.empty()) { - GetLogger()->Info("Mod autoload dirs:\n"); - for(const std::string& modAutoloadDir : m_modAutoloadDir) - GetLogger()->Info(" * %s\n", modAutoloadDir.c_str()); - } - if (!m_mods.empty()) - { - GetLogger()->Info("Mods:\n"); - for(const std::string& modPath : m_mods) - GetLogger()->Info(" * %s\n", modPath.c_str()); + GetLogger()->Info("Mod search dirs:\n"); + for(const std::string& modSearchDir : m_modSearchDirs) + GetLogger()->Info(" * %s\n", modSearchDir.c_str()); } CResourceManager::AddLocation(m_dataPath); - for (const std::string& modAutoloadDir : m_modAutoloadDir) - { - GetLogger()->Trace("Searching for mods in '%s'...\n", modAutoloadDir.c_str()); - for (const std::string& modPath : FindModsInDir(modAutoloadDir)) - { - GetLogger()->Info("Autoloading mod: '%s'\n", modPath.c_str()); - CResourceManager::AddLocation(modPath); - } - } - - for (const std::string& modPath : m_mods) - { - GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); - CResourceManager::AddLocation(modPath); - } - CResourceManager::SetSaveLocation(m_savePath); CResourceManager::AddLocation(m_savePath); @@ -174,7 +141,45 @@ void CPathManager::InitPaths() GetLogger()->Debug(" * %s\n", path.c_str()); } -std::vector CPathManager::FindModsInDir(const std::string &dir) +void CPathManager::AddMod(const std::string &path) +{ + m_mods.push_back(path); +} + +std::vector CPathManager::FindMods() const +{ + std::vector mods; + GetLogger()->Info("Found mods:\n"); + for (const auto &searchPath : m_modSearchDirs) + { + for (const auto &modPath : FindModsInDir(searchPath)) + { + GetLogger()->Info(" * %s\n", modPath.c_str()); + mods.push_back(modPath); + } + } + GetLogger()->Info("Additional mod paths:\n"); + for (const auto& modPath : m_mods) + { + if (boost::filesystem::exists(modPath)) + { + GetLogger()->Info(" * %s\n", modPath.c_str()); + mods.push_back(modPath); + } + else + { + GetLogger()->Warn("Mod does not exist: %s\n", modPath.c_str()); + } + } + return mods; +} + +void CPathManager::AddModSearchDir(const std::string &modSearchDirPath) +{ + m_modSearchDirs.push_back(modSearchDirPath); +} + +std::vector CPathManager::FindModsInDir(const std::string &dir) const { std::vector ret; try diff --git a/src/app/pathman.h b/src/app/pathman.h index 561f66f3..d1ba4bbe 100644 --- a/src/app/pathman.h +++ b/src/app/pathman.h @@ -37,8 +37,6 @@ public: void SetDataPath(const std::string &dataPath); void SetLangPath(const std::string &langPath); void SetSavePath(const std::string &savePath); - void AddModAutoloadDir(const std::string &modAutoloadDirPath); - void AddMod(const std::string &modPath); const std::string& GetDataPath(); const std::string& GetLangPath(); @@ -49,9 +47,15 @@ public: //! Loads configured paths void InitPaths(); + //! Adds a path to a mod + void AddMod(const std::string& path); + //! Find paths to mods in mod search directories + std::vector FindMods() const; + //! Adds a mod search directory + void AddModSearchDir(const std::string &modSearchDirPath); + private: - //! Loads all mods from given directory - std::vector FindModsInDir(const std::string &dir); + std::vector FindModsInDir(const std::string &dir) const; private: //! Data path @@ -60,8 +64,8 @@ private: std::string m_langPath; //! Save path std::string m_savePath; - //! Mod autoload paths - std::vector m_modAutoloadDir; - //! Mod paths + //! Mod search paths + std::vector m_modSearchDirs; + //! Additional mod paths std::vector m_mods; }; diff --git a/src/common/config_file.h b/src/common/config_file.h index 6fbeae71..6e95f481 100644 --- a/src/common/config_file.h +++ b/src/common/config_file.h @@ -26,9 +26,15 @@ #include "common/singleton.h" +#include "common/logger.h" + #include +#include #include +#include +#include +#include /** @@ -100,6 +106,76 @@ public: */ bool GetBoolProperty(std::string section, std::string key, bool &value); + /** Gets an array of values of type T in section under specified key + * The value separator is ','. + * \a array will only be changed if key exists + * \return return true on success + */ + template + bool SetArrayProperty(std::string section, std::string key, const std::vector& array) + { + try + { + std::string convertedValue = ArrayToString(array); + m_propertyTree.put(section + "." + key, convertedValue); + m_needsSave = true; + } + catch (std::exception & e) + { + GetLogger()->Error("Error on editing config file: %s\n", e.what()); + return false; + } + return true; + } + + /** Sets an array of values of type T in section under specified key. + * The value separator is ','. + * \a array will only be changed if key exists + * \return return true on success + */ + template + bool GetArrayProperty(std::string section, std::string key, std::vector& array) + { + try + { + std::string readValue = m_propertyTree.get(section + "." + key); + std::vector convertedValue = StringToArray(readValue); + array = std::move(convertedValue); + } + catch (std::exception & e) + { + GetLogger()->Log(m_loaded ? LOG_INFO : LOG_TRACE, "Error on parsing config file: %s\n", e.what()); + return false; + } + return true; + } + +private: + template + std::vector StringToArray(const std::string& s) + { + std::vector result; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, ',')) + { + result.push_back(boost::lexical_cast(item)); + } + return result; + } + + template + std::string ArrayToString(const std::vector &array) + { + std::ostringstream oss; + if (!array.empty()) + { + std::copy(array.begin(), array.end() - 1, std::ostream_iterator(oss, ",")); + oss << array.back(); + } + return oss.str(); + } + private: boost::property_tree::ptree m_propertyTree; bool m_needsSave; diff --git a/src/common/event.cpp b/src/common/event.cpp index 214fd20e..d75c8f9c 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -190,6 +190,8 @@ void InitializeEventTypeTexts() EVENT_TYPE_TEXT[EVENT_INTERFACE_ABORT] = "EVENT_INTERFACE_ABORT"; EVENT_TYPE_TEXT[EVENT_INTERFACE_USER] = "EVENT_INTERFACE_USER"; EVENT_TYPE_TEXT[EVENT_INTERFACE_SATCOM] = "EVENT_INTERFACE_SATCOM"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_PLUS] = "EVENT_INTERFACE_PLUS"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS] = "EVENT_INTERFACE_MODS"; EVENT_TYPE_TEXT[EVENT_INTERFACE_CHAP] = "EVENT_INTERFACE_CHAP"; EVENT_TYPE_TEXT[EVENT_INTERFACE_LIST] = "EVENT_INTERFACE_LIST"; @@ -272,6 +274,20 @@ void InitializeEventTypeTexts() EVENT_TYPE_TEXT[EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT]= "EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT"; EVENT_TYPE_TEXT[EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT]= "EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_PLUS_TRAINER] = "EVENT_INTERFACE_PLUS_TRAINER"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_PLUS_RESEARCH] = "EVENT_INTERFACE_PLUS_RESEARCH"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_PLUS_EXPLORER] = "EVENT_INTERFACE_PLUS_EXPLORER"; + + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_LIST] = "EVENT_INTERFACE_MOD_LIST"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_WORKSHOP] = "EVENT_INTERFACE_WORKSHOP"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_DIR] = "EVENT_INTERFACE_MODS_DIR"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE] = "EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_APPLY] = "EVENT_INTERFACE_MODS_APPLY"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_DETAILS] = "EVENT_INTERFACE_MOD_DETAILS"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_MOVE_UP] = "EVENT_INTERFACE_MOD_MOVE_UP"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_MOVE_DOWN] = "EVENT_INTERFACE_MOD_MOVE_DOWN"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_REFRESH] = "EVENT_INTERFACE_MODS_REFRESH"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_GLINTl] = "EVENT_INTERFACE_GLINTl"; EVENT_TYPE_TEXT[EVENT_INTERFACE_GLINTr] = "EVENT_INTERFACE_GLINTr"; EVENT_TYPE_TEXT[EVENT_INTERFACE_GLINTu] = "EVENT_INTERFACE_GLINTu"; @@ -405,7 +421,7 @@ void InitializeEventTypeTexts() EVENT_TYPE_TEXT[EVENT_OBJECT_BNUCLEAR] = "EVENT_OBJECT_BNUCLEAR"; EVENT_TYPE_TEXT[EVENT_OBJECT_BPARA] = "EVENT_OBJECT_BPARA"; EVENT_TYPE_TEXT[EVENT_OBJECT_BINFO] = "EVENT_OBJECT_BINFO"; - EVENT_TYPE_TEXT[EVENT_OBJECT_BDESTROYER] = "EVENT_OBJECT_BDESTROYER"; + EVENT_TYPE_TEXT[EVENT_OBJECT_BSAFE] = "EVENT_OBJECT_BSAFE"; EVENT_TYPE_TEXT[EVENT_OBJECT_GFLAT] = "EVENT_OBJECT_GFLAT"; EVENT_TYPE_TEXT[EVENT_OBJECT_FCREATE] = "EVENT_OBJECT_FCREATE"; EVENT_TYPE_TEXT[EVENT_OBJECT_FDELETE] = "EVENT_OBJECT_FDELETE"; diff --git a/src/common/event.h b/src/common/event.h index c2029e82..aec48ecb 100644 --- a/src/common/event.h +++ b/src/common/event.h @@ -225,6 +225,8 @@ enum EventType EVENT_INTERFACE_ABORT = 412, EVENT_INTERFACE_USER = 413, EVENT_INTERFACE_SATCOM = 414, + EVENT_INTERFACE_PLUS = 415, + EVENT_INTERFACE_MODS = 416, EVENT_INTERFACE_CHAP = 420, EVENT_INTERFACE_LIST = 421, @@ -311,6 +313,21 @@ enum EventType EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT = 573, EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT = 574, + EVENT_INTERFACE_PLUS_TRAINER = 575, + EVENT_INTERFACE_PLUS_RESEARCH = 576, + EVENT_INTERFACE_PLUS_EXPLORER = 577, + + EVENT_INTERFACE_MOD_LIST = 580, + EVENT_INTERFACE_WORKSHOP = 581, + EVENT_INTERFACE_MODS_DIR = 582, + EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE = 583, + EVENT_INTERFACE_MODS_APPLY = 584, + EVENT_INTERFACE_MOD_SUMMARY = 585, + EVENT_INTERFACE_MOD_DETAILS = 586, + EVENT_INTERFACE_MOD_MOVE_UP = 587, + EVENT_INTERFACE_MOD_MOVE_DOWN = 888, + EVENT_INTERFACE_MODS_REFRESH = 589, + EVENT_INTERFACE_GLINTl = 590, EVENT_INTERFACE_GLINTr = 591, EVENT_INTERFACE_GLINTu = 592, @@ -469,7 +486,7 @@ enum EventType EVENT_OBJECT_BNUCLEAR = 1060, EVENT_OBJECT_BPARA = 1061, EVENT_OBJECT_BINFO = 1062, - EVENT_OBJECT_BDESTROYER = 1063, + EVENT_OBJECT_BSAFE = 1063, EVENT_OBJECT_GFLAT = 1070, EVENT_OBJECT_FCREATE = 1071, EVENT_OBJECT_FDELETE = 1072, diff --git a/src/common/resources/resourcemanager.cpp b/src/common/resources/resourcemanager.cpp index b51325b8..7c572483 100644 --- a/src/common/resources/resourcemanager.cpp +++ b/src/common/resources/resourcemanager.cpp @@ -62,9 +62,9 @@ std::string CResourceManager::CleanPath(const std::string& path) } -bool CResourceManager::AddLocation(const std::string &location, bool prepend) +bool CResourceManager::AddLocation(const std::string &location, bool prepend, const std::string &mountPoint) { - if (!PHYSFS_mount(location.c_str(), nullptr, prepend ? 0 : 1)) + if (!PHYSFS_mount(location.c_str(), mountPoint.c_str(), prepend ? 0 : 1)) { GetLogger()->Error("Error while mounting \"%s\": %s\n", location.c_str(), PHYSFS_getLastError()); return false; @@ -95,6 +95,12 @@ std::vector CResourceManager::GetLocations() return ret; } +bool CResourceManager::LocationExists(const std::string& location) +{ + auto locations = GetLocations(); + auto it = std::find(locations.cbegin(), locations.cend(), location); + return it != locations.cend(); +} bool CResourceManager::SetSaveLocation(const std::string &location) { diff --git a/src/common/resources/resourcemanager.h b/src/common/resources/resourcemanager.h index 8c09b91a..d15a6f02 100644 --- a/src/common/resources/resourcemanager.h +++ b/src/common/resources/resourcemanager.h @@ -36,11 +36,13 @@ public: static std::string CleanPath(const std::string &path); //! Add a location to the search path - static bool AddLocation(const std::string &location, bool prepend = true); + static bool AddLocation(const std::string &location, bool prepend = true, const std::string &mountPoint = ""); //! Remove a location from the search path static bool RemoveLocation(const std::string &location); //! List all locations in the search path static std::vector GetLocations(); + //! Check if given location is in the search path + static bool LocationExists(const std::string &location); static bool SetSaveLocation(const std::string &location); static std::string GetSaveLocation(); diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 40b34565..d8e16a00 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -48,8 +48,8 @@ const char* stringsCbot[CBot::CBotErrMAX] = { nullptr }; */ #define TR(x) x -/* Please run `make update-pot` after changing this file - * in order to update translation files. Thank you. +/* Please run `cmake --build --target update-pot` + * after changing this file in order to update translation files. Thank you. */ void InitializeRestext() @@ -71,12 +71,14 @@ void InitializeRestext() stringsText[RT_TITLE_MISSION] = TR("Missions"); stringsText[RT_TITLE_FREE] = TR("Free game"); stringsText[RT_TITLE_USER] = TR("User levels"); - stringsText[RT_TITLE_CODE_BATTLES]=TR("Code battles"); + stringsText[RT_TITLE_CODE_BATTLES] = TR("Code battles"); stringsText[RT_TITLE_SETUP] = TR("Options"); stringsText[RT_TITLE_NAME] = TR("Player's name"); stringsText[RT_TITLE_PERSO] = TR("Customize your appearance"); stringsText[RT_TITLE_WRITE] = TR("Save the current mission"); stringsText[RT_TITLE_READ] = TR("Load a saved mission"); + stringsText[RT_TITLE_PLUS] = TR("Missions+"); + stringsText[RT_TITLE_MODS] = TR("Mods"); stringsText[RT_PLAY_CHAP_CHAPTERS] = TR("Chapters:"); stringsText[RT_PLAY_CHAP_PLANETS] = TR("Planets:"); @@ -108,6 +110,11 @@ void InitializeRestext() stringsText[RT_DIALOG_OK] = TR("OK"); stringsText[RT_DIALOG_NOUSRLVL_TITLE] = TR("No userlevels installed!"); stringsText[RT_DIALOG_NOUSRLVL_TEXT] = TR("This menu is for userlevels from mods, but you didn't install any"); + stringsText[RT_DIALOG_OPEN_PATH_FAILED_TITLE] = TR("Could not open the file explorer!"); + stringsText[RT_DIALOG_OPEN_PATH_FAILED_TEXT] = TR("The path %s could not be opened in a file explorer."); + stringsText[RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE] = TR("Could not open the web browser!"); + stringsText[RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT] = TR("The address %s could not be opened in a web browser."); + stringsText[RT_DIALOG_CHANGES_QUESTION] = TR("There are unsaved changes. Do you want to save them before leaving?"); stringsText[RT_STUDIO_LISTTT] = TR("Keyword help(\\key cbot;)"); stringsText[RT_STUDIO_COMPOK] = TR("Compilation ok (0 errors)"); @@ -153,7 +160,18 @@ void InitializeRestext() stringsText[RT_SCOREBOARD_RESULTS_TIME]= TR("Time: %s"); stringsText[RT_SCOREBOARD_RESULTS_LINE]= TR("%s: %d pts"); - + stringsText[RT_MOD_LIST] = TR("Mods:"); + stringsText[RT_MOD_DETAILS] = TR("Information:"); + stringsText[RT_MOD_SUMMARY] = TR("Description:"); + stringsText[RT_MOD_ENABLE] = TR("Enable\\Enable the selected mod"); + stringsText[RT_MOD_DISABLE] = TR("Disable\\Disable the selected mod"); + stringsText[RT_MOD_UNKNOWN_AUTHOR] = TR("Unknown author"); + stringsText[RT_MOD_AUTHOR_FIELD_NAME] = TR("by"); + stringsText[RT_MOD_VERSION_FIELD_NAME] = TR("Version"); + stringsText[RT_MOD_WEBSITE_FIELD_NAME] = TR("Website"); + stringsText[RT_MOD_CHANGES_FIELD_NAME] = TR("Changes"); + stringsText[RT_MOD_NO_SUMMARY] = TR("No description."); + stringsText[RT_MOD_NO_CHANGES] = TR("No changes."); stringsEvent[EVENT_LABEL_CODE_BATTLE] = TR("Code battle"); @@ -173,6 +191,7 @@ void InitializeRestext() stringsEvent[EVENT_INTERFACE_CODE_BATTLES] = TR("Code battles\\Program your robot to be the best of them all!"); stringsEvent[EVENT_INTERFACE_USER] = TR("Custom levels\\Levels from mods created by the users"); stringsEvent[EVENT_INTERFACE_SATCOM] = TR("SatCom"); + stringsEvent[EVENT_INTERFACE_MODS] = TR("Mods\\Mod manager"); stringsEvent[EVENT_INTERFACE_NAME] = TR("Change player\\Change player"); stringsEvent[EVENT_INTERFACE_SETUP] = TR("Options\\Preferences"); stringsEvent[EVENT_INTERFACE_AGAIN] = TR("Restart\\Restart the mission from the beginning"); @@ -181,7 +200,14 @@ void InitializeRestext() stringsEvent[EVENT_INTERFACE_ABORT] = TR("\\Return to Colobot: Gold Edition"); stringsEvent[EVENT_INTERFACE_QUIT] = TR("Quit\\Quit Colobot: Gold Edition"); stringsEvent[EVENT_INTERFACE_BACK] = TR("<< Back \\Back to the previous screen"); + stringsEvent[EVENT_INTERFACE_PLUS] = TR("+\\Missions with bonus content and optional challenges"); stringsEvent[EVENT_INTERFACE_PLAY] = TR("Play\\Start mission!"); + stringsEvent[EVENT_INTERFACE_WORKSHOP] = TR("Workshop\\Open the workshop to search for mods"); + stringsEvent[EVENT_INTERFACE_MODS_DIR] = TR("Open Directory\\Open the mods directory"); + stringsEvent[EVENT_INTERFACE_MODS_APPLY] = TR("Apply\\Apply the current mod configuration"); + stringsEvent[EVENT_INTERFACE_MOD_MOVE_UP] = TR("Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)"); + stringsEvent[EVENT_INTERFACE_MOD_MOVE_DOWN] = TR("Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)"); + stringsEvent[EVENT_INTERFACE_MODS_REFRESH] = TR("Refresh\\Refresh the list of currently installed mods"); stringsEvent[EVENT_INTERFACE_SETUPd] = TR("Device\\Driver and resolution settings"); stringsEvent[EVENT_INTERFACE_SETUPg] = TR("Graphics\\Graphics settings"); stringsEvent[EVENT_INTERFACE_SETUPp] = TR("Game\\Game settings"); @@ -275,6 +301,10 @@ void InitializeRestext() stringsEvent[EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT] = TR("Invert\\Invert values on this axis"); stringsEvent[EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT] = TR("Invert\\Invert values on this axis"); + stringsEvent[EVENT_INTERFACE_PLUS_TRAINER] = TR("Space Programmer\\Disables radio-control"); + stringsEvent[EVENT_INTERFACE_PLUS_RESEARCH] = TR("Space Researcher\\Disables using all previously researched technologies"); + stringsEvent[EVENT_INTERFACE_PLUS_EXPLORER] = TR("Space Explorer\\Disables astronaut abilities"); + stringsEvent[EVENT_INTERFACE_NEDIT] = TR("\\New player name"); stringsEvent[EVENT_INTERFACE_NOK] = TR("OK\\Choose the selected player"); stringsEvent[EVENT_INTERFACE_NDELETE] = TR("Delete player\\Deletes the player from the list"); @@ -341,7 +371,7 @@ void InitializeRestext() stringsEvent[EVENT_OBJECT_BNUCLEAR] = TR("Build a nuclear power plant"); stringsEvent[EVENT_OBJECT_BPARA] = TR("Build a lightning conductor"); stringsEvent[EVENT_OBJECT_BINFO] = TR("Build a exchange post"); - stringsEvent[EVENT_OBJECT_BDESTROYER] = TR("Build a destroyer"); + stringsEvent[EVENT_OBJECT_BSAFE] = TR("Build a vault"); stringsEvent[EVENT_OBJECT_GFLAT] = TR("Show if the ground is flat"); stringsEvent[EVENT_OBJECT_FCREATE] = TR("Plant a flag"); stringsEvent[EVENT_OBJECT_FDELETE] = TR("Remove a flag"); diff --git a/src/common/restext.h b/src/common/restext.h index c33a864f..0ba5d16d 100644 --- a/src/common/restext.h +++ b/src/common/restext.h @@ -71,6 +71,8 @@ enum ResTextType RT_TITLE_WRITE = 50, RT_TITLE_READ = 51, RT_TITLE_USER = 52, + RT_TITLE_PLUS = 53, + RT_TITLE_MODS = 54, RT_PLAY_CHAP_CHAPTERS = 60, RT_PLAY_CHAP_PLANETS = 61, @@ -102,6 +104,11 @@ enum ResTextType RT_DIALOG_OK = 110, RT_DIALOG_NOUSRLVL_TITLE = 111, RT_DIALOG_NOUSRLVL_TEXT = 112, + RT_DIALOG_OPEN_PATH_FAILED_TITLE = 113, + RT_DIALOG_OPEN_PATH_FAILED_TEXT = 114, + RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE = 115, + RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT = 116, + RT_DIALOG_CHANGES_QUESTION = 117, RT_STUDIO_LISTTT = 120, RT_STUDIO_COMPOK = 121, @@ -147,6 +154,18 @@ enum ResTextType RT_SCOREBOARD_RESULTS_TIME= 232, RT_SCOREBOARD_RESULTS_LINE= 233, + RT_MOD_LIST = 234, + RT_MOD_DETAILS = 235, + RT_MOD_SUMMARY = 236, + RT_MOD_ENABLE = 237, + RT_MOD_DISABLE = 238, + RT_MOD_UNKNOWN_AUTHOR = 239, + RT_MOD_AUTHOR_FIELD_NAME = 240, + RT_MOD_VERSION_FIELD_NAME = 241, + RT_MOD_WEBSITE_FIELD_NAME = 242, + RT_MOD_CHANGES_FIELD_NAME = 243, + RT_MOD_NO_SUMMARY = 244, + RT_MOD_NO_CHANGES = 245, RT_MAX //! < number of values }; diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 021fa37c..7c72030e 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -72,7 +72,6 @@ void CSettings::SaveSettings() CRobotMain* main = CRobotMain::GetInstancePointer(); Gfx::CEngine* engine = Gfx::CEngine::GetInstancePointer(); Gfx::CCamera* camera = main->GetCamera(); - CSoundInterface* sound = app->GetSound(); GetConfigFile().SetBoolProperty("Setup", "Tooltips", m_tooltips); GetConfigFile().SetBoolProperty("Setup", "InterfaceGlint", m_interfaceGlint); @@ -80,7 +79,6 @@ void CSettings::SaveSettings() GetConfigFile().SetBoolProperty("Setup", "Soluce4", m_soluce4); GetConfigFile().SetBoolProperty("Setup", "Movies", m_movies); GetConfigFile().SetBoolProperty("Setup", "FocusLostPause", m_focusLostPause); - GetConfigFile().SetBoolProperty("Setup", "FocusLostMute", m_focusLostMute); GetConfigFile().SetBoolProperty("Setup", "OldCameraScroll", camera->GetOldCameraScroll()); GetConfigFile().SetBoolProperty("Setup", "CameraInvertX", camera->GetCameraInvertX()); GetConfigFile().SetBoolProperty("Setup", "CameraInvertY", camera->GetCameraInvertY()); @@ -95,8 +93,6 @@ void CSettings::SaveSettings() GetConfigFile().SetIntProperty("Setup", "JoystickIndex", app->GetJoystickEnabled() ? app->GetJoystick().index : -1); GetConfigFile().SetFloatProperty("Setup", "ParticleDensity", engine->GetParticleDensity()); GetConfigFile().SetFloatProperty("Setup", "ClippingDistance", engine->GetClippingDistance()); - GetConfigFile().SetIntProperty("Setup", "AudioVolume", sound->GetAudioVolume()); - GetConfigFile().SetIntProperty("Setup", "MusicVolume", sound->GetMusicVolume()); GetConfigFile().SetBoolProperty("Setup", "EditIndentMode", engine->GetEditIndentMode()); GetConfigFile().SetIntProperty("Setup", "EditIndentValue", engine->GetEditIndentValue()); GetConfigFile().SetBoolProperty("Setup", "PauseBlur", engine->GetPauseBlurEnabled()); @@ -112,6 +108,9 @@ void CSettings::SaveSettings() GetConfigFile().SetIntProperty("Setup", "ShadowMappingResolution", engine->GetShadowMappingOffscreen() ? engine->GetShadowMappingOffscreenResolution() : 0); + // Save Audio settings + SaveAudioSettings(); + // Experimental settings GetConfigFile().SetBoolProperty("Experimental", "TerrainShadows", engine->GetTerrainShadows()); GetConfigFile().SetIntProperty("Setup", "VSync", engine->GetVSync()); @@ -140,6 +139,16 @@ void CSettings::SaveSettings() GetConfigFile().Save(); } +void CSettings::SaveAudioSettings() +{ + CApplication* app = CApplication::GetInstancePointer(); + CSoundInterface* sound = app->GetSound(); + + GetConfigFile().SetIntProperty("Setup", "AudioVolume", sound->GetAudioVolume()); + GetConfigFile().SetIntProperty("Setup", "MusicVolume", sound->GetMusicVolume()); + GetConfigFile().SetBoolProperty("Setup", "FocusLostMute", m_focusLostMute); +} + void CSettings::LoadSettings() { CApplication* app = CApplication::GetInstancePointer(); diff --git a/src/common/settings.h b/src/common/settings.h index 6ebf6e22..010fd036 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -37,6 +37,7 @@ public: void SaveSettings(); void LoadSettings(); + void SaveAudioSettings(); void SetTooltips(bool tooltips); bool GetTooltips(); diff --git a/src/common/system/system.cpp b/src/common/system/system.cpp index a815964d..b4893655 100644 --- a/src/common/system/system.cpp +++ b/src/common/system/system.cpp @@ -215,3 +215,13 @@ std::string CSystemUtils::GetEnvVar(const std::string& name) { return ""; } + +bool CSystemUtils::OpenPath(const std::string& path) +{ + return false; +} + +bool CSystemUtils::OpenWebsite(const std::string& url) +{ + return false; +} diff --git a/src/common/system/system.h b/src/common/system/system.h index 553dc56f..f20ee443 100644 --- a/src/common/system/system.h +++ b/src/common/system/system.h @@ -116,6 +116,9 @@ public: //! Copies the time stamp from \a src to \a dst TEST_VIRTUAL void CopyTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *src); + //! Interpolates between two timestamps. If i=0 then dst=a. If i=1 then dst=b. If i=0.5 then dst is halfway between. + virtual void InterpolateTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *a, SystemTimeStamp *b, float i) = 0; + //! Returns a time stamp associated with current time virtual void GetCurrentTimeStamp(SystemTimeStamp *stamp) = 0; @@ -142,6 +145,14 @@ public: //! Returns the environment variable with the given name or an empty string if it does not exist virtual std::string GetEnvVar(const std::string &name); + //! Opens a path with default file browser + /** \returns true if successful */ + virtual bool OpenPath(const std::string& path); + + //! Opens a website with default web browser + /** \returns true if successful */ + virtual bool OpenWebsite(const std::string& url); + //! Sleep for given amount of microseconds virtual void Usleep(int usecs) = 0; diff --git a/src/common/system/system_linux.cpp b/src/common/system/system_linux.cpp index 9a09b2c3..2b7d99fa 100644 --- a/src/common/system/system_linux.cpp +++ b/src/common/system/system_linux.cpp @@ -83,6 +83,19 @@ SystemDialogResult CSystemUtilsLinux::SystemDialog(SystemDialogType type, const return result; } +void CSystemUtilsLinux::InterpolateTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *a, SystemTimeStamp *b, float i) +{ + long long delta = TimeStampExactDiff(a, b); + delta *= i; // truncates + dst->clockTime.tv_sec = a->clockTime.tv_sec + delta / 1000000000; + dst->clockTime.tv_nsec = a->clockTime.tv_nsec + delta % 1000000000; + if(dst->clockTime.tv_nsec >= 1000000000) + { + dst->clockTime.tv_nsec -= 1000000000; + dst->clockTime.tv_sec++; + } +} + void CSystemUtilsLinux::GetCurrentTimeStamp(SystemTimeStamp *stamp) { clock_gettime(CLOCK_MONOTONIC_RAW, &stamp->clockTime); @@ -137,6 +150,28 @@ std::string CSystemUtilsLinux::GetEnvVar(const std::string& name) return ""; } +bool CSystemUtilsLinux::OpenPath(const std::string& path) +{ + int result = system(("xdg-open \"" + path + "\"").c_str()); + if (result != 0) + { + GetLogger()->Error("Failed to open path: %s, error code: %i\n", path.c_str(), result); + return false; + } + return true; +} + +bool CSystemUtilsLinux::OpenWebsite(const std::string& url) +{ + int result = system(("xdg-open \"" + url + "\"").c_str()); + if (result != 0) + { + GetLogger()->Error("Failed to open website: %s, error code: %i\n", url.c_str(), result); + return false; + } + return true; +} + void CSystemUtilsLinux::Usleep(int usec) { usleep(usec); diff --git a/src/common/system/system_linux.h b/src/common/system/system_linux.h index 6f0c1950..6607b150 100644 --- a/src/common/system/system_linux.h +++ b/src/common/system/system_linux.h @@ -40,6 +40,7 @@ public: SystemDialogResult SystemDialog(SystemDialogType type, const std::string& title, const std::string& message) override; + void InterpolateTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *a, SystemTimeStamp *b, float i) override; void GetCurrentTimeStamp(SystemTimeStamp *stamp) override; long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after) override; @@ -47,6 +48,9 @@ public: std::string GetEnvVar(const std::string& name) override; + bool OpenPath(const std::string& path) override; + bool OpenWebsite(const std::string& url) override; + void Usleep(int usec) override; private: diff --git a/src/common/system/system_macosx.cpp b/src/common/system/system_macosx.cpp index 752cf98f..d65fb6dc 100644 --- a/src/common/system/system_macosx.cpp +++ b/src/common/system/system_macosx.cpp @@ -119,6 +119,28 @@ std::string CSystemUtilsMacOSX::GetEnvVar(const std::string& str) return std::string(); } +bool CSystemUtilsMacOSX::OpenPath(const std::string& path) +{ + int result = system(("open \"" + path + "\"").c_str()); // TODO: Test on macOS + if (result != 0) + { + GetLogger()->Error("Failed to open path: %s, error code: %i\n", path.c_str(), result); + return false; + } + return true; +} + +bool CSystemUtilsMacOSX::OpenWebsite(const std::string& url) +{ + int result = system(("open \"" + url + "\"").c_str()); // TODO: Test on macOS + if (result != 0) + { + GetLogger()->Error("Failed to open website: %s, error code: %i\n", website.c_str(), result); + return false; + } + return true; +} + void CSystemUtilsMacOSX::Usleep(int usec) { usleep(usec); diff --git a/src/common/system/system_macosx.h b/src/common/system/system_macosx.h index dc3f785a..523e52e8 100644 --- a/src/common/system/system_macosx.h +++ b/src/common/system/system_macosx.h @@ -38,6 +38,9 @@ public: std::string GetEnvVar(const std::string& name) override; + bool OpenPath(const std::string& path) override; + bool OpenWebsite(const std::string& url) override; + void Usleep(int usec) override; private: diff --git a/src/common/system/system_other.cpp b/src/common/system/system_other.cpp index d73ce705..865db847 100644 --- a/src/common/system/system_other.cpp +++ b/src/common/system/system_other.cpp @@ -34,6 +34,11 @@ void CSystemUtilsOther::GetCurrentTimeStamp(SystemTimeStamp* stamp) stamp->sdlTicks = SDL_GetTicks(); } +void CSystemUtilsOther::InterpolateTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *a, SystemTimeStamp *b, float i) +{ + dst->sdlTicks = a->sdlTicks + static_cast((b->sdlTicks - a->sdlTicks) * i); +} + long long int CSystemUtilsOther::TimeStampExactDiff(SystemTimeStamp* before, SystemTimeStamp* after) { return (after->sdlTicks - before->sdlTicks) * 1000000ll; diff --git a/src/common/system/system_other.h b/src/common/system/system_other.h index dcec37a6..5964b585 100644 --- a/src/common/system/system_other.h +++ b/src/common/system/system_other.h @@ -46,6 +46,7 @@ public: void Init() override; SystemDialogResult SystemDialog(SystemDialogType type, const std::string& title, const std::string& message) override; + void InterpolateTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *a, SystemTimeStamp *b, float i) override; void GetCurrentTimeStamp(SystemTimeStamp *stamp) override; long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after) override; diff --git a/src/common/system/system_windows.cpp b/src/common/system/system_windows.cpp index 6406bee3..989d359b 100644 --- a/src/common/system/system_windows.cpp +++ b/src/common/system/system_windows.cpp @@ -21,6 +21,7 @@ #include "common/logger.h" +#include #include @@ -83,6 +84,11 @@ void CSystemUtilsWindows::GetCurrentTimeStamp(SystemTimeStamp* stamp) stamp->counterValue = value.QuadPart; } +void CSystemUtilsWindows::InterpolateTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *a, SystemTimeStamp *b, float i) +{ + dst->counterValue = a->counterValue + static_cast((b->counterValue - a->counterValue) * static_cast(i)); +} + long long int CSystemUtilsWindows::TimeStampExactDiff(SystemTimeStamp* before, SystemTimeStamp* after) { float floatValue = static_cast(after->counterValue - before->counterValue) * (1e9 / static_cast(m_counterFrequency)); @@ -147,6 +153,28 @@ std::string CSystemUtilsWindows::GetEnvVar(const std::string& name) } } +bool CSystemUtilsWindows::OpenPath(const std::string& path) +{ + int result = system(("start explorer \"" + boost::filesystem::path(path).make_preferred().string() + "\"").c_str()); + if (result != 0) + { + GetLogger()->Error("Failed to open path: %s, error code: %i\n", path.c_str(), result); + return false; + } + return true; +} + +bool CSystemUtilsWindows::OpenWebsite(const std::string& url) +{ + int result = system(("rundll32 url.dll,FileProtocolHandler \"" + url + "\"").c_str()); + if (result != 0) + { + GetLogger()->Error("Failed to open website: %s, error code: %i\n", url.c_str(), result); + return false; + } + return true; +} + void CSystemUtilsWindows::Usleep(int usec) { LARGE_INTEGER ft; diff --git a/src/common/system/system_windows.h b/src/common/system/system_windows.h index 736ab706..90ef6b91 100644 --- a/src/common/system/system_windows.h +++ b/src/common/system/system_windows.h @@ -38,6 +38,7 @@ public: SystemDialogResult SystemDialog(SystemDialogType type, const std::string& title, const std::string& message) override; + void InterpolateTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *a, SystemTimeStamp *b, float i) override; void GetCurrentTimeStamp(SystemTimeStamp *stamp) override; long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after) override; @@ -45,6 +46,9 @@ public: std::string GetEnvVar(const std::string& name) override; + bool OpenPath(const std::string& path) override; + bool OpenWebsite(const std::string& url) override; + void Usleep(int usec) override; public: diff --git a/src/graphics/engine/camera.cpp b/src/graphics/engine/camera.cpp index c55473ab..f3b96186 100644 --- a/src/graphics/engine/camera.cpp +++ b/src/graphics/engine/camera.cpp @@ -59,14 +59,14 @@ static void SetTransparency(CObject* obj, float value) if (obj->Implements(ObjectInterfaceType::Carrier)) { - CObject* cargo = dynamic_cast(obj)->GetCargo(); + CObject* cargo = dynamic_cast(*obj).GetCargo(); if (cargo != nullptr) cargo->SetTransparency(value); } if (obj->Implements(ObjectInterfaceType::Powered)) { - CObject* power = dynamic_cast(obj)->GetPower(); + CObject* power = dynamic_cast(*obj).GetPower(); if (power != nullptr) power->SetTransparency(value); } @@ -1233,7 +1233,7 @@ bool CCamera::EventFrameBack(const Event &event) bool ground = true; if (m_cameraObj->Implements(ObjectInterfaceType::Movable)) - ground = dynamic_cast(m_cameraObj)->GetPhysics()->GetLand(); + ground = dynamic_cast(*m_cameraObj).GetPhysics()->GetLand(); if ( ground ) // ground? { Math::Vector pos = lookatPt + (lookatPt - m_eyePt); @@ -1326,7 +1326,7 @@ bool CCamera::EventFrameOnBoard(const Event &event) { assert(m_cameraObj->Implements(ObjectInterfaceType::Controllable)); Math::Vector lookatPt, upVec; - dynamic_cast(m_cameraObj)->AdjustCamera(m_eyePt, m_directionH, m_directionV, lookatPt, upVec, m_type); + dynamic_cast(*m_cameraObj).AdjustCamera(m_eyePt, m_directionH, m_directionV, lookatPt, upVec, m_type); Math::Vector eye = m_effectOffset * 0.3f + m_eyePt; Math::Vector lookat = m_effectOffset * 0.3f + lookatPt; diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 198e230d..c146d767 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -408,6 +408,7 @@ void CEngine::ReloadAllTextures() { FlushTextureCache(); m_text->FlushCache(); + m_text->ReloadFonts(); m_app->GetEventQueue()->AddEvent(Event(EVENT_RELOAD_TEXTURES)); UpdateGroundSpotTextures(); diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index af455819..aee24f34 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -1189,6 +1189,10 @@ public: void EnablePauseBlur(); void DisablePauseBlur(); + //! Reloads all textures + /** This additionally sends EVENT_RELOAD_TEXTURES to reload all textures not maintained by CEngine **/ + void ReloadAllTextures(); + protected: //! Resets some states and flushes textures after device was changed (e.g. resoulution changed) /** Instead of calling this directly, send EVENT_RESOLUTION_CHANGED event **/ @@ -1287,10 +1291,6 @@ protected: }; static void WriteScreenShotThread(std::unique_ptr data); - //! Reloads all textures - /** This additionally sends EVENT_RELOAD_TEXTURES to reload all textures not maintained by CEngine **/ - void ReloadAllTextures(); - protected: CApplication* m_app; CSystemUtils* m_systemUtils; diff --git a/src/graphics/engine/lightning.cpp b/src/graphics/engine/lightning.cpp index 84a99bbc..8b101e53 100644 --- a/src/graphics/engine/lightning.cpp +++ b/src/graphics/engine/lightning.cpp @@ -323,7 +323,7 @@ CObject* CLightning::SearchObject(Math::Vector pos) if (!obj->Implements(ObjectInterfaceType::Destroyable)) continue; - float detect = m_magnetic * dynamic_cast(obj)->GetLightningHitProbability(); + float detect = m_magnetic * dynamic_cast(*obj).GetLightningHitProbability(); if (detect == 0.0f) continue; Math::Vector oPos = obj->GetPosition(); diff --git a/src/graphics/engine/oldmodelmanager.cpp b/src/graphics/engine/oldmodelmanager.cpp index ee155ce8..7207cc7b 100644 --- a/src/graphics/engine/oldmodelmanager.cpp +++ b/src/graphics/engine/oldmodelmanager.cpp @@ -57,7 +57,18 @@ bool COldModelManager::LoadModel(const std::string& fileName, bool mirrored, int if (!stream.is_open()) throw CModelIOException(std::string("Could not open file '") + fileName + "'"); - model = ModelInput::Read(stream, ModelFormat::Old); + std::string::size_type extension_index = fileName.find_last_of('.'); + if (extension_index == std::string::npos) + throw CModelIOException(std::string("Filename '") + fileName + "' has no extension"); + + std::string extension = fileName.substr(extension_index + 1); + + if (extension == "mod") + model = ModelInput::Read(stream, ModelFormat::Old); + else if (extension == "txt") + model = ModelInput::Read(stream, ModelFormat::Text); + else + throw CModelIOException(std::string("Filename '") + fileName + "' has unknown extension"); } catch (const CModelIOException& e) { diff --git a/src/graphics/engine/particle.cpp b/src/graphics/engine/particle.cpp index e42762da..fa430488 100644 --- a/src/graphics/engine/particle.cpp +++ b/src/graphics/engine/particle.cpp @@ -953,7 +953,7 @@ void CParticle::FrameParticle(float rTime) m_particle[i].goal = m_particle[i].pos; if (object != nullptr && object->Implements(ObjectInterfaceType::Damageable)) { - dynamic_cast(object)->DamageObject(DamageType::Phazer, 0.002f, m_particle[i].objFather); + 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); @@ -1156,7 +1156,7 @@ void CParticle::FrameParticle(float rTime) { if (object->Implements(ObjectInterfaceType::Damageable)) { - dynamic_cast(object)->DamageObject(DamageType::Fire, 0.001f, m_particle[i].objFather); + dynamic_cast(*object).DamageObject(DamageType::Fire, 0.001f, m_particle[i].objFather); } m_exploGunCounter++; @@ -1222,7 +1222,7 @@ void CParticle::FrameParticle(float rTime) m_particle[i].goal = m_particle[i].pos; if (object != nullptr) { - if (object->GetType() == OBJECT_MOBILErs && dynamic_cast(object)->GetActiveShieldRadius() > 0.0f) // protected by shield? + if (object->GetType() == OBJECT_MOBILErs && dynamic_cast(*object).GetActiveShieldRadius() > 0.0f) // protected by shield? { CreateParticle(m_particle[i].pos, Math::Vector(0.0f, 0.0f, 0.0f), Math::Point(6.0f, 6.0f), PARTIGUNDEL, 2.0f); if (m_lastTimeGunDel > 0.2f) @@ -1240,7 +1240,7 @@ void CParticle::FrameParticle(float rTime) if (object->Implements(ObjectInterfaceType::Damageable)) { - dynamic_cast(object)->DamageObject(DamageType::Organic, 0.1f, m_particle[i].objFather); // starts explosion + dynamic_cast(*object).DamageObject(DamageType::Organic, 0.1f, m_particle[i].objFather); // starts explosion } } } @@ -1270,7 +1270,7 @@ void CParticle::FrameParticle(float rTime) m_particle[i].goal = m_particle[i].pos; if (object != nullptr) { - if (object->GetType() == OBJECT_MOBILErs && dynamic_cast(object)->GetActiveShieldRadius() > 0.0f) + if (object->GetType() == OBJECT_MOBILErs && dynamic_cast(*object).GetActiveShieldRadius() > 0.0f) { CreateParticle(m_particle[i].pos, Math::Vector(0.0f, 0.0f, 0.0f), Math::Point(6.0f, 6.0f), PARTIGUNDEL, 2.0f); if (m_lastTimeGunDel > 0.2f) @@ -1285,7 +1285,7 @@ void CParticle::FrameParticle(float rTime) { if (object->Implements(ObjectInterfaceType::Damageable)) { - dynamic_cast(object)->DamageObject(DamageType::Fire, std::numeric_limits::infinity(), m_particle[i].objFather); // starts explosion + dynamic_cast(*object).DamageObject(DamageType::Fire, std::numeric_limits::infinity(), m_particle[i].objFather); // starts explosion } } } @@ -1344,7 +1344,7 @@ void CParticle::FrameParticle(float rTime) { if (object->Implements(ObjectInterfaceType::Damageable)) { - dynamic_cast(object)->DamageObject(DamageType::Organic, 0.001f, m_particle[i].objFather); + dynamic_cast(*object).DamageObject(DamageType::Organic, 0.001f, m_particle[i].objFather); } m_exploGunCounter ++; @@ -2422,7 +2422,7 @@ void CParticle::FrameParticle(float rTime) if (object != nullptr) { assert(object->Implements(ObjectInterfaceType::Damageable)); - dynamic_cast(object)->DamageObject(DamageType::Tower, std::numeric_limits::infinity(), m_particle[i].objFather); + dynamic_cast(*object).DamageObject(DamageType::Tower, std::numeric_limits::infinity(), m_particle[i].objFather); } } @@ -3543,6 +3543,7 @@ CObject* CParticle::SearchObjectGun(Math::Vector old, Math::Vector pos, continue; } if (!obj->Implements(ObjectInterfaceType::Damageable) && !obj->IsBulletWall()) continue; + if (obj->Implements(ObjectInterfaceType::Jostleable)) continue; Math::Vector oPos = obj->GetPosition(); diff --git a/src/graphics/engine/pyro.cpp b/src/graphics/engine/pyro.cpp index b7e2f043..33c144b8 100644 --- a/src/graphics/engine/pyro.cpp +++ b/src/graphics/engine/pyro.cpp @@ -129,7 +129,7 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) CObject* power = nullptr; if (obj->Implements(ObjectInterfaceType::Powered)) - power = dynamic_cast(obj)->GetPower(); + power = dynamic_cast(*obj).GetPower(); if (power == nullptr) { @@ -260,7 +260,7 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) m_sound->Play(SOUND_DEADw, m_pos); } assert(m_object->Implements(ObjectInterfaceType::Controllable)); - if ( type == PT_SHOTH && dynamic_cast(m_object)->GetSelect() ) + if ( type == PT_SHOTH && dynamic_cast(*m_object).GetSelect() ) { m_sound->Play(SOUND_AIE, m_pos); m_sound->Play(SOUND_AIE, m_engine->GetEyePt()); @@ -278,10 +278,10 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) if ( m_type == PT_DEADG ) { assert(m_object->Implements(ObjectInterfaceType::Destroyable)); - dynamic_cast(m_object)->SetDying(DeathType::Dead); + dynamic_cast(*m_object).SetDying(DeathType::Dead); assert(obj->Implements(ObjectInterfaceType::Movable)); - dynamic_cast(obj)->GetMotion()->SetAction(MHS_DEADg, 1.0f); + dynamic_cast(*obj).GetMotion()->SetAction(MHS_DEADg, 1.0f); m_camera->StartCentering(m_object, Math::PI*0.5f, 99.9f, 0.0f, 1.5f); m_camera->StartOver(CAM_OVER_EFFECT_FADEOUT_WHITE, m_pos, 1.0f); @@ -291,10 +291,10 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) if ( m_type == PT_DEADW ) { assert(m_object->Implements(ObjectInterfaceType::Destroyable)); - dynamic_cast(m_object)->SetDying(DeathType::Dead); + dynamic_cast(*m_object).SetDying(DeathType::Dead); assert(obj->Implements(ObjectInterfaceType::Movable)); - dynamic_cast(obj)->GetMotion()->SetAction(MHS_DEADw, 1.0f); + dynamic_cast(*obj).GetMotion()->SetAction(MHS_DEADw, 1.0f); m_camera->StartCentering(m_object, Math::PI*0.5f, 99.9f, 0.0f, 3.0f); m_camera->StartOver(CAM_OVER_EFFECT_FADEOUT_BLACK, m_pos, 1.0f); @@ -312,7 +312,7 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) if ( m_type == PT_SHOTH ) { assert(m_object->Implements(ObjectInterfaceType::Controllable)); - if ( m_camera->GetBlood() && dynamic_cast(m_object)->GetSelect() ) + if ( m_camera->GetBlood() && dynamic_cast(*m_object).GetSelect() ) { m_camera->StartOver(CAM_OVER_EFFECT_BLOOD, m_pos, force); } @@ -356,6 +356,12 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) if (oType == OBJECT_APOLLO2) limit = 2.0f; m_speed = 1.0f/limit; } + if ( m_type == PT_SQUASH ) + { + m_speed = 1.0f/2.0f; + m_object->SetLock(true); + } + if ( m_type == PT_EXPLOT || m_type == PT_EXPLOO || @@ -399,7 +405,8 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) if ( m_type != PT_FRAGV && m_type != PT_EGG && m_type != PT_WIN && - m_type != PT_LOST ) + m_type != PT_LOST && + m_type != PT_SQUASH) { float h = 40.0f; if ( m_type == PT_FRAGO || @@ -454,7 +461,8 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) m_type != PT_FLCREATE && m_type != PT_FLDELETE && m_type != PT_RESET && - m_type != PT_FINDING ) + m_type != PT_FINDING && + m_type != PT_SQUASH ) { m_camera->StartEffect(CAM_EFFECT_EXPLO, m_pos, force); } @@ -1049,6 +1057,11 @@ bool CPyro::EventProcess(const Event &event) } } + if ( m_type == PT_SQUASH && m_object != nullptr ) + { + m_object->SetScaleY(1.0f-sinf(m_progress)*0.5f); + } + if ( (m_type == PT_BURNT || m_type == PT_BURNO) && m_object != nullptr ) { @@ -1229,6 +1242,11 @@ Error CPyro::IsEnded() m_object->SetScale(1.0f); } + if ( m_type == PT_SQUASH ) + { + m_object->SetType(OBJECT_PLANT19); + } + if ( m_lightRank != -1 ) { m_lightMan->DeleteLight(m_lightRank); @@ -1395,7 +1413,7 @@ void CPyro::DeleteObject(bool primary, bool secondary) if (m_object->Implements(ObjectInterfaceType::Transportable)) { // TODO: this should be handled in the object's destructor - CObject* transporter = dynamic_cast(m_object)->GetTransporter(); + CObject* transporter = dynamic_cast(*m_object).GetTransporter(); if (transporter != nullptr) { if (transporter->Implements(ObjectInterfaceType::Powered)) @@ -1564,12 +1582,12 @@ void CPyro::ExploStart() m_object->Simplify(); m_object->SetLock(true); // ruin not usable yet assert(m_object->Implements(ObjectInterfaceType::Destroyable)); - dynamic_cast(m_object)->SetDying(DeathType::Exploding); // being destroyed + dynamic_cast(*m_object).SetDying(DeathType::Exploding); // being destroyed m_object->FlatParent(); - if ( m_object->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(m_object)->GetSelect() ) + if ( m_object->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(*m_object).GetSelect() ) { - dynamic_cast(m_object)->SetSelect(false); // deselects the object + dynamic_cast(*m_object).SetSelect(false); // deselects the object m_camera->SetType(CAM_TYPE_EXPLO); m_main->DeselectAll(); } @@ -1593,7 +1611,7 @@ void CPyro::ExploStart() // TODO: temporary hack (hopefully) assert(m_object->Implements(ObjectInterfaceType::Old)); - Math::Vector pos = dynamic_cast(m_object)->GetPartPosition(i); + Math::Vector pos = dynamic_cast(*m_object).GetPartPosition(i); Math::Vector speed; float weight; @@ -1640,9 +1658,9 @@ void CPyro::BurnStart() m_object->Simplify(); m_object->SetLock(true); // ruin not usable yet - if ( m_object->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(m_object)->GetSelect() ) + if ( m_object->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(*m_object).GetSelect() ) { - dynamic_cast(m_object)->SetSelect(false); // deselects the object + dynamic_cast(*m_object).SetSelect(false); // deselects the object m_camera->SetType(CAM_TYPE_EXPLO); m_main->DeselectAll(); } @@ -2180,7 +2198,7 @@ void CPyro::BurnProgress() if (m_object->Implements(ObjectInterfaceType::Powered)) { - CObject* sub = dynamic_cast(m_object)->GetPower(); + CObject* sub = dynamic_cast(*m_object).GetPower(); if (sub != nullptr) // is there a battery? sub->SetScaleY(1.0f - m_progress); // complete flattening } @@ -2274,7 +2292,7 @@ CObject* CPyro::FallSearchBeeExplo() if (obj->GetType() == OBJECT_MOBILErs) { - float shieldRadius = dynamic_cast(obj)->GetActiveShieldRadius(); + float shieldRadius = dynamic_cast(*obj).GetActiveShieldRadius(); if ( shieldRadius > 0.0f ) { float distance = Math::Distance(oPos, bulletCrashSphere.sphere.pos); @@ -2342,12 +2360,12 @@ void CPyro::FallProgress(float rTime) { assert(m_object->Implements(ObjectInterfaceType::Destroyable)); // TODO: implement "killer"? - dynamic_cast(m_object)->DestroyObject(DestructionType::Explosion); + dynamic_cast(*m_object).DestroyObject(DestructionType::Explosion); } } else { - if (obj->GetType() == OBJECT_MOBILErs && dynamic_cast(obj)->GetActiveShieldRadius() > 0.0f) // protected by shield? + if (obj->GetType() == OBJECT_MOBILErs && dynamic_cast(*obj).GetActiveShieldRadius() > 0.0f) // protected by shield? { m_particle->CreateParticle(pos, Math::Vector(0.0f, 0.0f, 0.0f), Math::Point(6.0f, 6.0f), PARTIGUNDEL, 2.0f, 0.0f, 0.0f); @@ -2358,7 +2376,7 @@ void CPyro::FallProgress(float rTime) else { assert(obj->Implements(ObjectInterfaceType::Damageable)); - if (dynamic_cast(obj)->DamageObject(DamageType::FallingObject)) + if (dynamic_cast(*obj).DamageObject(DamageType::FallingObject)) { DeleteObject(true, true); // removes the ball } @@ -2366,7 +2384,7 @@ void CPyro::FallProgress(float rTime) { assert(m_object->Implements(ObjectInterfaceType::Destroyable)); // TODO: implement "killer"? - dynamic_cast(m_object)->DestroyObject(DestructionType::Explosion); + dynamic_cast(*m_object).DestroyObject(DestructionType::Explosion); } } } diff --git a/src/graphics/engine/pyro_type.h b/src/graphics/engine/pyro_type.h index bef6cbaf..0336be71 100644 --- a/src/graphics/engine/pyro_type.h +++ b/src/graphics/engine/pyro_type.h @@ -59,6 +59,7 @@ enum PyroType PT_DEADW = 25, //! < drowning death PT_FINDING = 26, //! < object discovered PT_FRAGV = 27, //! < fragmentation of plant object + PT_SQUASH = 28, //! < flattening plants }; } // namespace Gfx diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index 484a7374..fef37333 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -191,17 +191,32 @@ CText::~CText() bool CText::Create() { - CFontLoader fontLoader; - if (!fontLoader.Init()) - { - GetLogger()->Warn("Error on parsing fonts config file: failed to open file\n"); - } if (TTF_Init() != 0) { m_error = std::string("TTF_Init error: ") + std::string(TTF_GetError()); return false; } + if (!ReloadFonts()) + { + return false; + } + + return true; +} + +bool CText::ReloadFonts() +{ + CFontLoader fontLoader; + if (!fontLoader.Init()) + { + GetLogger()->Debug("Error on parsing fonts config file: failed to open file\n"); + } + + // Backup previous fonts + auto fonts = std::move(m_fonts); + m_fonts.clear(); + for (auto type : {FONT_COMMON, FONT_STUDIO, FONT_SATCOM}) { m_fonts[static_cast(type)] = MakeUnique(fontLoader.GetFont(type)); @@ -214,7 +229,10 @@ bool CText::Create() FontType type = (*it).first; CachedFont* cf = GetOrOpenFont(type, m_defaultSize); if (cf == nullptr || cf->font == nullptr) + { + m_fonts = std::move(fonts); return false; + } } return true; @@ -430,7 +448,7 @@ float CText::GetStringWidth(const std::string &text, UTF8Char ch; - int len = StrUtils::Utf8CharSizeAt(text, index); + int len = GetCharSizeAt(font, text, index); if (len >= 1) ch.c1 = text[index]; if (len >= 2) @@ -441,7 +459,7 @@ float CText::GetStringWidth(const std::string &text, width += GetCharWidth(ch, font, size, width); index += len; - fmtIndex++; + fmtIndex += len; } return width; @@ -565,7 +583,7 @@ int CText::Justify(const std::string &text, std::vector::iterator UTF8Char ch; - int len = StrUtils::Utf8CharSizeAt(text, index); + int len = GetCharSizeAt(font, text, index); if (len >= 1) ch.c1 = text[index]; if (len >= 2) @@ -589,7 +607,7 @@ int CText::Justify(const std::string &text, std::vector::iterator } index += len; - fmtIndex++; + fmtIndex += len; } return index; @@ -606,7 +624,7 @@ int CText::Justify(const std::string &text, FontType font, float size, float wid { UTF8Char ch; - int len = StrUtils::Utf8CharSizeAt(text, index); + int len = GetCharSizeAt(font, text, index); if (len >= 1) ch.c1 = text[index]; if (len >= 2) @@ -648,12 +666,9 @@ int CText::Detect(const std::string &text, std::vector::iterator f if (format + fmtIndex != end) font = static_cast(*(format + fmtIndex) & FONT_MASK_FONT); - // TODO: if (font == FONT_BUTTON) - //if (font == FONT_BUTTON) continue; - UTF8Char ch; - int len = StrUtils::Utf8CharSizeAt(text, index); + int len = GetCharSizeAt(font, text, index); if (len >= 1) ch.c1 = text[index]; if (len >= 2) @@ -670,7 +685,7 @@ int CText::Detect(const std::string &text, std::vector::iterator f pos += width; index += len; - fmtIndex++; + fmtIndex += len; } return index; @@ -686,7 +701,7 @@ int CText::Detect(const std::string &text, FontType font, float size, float offs { UTF8Char ch; - int len = StrUtils::Utf8CharSizeAt(text, index); + int len = GetCharSizeAt(font, text, index); if (len >= 1) ch.c1 = text[index]; if (len >= 2) @@ -898,16 +913,7 @@ void CText::StringToUTFCharList(const std::string &text, std::vector & if (format + index != end) font = static_cast(*(format + index) & FONT_MASK_FONT); - int len; - - if(font == FONT_BUTTON) - { - len = 1; - } - else - { - len = StrUtils::Utf8CharSizeAt(text, index); - } + int len = GetCharSizeAt(font, text, index); if (len >= 1) ch.c1 = text[index]; @@ -922,6 +928,20 @@ void CText::StringToUTFCharList(const std::string &text, std::vector & } } +int CText::GetCharSizeAt(Gfx::FontType font, const std::string& text, unsigned int index) const +{ + int len = 0; + if (font == FONT_BUTTON) + { + len = 1; + } + else + { + len = StrUtils::Utf8CharSizeAt(text, index); + } + return len; +} + void CText::DrawString(const std::string &text, FontType font, float size, Math::IntPoint pos, int width, int eol, Color color) { diff --git a/src/graphics/engine/text.h b/src/graphics/engine/text.h index 3a5bff88..dc1be3db 100644 --- a/src/graphics/engine/text.h +++ b/src/graphics/engine/text.h @@ -256,6 +256,8 @@ public: //! Flushes cached textures void FlushCache(); + //! Try to load new font files + bool ReloadFonts(); //@{ //! Tab size management @@ -337,6 +339,8 @@ protected: void StringToUTFCharList(const std::string &text, std::vector &chars); void StringToUTFCharList(const std::string &text, std::vector &chars, std::vector::iterator format, std::vector::iterator end); + int GetCharSizeAt(Gfx::FontType font, const std::string& text, unsigned int index) const; + protected: CEngine* m_engine; CDevice* m_device; diff --git a/src/graphics/opengl/gl21device.cpp b/src/graphics/opengl/gl21device.cpp index b77a8f81..e0782271 100644 --- a/src/graphics/opengl/gl21device.cpp +++ b/src/graphics/opengl/gl21device.cpp @@ -388,6 +388,7 @@ bool CGL21Device::Create() uni.modelMatrix = glGetUniformLocation(m_normalProgram, "uni_ModelMatrix"); uni.normalMatrix = glGetUniformLocation(m_normalProgram, "uni_NormalMatrix"); uni.shadowMatrix = glGetUniformLocation(m_normalProgram, "uni_ShadowMatrix"); + uni.cameraPosition = glGetUniformLocation(m_normalProgram, "uni_CameraPosition"); uni.primaryTexture = glGetUniformLocation(m_normalProgram, "uni_PrimaryTexture"); uni.secondaryTexture = glGetUniformLocation(m_normalProgram, "uni_SecondaryTexture"); @@ -408,6 +409,7 @@ bool CGL21Device::Create() uni.fogColor = glGetUniformLocation(m_normalProgram, "uni_FogColor"); uni.shadowColor = glGetUniformLocation(m_normalProgram, "uni_ShadowColor"); + uni.shadowTexelSize = glGetUniformLocation(m_normalProgram, "uni_ShadowTexelSize"); uni.lightCount = glGetUniformLocation(m_normalProgram, "uni_LightCount"); uni.ambientColor = glGetUniformLocation(m_normalProgram, "uni_Material.ambient"); @@ -441,6 +443,7 @@ bool CGL21Device::Create() glUniformMatrix4fv(uni.modelMatrix, 1, GL_FALSE, matrix.Array()); glUniformMatrix4fv(uni.normalMatrix, 1, GL_FALSE, matrix.Array()); glUniformMatrix4fv(uni.shadowMatrix, 1, GL_FALSE, matrix.Array()); + glUniform3f(uni.cameraPosition, 0.0f, 0.0f, 0.0f); glUniform1i(uni.primaryTexture, 0); glUniform1i(uni.secondaryTexture, 1); @@ -457,6 +460,7 @@ bool CGL21Device::Create() glUniform4f(uni.fogColor, 0.8f, 0.8f, 0.8f, 1.0f); glUniform1f(uni.shadowColor, 0.5f); + glUniform1f(uni.shadowTexelSize, 0.5f); glUniform1i(uni.lightCount, 0); } @@ -653,6 +657,7 @@ void CGL21Device::SetTransform(TransformType type, const Math::Matrix &matrix) else if (type == TRANSFORM_VIEW) { Math::Matrix scale; + Math::Vector cameraPosition; scale.Set(3, 3, -1.0f); m_viewMat = Math::MultiplyMatrices(scale, matrix); @@ -660,6 +665,13 @@ void CGL21Device::SetTransform(TransformType type, const Math::Matrix &matrix) m_combinedMatrix = Math::MultiplyMatrices(m_projectionMat, m_modelviewMat); glUniformMatrix4fv(m_uniforms[m_mode].viewMatrix, 1, GL_FALSE, m_viewMat.Array()); + + if (m_uniforms[m_mode].cameraPosition >= 0) + { + cameraPosition.LoadZero(); + cameraPosition = MatrixVectorMultiply(m_viewMat.Inverse(), cameraPosition); + glUniform3fv(m_uniforms[m_mode].cameraPosition, 1, cameraPosition.Array()); + } } else if (type == TRANSFORM_PROJECTION) { @@ -1439,6 +1451,7 @@ void CGL21Device::SetRenderState(RenderState state, bool enabled) } else if (state == RENDER_STATE_SHADOW_MAPPING) { + glUniform1f(m_uniforms[m_mode].shadowTexelSize, 1.0/m_currentTextures[TEXTURE_SHADOW].size.x); SetTextureEnabled(TEXTURE_SHADOW, enabled); return; diff --git a/src/graphics/opengl/gl33device.cpp b/src/graphics/opengl/gl33device.cpp index cfcb28f2..bd26d35b 100644 --- a/src/graphics/opengl/gl33device.cpp +++ b/src/graphics/opengl/gl33device.cpp @@ -363,6 +363,7 @@ bool CGL33Device::Create() uni.modelMatrix = glGetUniformLocation(m_normalProgram, "uni_ModelMatrix"); uni.normalMatrix = glGetUniformLocation(m_normalProgram, "uni_NormalMatrix"); uni.shadowMatrix = glGetUniformLocation(m_normalProgram, "uni_ShadowMatrix"); + uni.cameraPosition = glGetUniformLocation(m_normalProgram, "uni_CameraPosition"); uni.primaryTexture = glGetUniformLocation(m_normalProgram, "uni_PrimaryTexture"); uni.secondaryTexture = glGetUniformLocation(m_normalProgram, "uni_SecondaryTexture"); @@ -420,6 +421,7 @@ bool CGL33Device::Create() glUniformMatrix4fv(uni.modelMatrix, 1, GL_FALSE, matrix.Array()); glUniformMatrix4fv(uni.normalMatrix, 1, GL_FALSE, matrix.Array()); glUniformMatrix4fv(uni.shadowMatrix, 1, GL_FALSE, matrix.Array()); + glUniform3f(uni.cameraPosition, 0.0f, 0.0f, 0.0f); glUniform1i(uni.primaryTexture, 0); glUniform1i(uni.secondaryTexture, 1); @@ -660,6 +662,7 @@ void CGL33Device::SetTransform(TransformType type, const Math::Matrix &matrix) else if (type == TRANSFORM_VIEW) { Math::Matrix scale; + Math::Vector cameraPosition; scale.Set(3, 3, -1.0f); m_viewMat = Math::MultiplyMatrices(scale, matrix); @@ -667,6 +670,13 @@ void CGL33Device::SetTransform(TransformType type, const Math::Matrix &matrix) m_combinedMatrixOutdated = true; glUniformMatrix4fv(m_uni->viewMatrix, 1, GL_FALSE, m_viewMat.Array()); + + if (m_uni->cameraPosition >= 0) + { + cameraPosition.LoadZero(); + cameraPosition = MatrixVectorMultiply(m_viewMat.Inverse(), cameraPosition); + glUniform3fv(m_uni->cameraPosition, 1, cameraPosition.Array()); + } } else if (type == TRANSFORM_PROJECTION) { @@ -876,6 +886,8 @@ void CGL33Device::UpdateTexture(const Texture& texture, Math::IntPoint offset, I glTexSubImage2D(GL_TEXTURE_2D, 0, offset.x, offset.y, texData.actualSurface->w, texData.actualSurface->h, texData.sourceFormat, GL_UNSIGNED_BYTE, texData.actualSurface->pixels); + glGenerateMipmap(GL_TEXTURE_2D); + SDL_FreeSurface(texData.convertedSurface); } diff --git a/src/graphics/opengl/glutil.h b/src/graphics/opengl/glutil.h index 8c67c378..f92888a4 100644 --- a/src/graphics/opengl/glutil.h +++ b/src/graphics/opengl/glutil.h @@ -167,6 +167,8 @@ struct UniformLocations GLint shadowMatrix = -1; //! Normal matrix GLint normalMatrix = -1; + //! Camera position + GLint cameraPosition = -1; //! Primary texture sampler GLint primaryTexture = -1; @@ -193,6 +195,8 @@ struct UniformLocations //! Shadow color GLint shadowColor = -1; + //! Shadow texel size + GLint shadowTexelSize = -1; // Number of enabled lights GLint lightCount = -1; diff --git a/src/graphics/opengl/shaders/gl21/fs_normal.glsl b/src/graphics/opengl/shaders/gl21/fs_normal.glsl index d21bc9cf..1aeffcbe 100644 --- a/src/graphics/opengl/shaders/gl21/fs_normal.glsl +++ b/src/graphics/opengl/shaders/gl21/fs_normal.glsl @@ -35,6 +35,7 @@ uniform vec2 uni_FogRange; uniform vec4 uni_FogColor; uniform float uni_ShadowColor; +uniform float uni_ShadowTexelSize; struct LightParams { @@ -56,6 +57,7 @@ uniform Material uni_Material; uniform int uni_LightCount; uniform LightParams uni_Light[4]; +varying vec3 pass_CameraDirection; varying float pass_Distance; varying vec4 pass_Color; varying vec3 pass_Normal; @@ -76,16 +78,17 @@ void main() vec4 specular = vec4(0.0f); vec3 normal = normalize(pass_Normal); + vec3 camera = normalize(pass_CameraDirection); for (int i = 0; i < uni_LightCount; i++) { LightParams light = uni_Light[i]; vec3 lightDirection = light.Position.xyz; - vec3 reflectDirection = -reflect(lightDirection, normal); + vec3 reflectAxis = normalize(normalize(lightDirection) + camera); float diffuseComponent = clamp(dot(normal, lightDirection), 0.0f, 1.0f); - float specularComponent = clamp(pow(dot(normal, lightDirection + reflectDirection), 10.0f), 0.0f, 1.0f); + float specularComponent = pow(clamp(dot(normal, reflectAxis), 0.0f, 1.0f), 10.0f); ambient += light.Ambient; diffuse += diffuseComponent * light.Diffuse; @@ -97,13 +100,11 @@ void main() 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); + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3( uni_ShadowTexelSize, 0.0f, 0.0f)).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3(-uni_ShadowTexelSize, 0.0f, 0.0f)).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3( 0.0f, uni_ShadowTexelSize, 0.0f)).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3( 0.0f, -uni_ShadowTexelSize, 0.0f)).x); shadow = mix(uni_ShadowColor, 1.0f, value); #else diff --git a/src/graphics/opengl/shaders/gl21/vs_normal.glsl b/src/graphics/opengl/shaders/gl21/vs_normal.glsl index 9195cd20..41141c4b 100644 --- a/src/graphics/opengl/shaders/gl21/vs_normal.glsl +++ b/src/graphics/opengl/shaders/gl21/vs_normal.glsl @@ -24,7 +24,9 @@ uniform mat4 uni_ViewMatrix; uniform mat4 uni_ModelMatrix; uniform mat4 uni_ShadowMatrix; uniform mat4 uni_NormalMatrix; +uniform vec3 uni_CameraPosition; +varying vec3 pass_CameraDirection; varying float pass_Distance; varying vec4 pass_Color; varying vec3 pass_Normal; @@ -40,6 +42,7 @@ void main() gl_Position = uni_ProjectionMatrix * eyeSpace; + pass_CameraDirection = uni_CameraPosition - position.xyz; pass_Color = gl_Color; pass_Normal = normalize((uni_NormalMatrix * vec4(gl_Normal, 0.0f)).xyz); pass_Distance = abs(eyeSpace.z / eyeSpace.w); diff --git a/src/graphics/opengl/shaders/gl33/fs_normal.glsl b/src/graphics/opengl/shaders/gl33/fs_normal.glsl index e6fd1e07..be58f4fd 100644 --- a/src/graphics/opengl/shaders/gl33/fs_normal.glsl +++ b/src/graphics/opengl/shaders/gl33/fs_normal.glsl @@ -63,6 +63,7 @@ in VertexData vec4 ShadowCoord; vec4 LightColor; float Distance; + vec3 CameraDirection; } data; out vec4 out_FragColor; @@ -78,17 +79,17 @@ void main() vec4 specular = vec4(0.0f); vec3 normal = normalize(data.Normal); + vec3 camera = normalize(data.CameraDirection); for (int i = 0; i < uni_LightCount; i++) { vec3 lightDirection = uni_Light[i].Position.xyz; - - vec3 reflectDirection = -reflect(lightDirection, normal); + vec3 reflectAxis = normalize(normalize(lightDirection) + camera); 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) + specular += pow(clamp(dot(normal, reflectAxis), 0.0f, 1.0f), 10.0f) * uni_Light[i].Specular; } diff --git a/src/graphics/opengl/shaders/gl33/vs_normal.glsl b/src/graphics/opengl/shaders/gl33/vs_normal.glsl index aeed3c60..216682f7 100644 --- a/src/graphics/opengl/shaders/gl33/vs_normal.glsl +++ b/src/graphics/opengl/shaders/gl33/vs_normal.glsl @@ -25,6 +25,7 @@ uniform mat4 uni_ViewMatrix; uniform mat4 uni_ModelMatrix; uniform mat4 uni_ShadowMatrix; uniform mat4 uni_NormalMatrix; +uniform vec3 uni_CameraPosition; layout(location = 0) in vec4 in_VertexCoord; layout(location = 1) in vec3 in_Normal; @@ -41,6 +42,7 @@ out VertexData vec4 ShadowCoord; vec4 LightColor; float Distance; + vec3 CameraDirection; } data; void main() @@ -56,4 +58,5 @@ void main() 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); + data.CameraDirection = uni_CameraPosition - position.xyz; } diff --git a/src/level/build_type.h b/src/level/build_type.h index cabe4159..26c595e4 100644 --- a/src/level/build_type.h +++ b/src/level/build_type.h @@ -41,7 +41,8 @@ enum BuildType BUILD_LABO = (1<<10), //! < AutoLab BUILD_PARA = (1<<11), //! < PowerCaptor BUILD_INFO = (1<<12), //! < ExchangePost - BUILD_DESTROYER = (1<<13), //! < Destroyer + BUILD_SAFE = (1<<13), //! < Vault + BUILD_DESTROYER = (1<<14), //! < Destroyer BUILD_GFLAT = (1<<16), //! < checking flat ground BUILD_FLAG = (1<<17) //! < putting / removing flags }; diff --git a/src/level/level_category.cpp b/src/level/level_category.cpp index 7ab27d83..e34925a3 100644 --- a/src/level/level_category.cpp +++ b/src/level/level_category.cpp @@ -25,10 +25,11 @@ // TODO: I'm not sure about "challenges" + "custom". It may be messing things up already right now. const std::map CATEGORY_DIR_MAP = { { LevelCategory::Missions, "missions" }, - { LevelCategory::FreeGame, "freemissions" }, + { LevelCategory::FreeGame, "free" }, { LevelCategory::Exercises, "exercises" }, { LevelCategory::Challenges, "challenges" }, { LevelCategory::CodeBattles, "battles" }, + { LevelCategory::GamePlus, "plus" }, { LevelCategory::CustomLevels, "custom" }, }; diff --git a/src/level/level_category.h b/src/level/level_category.h index 9d960c1b..39920914 100644 --- a/src/level/level_category.h +++ b/src/level/level_category.h @@ -28,6 +28,7 @@ enum class LevelCategory Missions, FreeGame, CodeBattles, + GamePlus, CustomLevels, Max, }; diff --git a/src/level/mainmovie.cpp b/src/level/mainmovie.cpp index 045a64ed..6a34510d 100644 --- a/src/level/mainmovie.cpp +++ b/src/level/mainmovie.cpp @@ -90,7 +90,7 @@ bool CMainMovie::Start(MainMovieType type, float time) } assert(pObj->Implements(ObjectInterfaceType::Movable)); - dynamic_cast(pObj)->GetMotion()->SetAction(MHS_SATCOM, 0.5f); // reads the SatCom + dynamic_cast(*pObj).GetMotion()->SetAction(MHS_SATCOM, 0.5f); // reads the SatCom m_camera->GetCamera(m_initialEye, m_initialLookat); m_camera->SetType(Gfx::CAM_TYPE_SCRIPT); @@ -110,7 +110,7 @@ bool CMainMovie::Start(MainMovieType type, float time) if ( pObj != nullptr ) { assert(pObj->Implements(ObjectInterfaceType::Movable)); - dynamic_cast(pObj)->GetMotion()->SetAction(-1); // finishes reading SatCom + dynamic_cast(*pObj).GetMotion()->SetAction(-1); // finishes reading SatCom } m_camera->SetType(Gfx::CAM_TYPE_BACK); @@ -132,7 +132,7 @@ bool CMainMovie::Stop() if ( pObj != nullptr ) { assert(pObj->Implements(ObjectInterfaceType::Movable)); - dynamic_cast(pObj)->GetMotion()->SetAction(-1); // finishes reading SatCom + dynamic_cast(*pObj).GetMotion()->SetAction(-1); // finishes reading SatCom } } diff --git a/src/level/parser/parser.cpp b/src/level/parser/parser.cpp index e8f871bd..1bf78a24 100644 --- a/src/level/parser/parser.cpp +++ b/src/level/parser/parser.cpp @@ -41,6 +41,7 @@ #include #include #include +#include CLevelParser::CLevelParser() { @@ -172,13 +173,28 @@ void CLevelParser::Load() boost::replace_all(line, "\t", " "); // replace tab by space // ignore comments - std::size_t comment = line.find("//"); - if (comment != std::string::npos) - line = line.substr(0, comment); + size_t pos = 0; + std::string linesuffix = line; + boost::regex commentRegex{ R"(("[^"]*")|('[^']*')|(//.*$))" }; + boost::smatch matches; + while (boost::regex_search(linesuffix, matches, commentRegex)) + { + if (matches[3].matched) + { + pos += std::distance(linesuffix.cbegin(), matches.prefix().second); + line = line.substr(0, pos); + linesuffix = ""; + } + else + { + pos += std::distance(linesuffix.cbegin(), matches.suffix().first); + linesuffix = matches.suffix().str(); + } + } boost::algorithm::trim(line); - std::size_t pos = line.find_first_of(" \t\n"); + pos = line.find_first_of(" \t\n"); std::string command = line.substr(0, pos); if (pos != std::string::npos) { diff --git a/src/level/parser/parserparam.cpp b/src/level/parser/parserparam.cpp index 083ce47a..159ab5f7 100644 --- a/src/level/parser/parserparam.cpp +++ b/src/level/parser/parserparam.cpp @@ -885,6 +885,7 @@ int CLevelParserParam::ToBuildFlag(std::string value) if (value == "AutoLab" ) return BUILD_LABO; if (value == "PowerCaptor" ) return BUILD_PARA; if (value == "ExchangePost" ) return BUILD_INFO; + if (value == "Vault" ) return BUILD_SAFE; if (value == "Destroyer" ) return BUILD_DESTROYER; if (value == "FlatGround" ) return BUILD_GFLAT; if (value == "Flag" ) return BUILD_FLAG; diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 6bf29755..af04448c 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -320,6 +320,7 @@ std::string PhaseToString(Phase phase) if (phase == PHASE_APPERANCE) return "PHASE_APPERANCE"; if (phase == PHASE_MAIN_MENU) return "PHASE_MAIN_MENU"; if (phase == PHASE_LEVEL_LIST) return "PHASE_LEVEL_LIST"; + if (phase == PHASE_MOD_LIST) return "PHASE_MOD_LIST"; if (phase == PHASE_SIMUL) return "PHASE_SIMUL"; if (phase == PHASE_SETUPd) return "PHASE_SETUPd"; if (phase == PHASE_SETUPg) return "PHASE_SETUPg"; @@ -742,7 +743,7 @@ bool CRobotMain::ProcessEvent(Event &event) } else { - m_sound->SetMusicVolume(100); + m_sound->SetMusicVolume(MAXVOLUME*3/4); } // Set audio volume if (GetConfigFile().GetIntProperty("Setup", "AudioVolume", volume)) @@ -751,7 +752,7 @@ bool CRobotMain::ProcessEvent(Event &event) } else { - m_sound->SetAudioVolume(100); + m_sound->SetAudioVolume(MAXVOLUME); } } @@ -1360,7 +1361,7 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) { CObject* object = GetSelect(); if (object != nullptr && object->Implements(ObjectInterfaceType::Shielded)) - dynamic_cast(object)->SetMagnifyDamage(dynamic_cast(object)->GetMagnifyDamage()*0.1f); + dynamic_cast(*object).SetMagnifyDamage(dynamic_cast(*object).GetMagnifyDamage()*0.1f); return; } @@ -1368,7 +1369,7 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) { CObject* object = GetSelect(); if (object != nullptr && object->Implements(ObjectInterfaceType::JetFlying)) - dynamic_cast(object)->SetRange(dynamic_cast(object)->GetRange()*10.0f); + dynamic_cast(*object).SetRange(dynamic_cast(*object).GetRange()*10.0f); return; } @@ -1393,16 +1394,16 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) { if (object->Implements(ObjectInterfaceType::Powered)) { - CObject* power = dynamic_cast(object)->GetPower(); + CObject* power = dynamic_cast(*object).GetPower(); if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - dynamic_cast(power)->SetEnergyLevel(1.0f); + dynamic_cast(*power).SetEnergyLevel(1.0f); } if (object->Implements(ObjectInterfaceType::Shielded)) - dynamic_cast(object)->SetShield(1.0f); + dynamic_cast(*object).SetShield(1.0f); if (object->Implements(ObjectInterfaceType::JetFlying)) - dynamic_cast(object)->SetReactorRange(1.0f); + dynamic_cast(*object).SetReactorRange(1.0f); } return; } @@ -1415,9 +1416,9 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) { if (object->Implements(ObjectInterfaceType::Powered)) { - CObject* power = dynamic_cast(object)->GetPower(); + CObject* power = dynamic_cast(*object).GetPower(); if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - dynamic_cast(power)->SetEnergyLevel(1.0f); + dynamic_cast(*power).SetEnergyLevel(1.0f); } } return; @@ -1427,7 +1428,7 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) { CObject* object = GetSelect(); if (object != nullptr && object->Implements(ObjectInterfaceType::Shielded)) - dynamic_cast(object)->SetShield(1.0f); + dynamic_cast(*object).SetShield(1.0f); return; } @@ -1437,7 +1438,7 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) if (object != nullptr) { if (object->Implements(ObjectInterfaceType::JetFlying)) - dynamic_cast(object)->SetReactorRange(1.0f); + dynamic_cast(*object).SetReactorRange(1.0f); } return; } @@ -1537,7 +1538,7 @@ void CRobotMain::StartDisplayInfo(int index, bool movie) if (!m_editLock && movie && !m_movie->IsExist() && human) { assert(obj->Implements(ObjectInterfaceType::Movable)); - if (dynamic_cast(obj)->GetMotion()->GetAction() == -1) + if (dynamic_cast(*obj).GetMotion()->GetAction() == -1) { m_movieInfoIndex = index; m_movie->Start(MM_SATCOMopen, 2.5f); @@ -1851,7 +1852,7 @@ CObject* CRobotMain::DeselectAll() void CRobotMain::SelectOneObject(CObject* obj, bool displayError) { assert(obj->Implements(ObjectInterfaceType::Controllable)); - dynamic_cast(obj)->SetSelect(true, displayError); + dynamic_cast(*obj).SetSelect(true, displayError); m_camera->SetControllingObject(obj); ObjectType type = obj->GetType(); @@ -1890,7 +1891,7 @@ void CRobotMain::SelectOneObject(CObject* obj, bool displayError) type == OBJECT_MOBILEdr || type == OBJECT_APOLLO2 ) { - m_camera->SetType(dynamic_cast(obj)->GetCameraType()); + m_camera->SetType(dynamic_cast(*obj).GetCameraType()); } else { @@ -1906,7 +1907,7 @@ bool CRobotMain::SelectObject(CObject* obj, bool displayError) if (m_movieLock || m_editLock) return false; if (m_movie->IsExist()) return false; if (obj != nullptr && - (!obj->Implements(ObjectInterfaceType::Controllable) || !(dynamic_cast(obj)->GetSelectable() || m_cheatSelectInsect))) return false; + (!obj->Implements(ObjectInterfaceType::Controllable) || !(dynamic_cast(*obj).GetSelectable() || m_cheatSelectInsect))) return false; if (m_missionType == MISSION_CODE_BATTLE && m_codeBattleStarted && m_codeBattleSpectator) { @@ -1982,7 +1983,7 @@ CObject* CRobotMain::GetSelect() for (CObject* obj : m_objMan->GetAllObjects()) { if (!obj->Implements(ObjectInterfaceType::Controllable)) continue; - if (dynamic_cast(obj)->GetSelect()) + if (dynamic_cast(*obj).GetSelect()) return obj; } return nullptr; @@ -2000,7 +2001,7 @@ CObject* CRobotMain::DetectObject(Math::Point pos) CObject* transporter = nullptr; if (obj->Implements(ObjectInterfaceType::Transportable)) - transporter = dynamic_cast(obj)->GetTransporter(); + transporter = dynamic_cast(*obj).GetTransporter(); if (transporter != nullptr && !transporter->GetDetectable()) continue; if (obj->GetProxyActivate()) continue; @@ -2008,14 +2009,14 @@ CObject* CRobotMain::DetectObject(Math::Point pos) CObject* target = obj; if (obj->Implements(ObjectInterfaceType::PowerContainer) && obj->Implements(ObjectInterfaceType::Transportable)) { - target = dynamic_cast(obj)->GetTransporter(); // battery connected + target = dynamic_cast(*obj).GetTransporter(); // battery connected if (target == nullptr) { target = obj; // standalone battery } else { - if (!target->Implements(ObjectInterfaceType::Powered) || dynamic_cast(target)->GetPower() != obj) + if (!target->Implements(ObjectInterfaceType::Powered) || dynamic_cast(*target).GetPower() != obj) { // transported, but not in the power slot target = obj; @@ -2045,7 +2046,7 @@ bool CRobotMain::DestroySelectedObject() m_engine->GetPyroManager()->Create(Gfx::PT_FRAGT, obj); - dynamic_cast(obj)->SetSelect(false); // deselects the object + dynamic_cast(*obj).SetSelect(false); // deselects the object m_camera->SetType(Gfx::CAM_TYPE_EXPLO); DeselectAll(); RemoveFromSelectionHistory(obj); @@ -2068,7 +2069,7 @@ void CRobotMain::HiliteClear() for (CObject* obj : m_objMan->GetAllObjects()) { if (!obj->Implements(ObjectInterfaceType::Controllable)) continue; - dynamic_cast(obj)->SetHighlight(false); + dynamic_cast(*obj).SetHighlight(false); } m_map->SetHighlight(nullptr); m_short->SetHighlight(nullptr); @@ -2128,12 +2129,12 @@ void CRobotMain::HiliteObject(Math::Point pos) } } - if (obj->Implements(ObjectInterfaceType::Controllable) && (dynamic_cast(obj)->GetSelectable() || m_cheatSelectInsect)) + if (obj->Implements(ObjectInterfaceType::Controllable) && (dynamic_cast(*obj).GetSelectable() || m_cheatSelectInsect)) { - if (dynamic_cast(obj)->GetSelectable()) + if (dynamic_cast(*obj).GetSelectable()) { // Don't highlight objects that would not be selectable without selectinsect - dynamic_cast(obj)->SetHighlight(true); + dynamic_cast(*obj).SetHighlight(true); } m_map->SetHighlight(obj); m_short->SetHighlight(obj); @@ -2390,7 +2391,7 @@ bool CRobotMain::EventFrame(const Event &event) if (obj->GetType() == OBJECT_TOTO) toto = obj; else if (obj->Implements(ObjectInterfaceType::Interactive)) - dynamic_cast(obj)->EventProcess(event); + dynamic_cast(*obj).EventProcess(event); if ( obj->GetProxyActivate() ) // active if it is near? { @@ -2413,7 +2414,7 @@ bool CRobotMain::EventFrame(const Event &event) continue; if (obj->Implements(ObjectInterfaceType::Interactive)) - dynamic_cast(obj)->EventProcess(event); + dynamic_cast(*obj).EventProcess(event); } m_engine->GetPyroManager()->EventProcess(event); @@ -2437,7 +2438,7 @@ bool CRobotMain::EventFrame(const Event &event) // Advances toto following the camera, because its position depends on the camera. if (toto != nullptr) - dynamic_cast(toto)->EventProcess(event); + dynamic_cast(*toto).EventProcess(event); // NOTE: m_movieLock is set only after the first update of CAutoBase finishes @@ -2461,6 +2462,7 @@ bool CRobotMain::EventFrame(const Event &event) { if (m_levelCategory == LevelCategory::Missions || m_levelCategory == LevelCategory::FreeGame || + m_levelCategory == LevelCategory::GamePlus || m_levelCategory == LevelCategory::CustomLevels) { if (!IOIsBusy() && m_missionType != MISSION_CODE_BATTLE) @@ -2670,7 +2672,7 @@ bool CRobotMain::EventObject(const Event &event) { if (obj->Implements(ObjectInterfaceType::Interactive)) { - dynamic_cast(obj)->EventProcess(event); + dynamic_cast(*obj).EventProcess(event); } } @@ -2711,7 +2713,7 @@ void CRobotMain::ScenePerso() obj->SetDrawFront(true); // draws the interface assert(obj->Implements(ObjectInterfaceType::Movable)); - CMotionHuman* mh = static_cast(dynamic_cast(obj)->GetMotion()); + CMotionHuman* mh = static_cast(dynamic_cast(*obj).GetMotion()); mh->StartDisplayPerso(); } } @@ -3149,6 +3151,8 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) m_missionType = line->GetParam("type")->AsMissionType(MISSION_NORMAL); m_globalMagnifyDamage = line->GetParam("magnifyDamage")->AsFloat(1.0f); + m_globalNuclearCapacity = line->GetParam("nuclearCapacity")->AsFloat(10.0f); + m_globalCellCapacity = line->GetParam("cellCapacity")->AsFloat(1.0f); continue; } @@ -3379,7 +3383,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) assert(m_controller->Implements(ObjectInterfaceType::ProgramStorage)); assert(m_controller->Implements(ObjectInterfaceType::Old)); - dynamic_cast(m_controller)->SetCheckToken(false); + dynamic_cast(*m_controller).SetCheckToken(false); if (line->GetParam("script")->IsDefined()) { @@ -3387,7 +3391,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) Program* program = programStorage->AddProgram(); programStorage->ReadProgram(program, line->GetParam("script")->AsPath("ai")); program->readOnly = true; - dynamic_cast(m_controller)->RunProgram(program); + dynamic_cast(*m_controller).RunProgram(program); } continue; } @@ -3412,7 +3416,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) if (m_fixScene && obj->GetType() == OBJECT_HUMAN) { assert(obj->Implements(ObjectInterfaceType::Movable)); - CMotion* motion = dynamic_cast(obj)->GetMotion(); + CMotion* motion = dynamic_cast(*obj).GetMotion(); if (m_phase == PHASE_WIN ) motion->SetAction(MHS_WIN, 0.4f); if (m_phase == PHASE_LOST) motion->SetAction(MHS_LOST, 0.5f); } @@ -3427,7 +3431,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) { CProgramStorageObject* programStorage = dynamic_cast(obj); - if (obj->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(obj)->GetSelectable() && obj->GetType() != OBJECT_HUMAN) + if (obj->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(*obj).GetSelectable() && obj->GetType() != OBJECT_HUMAN) { programStorage->SetProgramStorageIndex(rankObj); } @@ -3781,6 +3785,12 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) m_build |= BUILD_FLAG; } + if (m_levelCategory == LevelCategory::GamePlus && !m_ui->GetPlusResearch() && !resetObject) // new game plus? + { + m_researchDone[0] |= m_playerProfile->GetFreeGameResearchUnlock(); + m_build |= m_playerProfile->GetFreeGameBuildUnlock(); + } + if (!resetObject) { m_short->SetMode(false); // vehicles? @@ -3810,7 +3820,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) assert(obj->Implements(ObjectInterfaceType::Controllable)); SelectObject(obj); m_camera->SetControllingObject(obj); - m_camera->SetType(dynamic_cast(obj)->GetCameraType()); + m_camera->SetType(dynamic_cast(*obj).GetCameraType()); } } @@ -3911,6 +3921,7 @@ void CRobotMain::ChangeColor() m_phase != PHASE_SETUPps && m_phase != PHASE_SETUPcs && m_phase != PHASE_SETUPss && + m_phase != PHASE_MOD_LIST && m_phase != PHASE_WIN && m_phase != PHASE_LOST && m_phase != PHASE_APPERANCE ) return; @@ -4406,7 +4417,7 @@ void CRobotMain::StartShowLimit() CObject* obj = GetSelect(); if (obj == nullptr) return; if (!obj->Implements(ObjectInterfaceType::Ranged)) return; - float range = dynamic_cast(obj)->GetShowLimitRadius(); + float range = dynamic_cast(*obj).GetShowLimitRadius(); if (range == 0.0f) return; SetShowLimit(0, Gfx::PARTILIMIT1, obj, obj->GetPosition(), range); } @@ -4576,8 +4587,8 @@ bool CRobotMain::IOIsBusy() { if (! obj->Implements(ObjectInterfaceType::TaskExecutor)) continue; - if (obj->Implements(ObjectInterfaceType::Programmable) && dynamic_cast(obj)->IsProgram()) continue; // TODO: I'm not sure if this is correct but this is how it worked earlier - if (dynamic_cast(obj)->IsForegroundTask()) return true; + if (obj->Implements(ObjectInterfaceType::Programmable) && dynamic_cast(*obj).IsProgram()) continue; // TODO: I'm not sure if this is correct but this is how it worked earlier + if (dynamic_cast(*obj).IsForegroundTask()) return true; } return false; } @@ -4628,7 +4639,7 @@ void CRobotMain::IOWriteObject(CLevelParserLine* line, CObject* obj, const std:: if (obj->Implements(ObjectInterfaceType::Programmable)) { - int run = dynamic_cast(obj)->GetProgramIndex(dynamic_cast(obj)->GetCurrentProgram()); + int run = dynamic_cast(*obj).GetProgramIndex(dynamic_cast(*obj).GetCurrentProgram()); if (run != -1) { line->AddParam("run", MakeUnique(run+1)); @@ -4703,11 +4714,11 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s { if (obj->GetType() == OBJECT_TOTO) continue; if (IsObjectBeingTransported(obj)) continue; - if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(obj)->IsDying()) continue; + if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(*obj).IsDying()) continue; if (obj->Implements(ObjectInterfaceType::Carrier)) { - CObject* cargo = dynamic_cast(obj)->GetCargo(); + CObject* cargo = dynamic_cast(*obj).GetCargo(); if (cargo != nullptr) // object transported? { line = MakeUnique("CreateFret"); @@ -4718,7 +4729,7 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s if (obj->Implements(ObjectInterfaceType::Powered)) { - CObject* power = dynamic_cast(obj)->GetPower(); + CObject* power = dynamic_cast(*obj).GetPower(); if (power != nullptr) // battery transported? { line = MakeUnique("CreatePower"); @@ -4757,7 +4768,7 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s { if (obj->GetType() == OBJECT_TOTO) continue; if (IsObjectBeingTransported(obj)) continue; - if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(obj)->IsDying()) continue; + if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(*obj).IsDying()) continue; if (!SaveFileStack(obj, ostr)) { @@ -4908,7 +4919,7 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) { assert(obj->Implements(ObjectInterfaceType::Carrier)); // TODO: exception? assert(obj->Implements(ObjectInterfaceType::Old)); - dynamic_cast(obj)->SetCargo(cargo); + dynamic_cast(*obj).SetCargo(cargo); auto task = MakeUnique(dynamic_cast(obj)); task->Start(TMO_AUTO, TMA_GRAB); // holds the object! } @@ -4916,9 +4927,9 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) if (power != nullptr) { assert(obj->Implements(ObjectInterfaceType::Powered)); - dynamic_cast(obj)->SetPower(power); + dynamic_cast(*obj).SetPower(power); assert(power->Implements(ObjectInterfaceType::Transportable)); - dynamic_cast(power)->SetTransporter(obj); + dynamic_cast(*power).SetTransporter(obj); } cargo = nullptr; power = nullptr; @@ -4950,7 +4961,7 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) { if (obj->GetType() == OBJECT_TOTO) continue; if (IsObjectBeingTransported(obj)) continue; - if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(obj)->IsDying()) continue; + if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(*obj).IsDying()) continue; if (!ReadFileStack(obj, istr)) { @@ -5307,7 +5318,7 @@ Error CRobotMain::CheckEndMission(bool frame) if (m_base != nullptr && !m_endTakeImmediat) { assert(m_base->Implements(ObjectInterfaceType::Controllable)); - if(dynamic_cast(m_base)->GetSelectable()) + if(dynamic_cast(*m_base).GetSelectable()) return ERR_MISSION_NOTERM; } } @@ -5347,6 +5358,16 @@ bool CRobotMain::GetTrainerPilot() return m_cheatTrainerPilot; } +bool CRobotMain::GetPlusTrainer() +{ + return m_ui->GetPlusTrainer(); +} + +bool CRobotMain::GetPlusExplorer() +{ + return m_ui->GetPlusExplorer(); +} + //! Indicates whether the scene is fixed, without interaction bool CRobotMain::GetFixScene() { @@ -5870,6 +5891,7 @@ bool CRobotMain::IsBuildingEnabled(ObjectType type) if(type == OBJECT_NUCLEAR) return IsBuildingEnabled(BUILD_NUCLEAR); if(type == OBJECT_INFO) return IsBuildingEnabled(BUILD_INFO); if(type == OBJECT_PARA) return IsBuildingEnabled(BUILD_PARA); + if(type == OBJECT_SAFE) return IsBuildingEnabled(BUILD_SAFE); if(type == OBJECT_DESTROYER) return IsBuildingEnabled(BUILD_DESTROYER); return false; @@ -5940,6 +5962,8 @@ Error CRobotMain::CanFactoryError(ObjectType type, int team) if (type == OBJECT_MOBILEst && !IsResearchDone(RESEARCH_SUBM, team)) return ERR_BUILD_DISABLED; if (type == OBJECT_MOBILEtg && !IsResearchDone(RESEARCH_TARGET, team)) return ERR_BUILD_RESEARCH; + if (tool == ToolType::Other && drive == DriveType::Other && type != OBJECT_MOBILEtg) return ERR_WRONG_OBJ; + return ERR_OK; } @@ -5976,6 +6000,16 @@ float CRobotMain::GetGlobalMagnifyDamage() return m_globalMagnifyDamage; } +float CRobotMain::GetGlobalNuclearCapacity() +{ + return m_globalNuclearCapacity; +} + +float CRobotMain::GetGlobalCellCapacity() +{ + return m_globalCellCapacity; +} + // Beginning of the effect when the instruction "detect" is used. void CRobotMain::StartDetectEffect(COldObject* object, CObject* target) diff --git a/src/level/robotmain.h b/src/level/robotmain.h index e95d6671..42590b50 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -55,6 +55,7 @@ enum Phase PHASE_APPERANCE, PHASE_MAIN_MENU, PHASE_LEVEL_LIST, + PHASE_MOD_LIST, PHASE_SIMUL, PHASE_SETUPd, PHASE_SETUPg, @@ -266,6 +267,8 @@ public: const std::string& GetScriptName(); const std::string& GetScriptFile(); bool GetTrainerPilot(); + bool GetPlusTrainer(); + bool GetPlusExplorer(); bool GetFixScene(); bool GetShowSoluce(); bool GetSceneSoluce(); @@ -467,6 +470,11 @@ public: //! Returns global magnifyDamage setting float GetGlobalMagnifyDamage(); + //! Returns global NuclearCell capacity Setting + float GetGlobalNuclearCapacity(); + //! Returns global PowerCell capacity setting + float GetGlobalCellCapacity(); + void StartDetectEffect(COldObject* object, CObject* target); //! Enable crash sphere debug rendering @@ -649,6 +657,9 @@ protected: float m_globalMagnifyDamage = 0.0f; + float m_globalNuclearCapacity = 10.0f; + float m_globalCellCapacity = 1.0f; + bool m_exitAfterMission = false; bool m_codeBattleInit = false; diff --git a/src/level/scene_conditions.cpp b/src/level/scene_conditions.cpp index 41a8a8f0..36e2bdd9 100644 --- a/src/level/scene_conditions.cpp +++ b/src/level/scene_conditions.cpp @@ -82,7 +82,7 @@ bool CObjectCondition::CheckForObject(CObject* obj) } else if (obj->Implements(ObjectInterfaceType::Powered)) { - CObject* powerObj = dynamic_cast(obj)->GetPower(); + CObject* powerObj = dynamic_cast(*obj).GetPower(); if(powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer)) { power = dynamic_cast(powerObj); @@ -98,7 +98,7 @@ bool CObjectCondition::CheckForObject(CObject* obj) Math::Vector oPos; if (IsObjectBeingTransported(obj)) - oPos = dynamic_cast(obj)->GetTransporter()->GetPosition(); + oPos = dynamic_cast(*obj).GetTransporter()->GetPosition(); else oPos = obj->GetPosition(); oPos.y = 0.0f; diff --git a/src/object/auto/autobase.cpp b/src/object/auto/autobase.cpp index d5522c56..7b908095 100644 --- a/src/object/auto/autobase.cpp +++ b/src/object/auto/autobase.cpp @@ -169,7 +169,7 @@ begin: else { assert(pObj->Implements(ObjectInterfaceType::Controllable)); - m_camera->SetType(dynamic_cast(pObj)->GetCameraType()); + m_camera->SetType(dynamic_cast(*pObj).GetCameraType()); } m_main->StartMusic(); @@ -594,7 +594,7 @@ begin: else { assert(pObj->Implements(ObjectInterfaceType::Controllable)); - m_camera->SetType(dynamic_cast(pObj)->GetCameraType()); + m_camera->SetType(dynamic_cast(*pObj).GetCameraType()); } m_sound->Play(SOUND_BOUM, m_object->GetPosition()); m_soundChannel = -1; @@ -1124,7 +1124,7 @@ bool CAutoBase::Abort() else { assert(pObj->Implements(ObjectInterfaceType::Controllable)); - m_camera->SetType(dynamic_cast(pObj)->GetCameraType()); + m_camera->SetType(dynamic_cast(*pObj).GetCameraType()); } m_engine->SetFogStart(m_fogStart); @@ -1248,7 +1248,7 @@ void CAutoBase::FreezeCargo(bool freeze) m_cargoObjects.insert(obj); if ( obj->Implements(ObjectInterfaceType::Movable) ) { - CPhysics* physics = dynamic_cast(obj)->GetPhysics(); + CPhysics* physics = dynamic_cast(*obj).GetPhysics(); physics->SetFreeze(freeze); } } diff --git a/src/object/auto/autodestroyer.cpp b/src/object/auto/autodestroyer.cpp index f30acebf..717a1f2b 100644 --- a/src/object/auto/autodestroyer.cpp +++ b/src/object/auto/autodestroyer.cpp @@ -176,7 +176,7 @@ bool CAutoDestroyer::EventProcess(const Event &event) if ( scrap != nullptr ) { assert(scrap->Implements(ObjectInterfaceType::Destroyable)); - dynamic_cast(scrap)->DestroyObject(DestructionType::Explosion); + dynamic_cast(*scrap).DestroyObject(DestructionType::Explosion); } m_bExplo = true; } diff --git a/src/object/auto/autoegg.cpp b/src/object/auto/autoegg.cpp index a4e7d493..6c0026d8 100644 --- a/src/object/auto/autoegg.cpp +++ b/src/object/auto/autoegg.cpp @@ -74,7 +74,7 @@ void CAutoEgg::DeleteObject(bool all) alien->SetLock(false); if (alien->Implements(ObjectInterfaceType::Programmable)) { - dynamic_cast(alien)->SetActivity(true); // the insect is active + dynamic_cast(*alien).SetActivity(true); // the insect is active } } else @@ -123,7 +123,7 @@ void CAutoEgg::Init() if (alien->Implements(ObjectInterfaceType::Programmable)) { - dynamic_cast(alien)->SetActivity(false); + dynamic_cast(*alien).SetActivity(false); } } @@ -204,7 +204,7 @@ bool CAutoEgg::EventProcess(const Event &event) if ( alien == nullptr ) return true; if (alien->Implements(ObjectInterfaceType::Programmable)) { - dynamic_cast(alien)->SetActivity(false); + dynamic_cast(*alien).SetActivity(false); } m_progress += event.rTime*m_speed; @@ -265,7 +265,7 @@ Error CAutoEgg::IsEnded() alien->SetLock(false); if(alien->Implements(ObjectInterfaceType::Programmable)) { - dynamic_cast(alien)->SetActivity(true); // the insect is active + dynamic_cast(*alien).SetActivity(true); // the insect is active } } diff --git a/src/object/auto/autofactory.cpp b/src/object/auto/autofactory.cpp index 9ba1f3e1..a6061a32 100644 --- a/src/object/auto/autofactory.cpp +++ b/src/object/auto/autofactory.cpp @@ -397,7 +397,7 @@ bool CAutoFactory::EventProcess(const Event &event) if ( vehicle != nullptr ) { assert(vehicle->Implements(ObjectInterfaceType::Movable)); - physics = dynamic_cast(vehicle)->GetPhysics(); + physics = dynamic_cast(*vehicle).GetPhysics(); physics->SetFreeze(false); // can move vehicle->SetLock(false); // vehicle useable @@ -408,7 +408,7 @@ bool CAutoFactory::EventProcess(const Event &event) { if (vehicle->Implements(ObjectInterfaceType::Programmable) && vehicle->Implements(ObjectInterfaceType::ProgramStorage)) { - Program* program = dynamic_cast(vehicle)->AddProgram(); + Program* program = dynamic_cast(*vehicle).AddProgram(); if (boost::regex_match(m_program, boost::regex("[A-Za-z0-9_]+"))) // Public function name? { @@ -424,7 +424,7 @@ bool CAutoFactory::EventProcess(const Event &event) program->script->SendScript(m_program.c_str()); } - dynamic_cast(vehicle)->RunProgram(program); + dynamic_cast(*vehicle).RunProgram(program); } } } @@ -670,7 +670,7 @@ bool CAutoFactory::CreateVehicle() vehicle->SetLock(true); // not usable assert(vehicle->Implements(ObjectInterfaceType::Movable)); - CPhysics* physics = dynamic_cast(vehicle)->GetPhysics(); + CPhysics* physics = dynamic_cast(*vehicle).GetPhysics(); physics->SetFreeze(true); // it doesn't move if (vehicle->Implements(ObjectInterfaceType::ProgramStorage)) diff --git a/src/object/auto/autonuclearplant.cpp b/src/object/auto/autonuclearplant.cpp index 14fd6fc3..2c138c61 100644 --- a/src/object/auto/autonuclearplant.cpp +++ b/src/object/auto/autonuclearplant.cpp @@ -400,7 +400,7 @@ void CAutoNuclearPlant::CreatePower() float powerLevel = 1.0f; CObject* power = CObjectManager::GetInstancePointer()->CreateObject(pos, angle, OBJECT_ATOMIC, powerLevel); - dynamic_cast(power)->SetTransporter(m_object); + dynamic_cast(*power).SetTransporter(m_object); power->SetPosition(Math::Vector(22.0f, 3.0f, 0.0f)); m_object->SetPower(power); } diff --git a/src/object/auto/autopowercaptor.cpp b/src/object/auto/autopowercaptor.cpp index d10a6c88..614c5da4 100644 --- a/src/object/auto/autopowercaptor.cpp +++ b/src/object/auto/autopowercaptor.cpp @@ -269,7 +269,7 @@ void CAutoPowerCaptor::ChargeObject(float rTime) if (obj->Implements(ObjectInterfaceType::Powered)) { - CObject* power = dynamic_cast(obj)->GetPower(); + CObject* power = dynamic_cast(*obj).GetPower(); if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) { CPowerContainerObject* powerContainer = dynamic_cast(power); @@ -285,7 +285,7 @@ void CAutoPowerCaptor::ChargeObject(float rTime) if (obj->Implements(ObjectInterfaceType::Carrier)) { - CObject* power = dynamic_cast(obj)->GetCargo(); + CObject* power = dynamic_cast(*obj).GetCargo(); if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) { CPowerContainerObject* powerContainer = dynamic_cast(power); diff --git a/src/object/auto/autopowerplant.cpp b/src/object/auto/autopowerplant.cpp index 50447573..d0006e2d 100644 --- a/src/object/auto/autopowerplant.cpp +++ b/src/object/auto/autopowerplant.cpp @@ -331,7 +331,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event) cargo->SetScale(1.0f); cargo->SetLock(false); // usable battery - dynamic_cast(cargo)->SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporter(m_object); cargo->SetPosition(Math::Vector(0.0f, 3.0f, 0.0f)); m_object->SetPower(cargo); diff --git a/src/object/auto/autopowerstation.cpp b/src/object/auto/autopowerstation.cpp index 0693c7c7..96bed578 100644 --- a/src/object/auto/autopowerstation.cpp +++ b/src/object/auto/autopowerstation.cpp @@ -138,7 +138,7 @@ bool CAutoPowerStation::EventProcess(const Event &event) { if (vehicle->Implements(ObjectInterfaceType::Powered)) { - CObject* power = dynamic_cast(vehicle)->GetPower(); + CObject* power = dynamic_cast(*vehicle).GetPower(); if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) { CPowerContainerObject* powerContainer = dynamic_cast(power); @@ -158,7 +158,7 @@ bool CAutoPowerStation::EventProcess(const Event &event) if (vehicle->Implements(ObjectInterfaceType::Carrier)) { - CObject* power = dynamic_cast(vehicle)->GetCargo(); + CObject* power = dynamic_cast(*vehicle).GetCargo(); if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) { CPowerContainerObject* powerContainer = dynamic_cast(power); diff --git a/src/object/auto/autorepair.cpp b/src/object/auto/autorepair.cpp index f860f732..39b41d42 100644 --- a/src/object/auto/autorepair.cpp +++ b/src/object/auto/autorepair.cpp @@ -148,7 +148,7 @@ bool CAutoRepair::EventProcess(const Event &event) assert(vehicle->Implements(ObjectInterfaceType::Shielded)); if ( m_progress < 1.0f || - (vehicle != nullptr && dynamic_cast(vehicle)->GetShield() < 1.0f) ) + (vehicle != nullptr && dynamic_cast(*vehicle).GetShield() < 1.0f) ) { if ( vehicle != nullptr ) { @@ -243,9 +243,9 @@ CObject* CAutoRepair::SearchVehicle() { if (obj == m_object) continue; if ( !obj->Implements(ObjectInterfaceType::Shielded) ) continue; - if ( !dynamic_cast(obj)->IsRepairable() ) continue; + if ( !dynamic_cast(*obj).IsRepairable() ) continue; - if ( obj->Implements(ObjectInterfaceType::Movable) && !dynamic_cast(obj)->GetPhysics()->GetLand() ) continue; // in flight? + if ( obj->Implements(ObjectInterfaceType::Movable) && !dynamic_cast(*obj).GetPhysics()->GetLand() ) continue; // in flight? Math::Vector oPos = obj->GetPosition(); float dist = Math::Distance(oPos, sPos); diff --git a/src/object/auto/autotower.cpp b/src/object/auto/autotower.cpp index 65d83277..b509e7e9 100644 --- a/src/object/auto/autotower.cpp +++ b/src/object/auto/autotower.cpp @@ -289,7 +289,7 @@ CObject* CAutoTower::SearchTarget(Math::Vector &impact) { if ( obj->Implements(ObjectInterfaceType::Movable) ) { - CPhysics* physics = dynamic_cast(obj)->GetPhysics(); + CPhysics* physics = dynamic_cast(*obj).GetPhysics(); float speed = fabs(physics->GetLinMotionX(MO_REASPEED)); if ( speed > 20.0f ) continue; // moving too fast? } @@ -302,8 +302,7 @@ CObject* CAutoTower::SearchTarget(Math::Vector &impact) if ( distance > TOWER_SCOPE ) continue; // too far if ( distance < min ) { - min = distance; - best = obj; + min = distance; best = obj; } } if ( best == nullptr ) return nullptr; @@ -327,7 +326,7 @@ Error CAutoTower::GetError() return ERR_TOWER_POWER; // no battery } - if ( dynamic_cast(m_object->GetPower())->GetEnergy() < ENERGY_FIRE ) + if ( dynamic_cast(*m_object->GetPower()).GetEnergy() < ENERGY_FIRE ) { return ERR_TOWER_ENERGY; // not enough energy } diff --git a/src/object/drive_type.cpp b/src/object/drive_type.cpp index ce85d443..b30f4eb7 100644 --- a/src/object/drive_type.cpp +++ b/src/object/drive_type.cpp @@ -62,6 +62,7 @@ DriveType GetDriveFromObject(ObjectType type) case OBJECT_MOBILErs: return DriveType::Heavy; + case OBJECT_MOBILEst: case OBJECT_MOBILEsa: return DriveType::Amphibious; diff --git a/src/object/implementation/program_storage_impl.cpp b/src/object/implementation/program_storage_impl.cpp index ced1a4c7..d5a914ff 100644 --- a/src/object/implementation/program_storage_impl.cpp +++ b/src/object/implementation/program_storage_impl.cpp @@ -270,7 +270,7 @@ void CProgramStorageObjectImpl::LoadAllProgramsForLevel(CLevelParserLine* levelS if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run) { - dynamic_cast(m_object)->RunProgram(program); + dynamic_cast(*m_object).RunProgram(program); } } else @@ -327,7 +327,7 @@ void CProgramStorageObjectImpl::SaveAllProgramsForSavedScene(CLevelParserLine* l } if (m_programStorageIndex < 0) return; - if (!m_object->Implements(ObjectInterfaceType::Controllable) || !dynamic_cast(m_object)->GetSelectable() || m_object->GetType() == OBJECT_HUMAN) return; + if (!m_object->Implements(ObjectInterfaceType::Controllable) || !dynamic_cast(*m_object).GetSelectable() || m_object->GetType() == OBJECT_HUMAN) return; GetLogger()->Debug("Saving saved scene programs to '%s/prog%.3d___.txt'\n", levelSource.c_str(), m_programStorageIndex); for (unsigned int i = 0; i < m_program.size(); i++) @@ -379,7 +379,7 @@ void CProgramStorageObjectImpl::LoadAllProgramsForSavedScene(CLevelParserLine* l if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run) { - dynamic_cast(m_object)->RunProgram(program); + dynamic_cast(*m_object).RunProgram(program); } } } @@ -403,7 +403,7 @@ void CProgramStorageObjectImpl::LoadAllProgramsForSavedScene(CLevelParserLine* l if (m_object->Implements(ObjectInterfaceType::Programmable) && i == run) { - dynamic_cast(m_object)->RunProgram(program); + dynamic_cast(*m_object).RunProgram(program); } } } diff --git a/src/object/implementation/programmable_impl.cpp b/src/object/implementation/programmable_impl.cpp index f21bf4b7..8748f040 100644 --- a/src/object/implementation/programmable_impl.cpp +++ b/src/object/implementation/programmable_impl.cpp @@ -68,7 +68,7 @@ bool CProgrammableObjectImpl::EventProcess(const Event &event) { if (event.type == EVENT_FRAME) { - if ( m_object->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(m_object)->IsDying() && IsProgram() ) + if ( m_object->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(*m_object).IsDying() && IsProgram() ) { StopProgram(); } @@ -113,7 +113,7 @@ void CProgrammableObjectImpl::RunProgram(Program* program) { m_currentProgram = program; // start new program m_object->UpdateInterface(); - if (m_object->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(m_object)->GetTrainer()) + if (m_object->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(*m_object).GetTrainer()) CRobotMain::GetInstancePointer()->StartMissionTimer(); } } @@ -155,7 +155,7 @@ bool CProgrammableObjectImpl::ReadStack(std::istream &istr) { if (m_object->Implements(ObjectInterfaceType::ProgramStorage)) { - int count = static_cast(dynamic_cast(m_object)->GetProgramCount()); + int count = static_cast(dynamic_cast(*m_object).GetProgramCount()); if (!(op < count)) { GetLogger()->Info("Object program count: %i\n", count); @@ -163,7 +163,7 @@ bool CProgrammableObjectImpl::ReadStack(std::istream &istr) return false; } - m_currentProgram = dynamic_cast(m_object)->GetProgram(op); + m_currentProgram = dynamic_cast(*m_object).GetProgram(op); if (!m_currentProgram->script->ReadStack(istr)) { GetLogger()->Error("Restore state failed at program index: %i\n", op); @@ -203,7 +203,7 @@ bool CProgrammableObjectImpl::WriteStack(std::ostream &ostr) op = -1; if (m_object->Implements(ObjectInterfaceType::ProgramStorage)) { - op = dynamic_cast(m_object)->GetProgramIndex(m_currentProgram); + op = dynamic_cast(*m_object).GetProgramIndex(m_currentProgram); } if (!CBot::WriteShort(ostr, op)) return false; @@ -266,7 +266,7 @@ void CProgrammableObjectImpl::TraceRecordFrame() assert(m_object->Implements(ObjectInterfaceType::TraceDrawing)); CTraceDrawingObject* traceDrawing = dynamic_cast(m_object); - CPhysics* physics = dynamic_cast(m_object)->GetPhysics(); + CPhysics* physics = dynamic_cast(*m_object).GetPhysics(); speed = physics->GetLinMotionX(MO_REASPEED); if ( speed > 0.0f ) oper = TO_ADVANCE; @@ -353,7 +353,7 @@ void CProgrammableObjectImpl::TraceRecordStop() buffer << "}\n"; assert(m_object->Implements(ObjectInterfaceType::ProgramStorage)); - Program* prog = dynamic_cast(m_object)->AddProgram(); + Program* prog = dynamic_cast(*m_object).AddProgram(); prog->script->SendScript(buffer.str().c_str()); } diff --git a/src/object/interface/carrier_object.h b/src/object/interface/carrier_object.h index 857ff0bf..2cbf3d73 100644 --- a/src/object/interface/carrier_object.h +++ b/src/object/interface/carrier_object.h @@ -51,5 +51,5 @@ public: inline bool IsObjectCarryingCargo(CObject* obj) { return obj->Implements(ObjectInterfaceType::Carrier) && - dynamic_cast(obj)->IsCarryingCargo(); + dynamic_cast(*obj).IsCarryingCargo(); } diff --git a/src/object/interface/destroyable_object.h b/src/object/interface/destroyable_object.h index f40c3f7e..fd83a87e 100644 --- a/src/object/interface/destroyable_object.h +++ b/src/object/interface/destroyable_object.h @@ -33,6 +33,7 @@ enum class DestructionType Burn = 3, //!< burning Drowned = 4, //!< drowned (only for Me) Win = 5, //!< used when removing objects from a team that won + Squash = 6, //!< flatten }; /** diff --git a/src/object/interface/powered_object.h b/src/object/interface/powered_object.h index b1652e47..3aea3296 100644 --- a/src/object/interface/powered_object.h +++ b/src/object/interface/powered_object.h @@ -61,10 +61,10 @@ inline float GetObjectEnergy(CObject* object) if (object->Implements(ObjectInterfaceType::Powered)) { - CObject* power = dynamic_cast(object)->GetPower(); + CObject* power = dynamic_cast(*object).GetPower(); if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) { - energy = dynamic_cast(power)->GetEnergy(); + energy = dynamic_cast(*power).GetEnergy(); } } @@ -77,10 +77,10 @@ inline float GetObjectEnergyLevel(CObject* object) if (object->Implements(ObjectInterfaceType::Powered)) { - CObject* power = dynamic_cast(object)->GetPower(); + CObject* power = dynamic_cast(*object).GetPower(); if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) { - energy = dynamic_cast(power)->GetEnergyLevel(); + energy = dynamic_cast(*power).GetEnergyLevel(); } } @@ -90,5 +90,5 @@ inline float GetObjectEnergyLevel(CObject* object) inline bool ObjectHasPowerCell(CObject* object) { return object->Implements(ObjectInterfaceType::Powered) && - dynamic_cast(object)->GetPower() != nullptr; + dynamic_cast(*object).GetPower() != nullptr; } diff --git a/src/object/interface/trace_drawing_object.cpp b/src/object/interface/trace_drawing_object.cpp index c8fdab98..b0c8343d 100644 --- a/src/object/interface/trace_drawing_object.cpp +++ b/src/object/interface/trace_drawing_object.cpp @@ -27,21 +27,22 @@ std::string TraceColorName(TraceColor color) { switch(color) { + case TraceColor::Blue: return "Blue"; + case TraceColor::Red: return "Red"; + case TraceColor::Green: return "Green"; + case TraceColor::Yellow: return "Yellow"; + case TraceColor::Violet: return "Violet"; case TraceColor::White: return "White"; case TraceColor::Black: return "Black"; case TraceColor::Gray: return "Gray"; case TraceColor::LightGray: return "LightGray"; - case TraceColor::Red: return "Red"; case TraceColor::Pink: return "Pink"; case TraceColor::Purple: return "Purple"; case TraceColor::Orange: return "Orange"; - case TraceColor::Yellow: return "Yellow"; case TraceColor::Beige: return "Beige"; case TraceColor::Brown: return "Brown"; case TraceColor::Skin: return "Skin"; - case TraceColor::Green: return "Green"; case TraceColor::LightGreen: return "LightGreen"; - case TraceColor::Blue: return "Blue"; case TraceColor::LightBlue: return "LightBlue"; case TraceColor::RedArrow: return "RedArrow"; case TraceColor::BlackArrow: return "BlackArrow"; @@ -55,21 +56,22 @@ Gfx::Color TraceColorColor(TraceColor color) { switch(color) { + case TraceColor::Blue: return Gfx::Color(0.000f, 0.000f, 0.753f, 1.0f); + case TraceColor::Red: return Gfx::Color(1.000f, 0.000f, 0.000f, 1.0f); + case TraceColor::Green: return Gfx::Color(0.000f, 0.627f, 0.000f, 1.0f); + case TraceColor::Yellow: return Gfx::Color(1.000f, 1.000f, 0.000f, 1.0f); + case TraceColor::Violet: return Gfx::Color(0.820f, 0.000f, 0.997f, 1.0f); case TraceColor::White: return Gfx::Color(1.000f, 1.000f, 1.000f, 1.0f); case TraceColor::Black: return Gfx::Color(0.000f, 0.000f, 0.000f, 1.0f); case TraceColor::Gray: return Gfx::Color(0.549f, 0.549f, 0.549f, 1.0f); case TraceColor::LightGray: return Gfx::Color(0.753f, 0.753f, 0.753f, 1.0f); - case TraceColor::Red: return Gfx::Color(1.000f, 0.000f, 0.000f, 1.0f); case TraceColor::Pink: return Gfx::Color(1.000f, 0.627f, 0.753f, 1.0f); case TraceColor::Purple: return Gfx::Color(0.878f, 0.000f, 0.753f, 1.0f); case TraceColor::Orange: return Gfx::Color(1.000f, 0.627f, 0.000f, 1.0f); - case TraceColor::Yellow: return Gfx::Color(1.000f, 1.000f, 0.000f, 1.0f); case TraceColor::Beige: return Gfx::Color(0.878f, 0.753f, 0.000f, 1.0f); case TraceColor::Brown: return Gfx::Color(0.627f, 0.361f, 0.000f, 1.0f); case TraceColor::Skin: return Gfx::Color(0.961f, 0.839f, 0.714f, 1.0f); - case TraceColor::Green: return Gfx::Color(0.000f, 0.627f, 0.000f, 1.0f); case TraceColor::LightGreen: return Gfx::Color(0.000f, 1.000f, 0.000f, 1.0f); - case TraceColor::Blue: return Gfx::Color(0.000f, 0.000f, 0.753f, 1.0f); case TraceColor::LightBlue: return Gfx::Color(0.000f, 0.871f, 1.000f, 1.0f); case TraceColor::BlackArrow: return TraceColorColor(TraceColor::Black); case TraceColor::RedArrow: return TraceColorColor(TraceColor::Red); //TODO: We could probably have all the colors available as arrows now diff --git a/src/object/interface/trace_drawing_object.h b/src/object/interface/trace_drawing_object.h index 6dd0dda0..36e05463 100644 --- a/src/object/interface/trace_drawing_object.h +++ b/src/object/interface/trace_drawing_object.h @@ -32,24 +32,25 @@ enum class TraceColor { Default = -1, - White = 0, - Black = 1, - Gray = 2, - LightGray = 3, - Red = 4, - Pink = 5, - Purple = 6, - Orange = 7, - Yellow = 8, - Beige = 9, - Brown = 10, - Skin = 11, - Green = 12, - LightGreen = 13, - Blue = 14, - LightBlue = 15, - BlackArrow = 16, - RedArrow = 17, + Blue = 0, + Red = 1, + Green = 2, + Yellow = 3, + Violet = 4, + White = 5, + Black = 6, + Gray = 7, + LightGray = 8, + Pink = 9, + Purple = 10, + Orange = 11, + Beige = 12, + Brown = 13, + Skin = 14, + LightGreen = 15, + LightBlue = 16, + BlackArrow = 17, + RedArrow = 18, Max, }; //! Convert TraceColor to a std::string diff --git a/src/object/interface/transportable_object.h b/src/object/interface/transportable_object.h index a45d9e0c..38e88dc5 100644 --- a/src/object/interface/transportable_object.h +++ b/src/object/interface/transportable_object.h @@ -54,5 +54,5 @@ public: inline bool IsObjectBeingTransported(CObject* obj) { return obj->Implements(ObjectInterfaceType::Transportable) && - dynamic_cast(obj)->IsBeingTransported(); + dynamic_cast(*obj).IsBeingTransported(); } diff --git a/src/object/motion/motionant.cpp b/src/object/motion/motionant.cpp index 889e6d96..2b4cf2c7 100644 --- a/src/object/motion/motionant.cpp +++ b/src/object/motion/motionant.cpp @@ -429,7 +429,7 @@ bool CMotionAnt::EventFrame(const Event &event) assert(m_object->Implements(ObjectInterfaceType::Destroyable)); if ( dynamic_cast(m_object)->GetDying() == DeathType::Burning ) // burning? { - if ( dynamic_cast(m_object)->GetFixed() ) + if ( dynamic_cast(*m_object).GetFixed() ) { m_actionType = MAS_BURN; } @@ -724,7 +724,7 @@ bool CMotionAnt::EventFrame(const Event &event) if ( m_progress >= 1.0f ) { SetAction(-1); - dynamic_cast(m_object)->SetFixed(false); // moving again + dynamic_cast(*m_object).SetFixed(false); // moving again } } else diff --git a/src/object/motion/motionhuman.cpp b/src/object/motion/motionhuman.cpp index d6f41144..aef056c6 100644 --- a/src/object/motion/motionhuman.cpp +++ b/src/object/motion/motionhuman.cpp @@ -314,7 +314,7 @@ void CMotionHuman::Create(Math::Vector pos, float angle, ObjectType type, m_object->SetPartRotation(13, Math::Vector(10.0f*Math::PI/180.0f, -5.0f*Math::PI/180.0f, 5.0f*Math::PI/180.0f)); // Creates the neutron gun. - if ( option != 2 ) // with backpack? + if ( option != 2 && !m_main->GetPlusExplorer()) // with backpack? { rank = m_engine->CreateObject(); m_engine->SetObjectType(rank, Gfx::ENG_OBJTYPE_DESCENDANT); diff --git a/src/object/motion/motionspider.cpp b/src/object/motion/motionspider.cpp index ec8af342..c65807f1 100644 --- a/src/object/motion/motionspider.cpp +++ b/src/object/motion/motionspider.cpp @@ -364,7 +364,7 @@ bool CMotionSpider::EventFrame(const Event &event) assert(m_object->Implements(ObjectInterfaceType::Destroyable)); if (dynamic_cast(m_object)->GetDying() == DeathType::Burning ) // burning? { - if ( dynamic_cast(m_object)->GetFixed() ) + if ( dynamic_cast(*m_object).GetFixed() ) { m_actionType = MSS_BURN; } @@ -648,7 +648,7 @@ bool CMotionSpider::EventFrame(const Event &event) if ( m_progress >= 1.0f ) { SetAction(-1); - dynamic_cast(m_object)->SetFixed(false); // moving again + dynamic_cast(*m_object).SetFixed(false); // moving again } } else diff --git a/src/object/motion/motionvehicle.cpp b/src/object/motion/motionvehicle.cpp index 4f2a09f1..b9719c34 100644 --- a/src/object/motion/motionvehicle.cpp +++ b/src/object/motion/motionvehicle.cpp @@ -1075,7 +1075,7 @@ void CMotionVehicle::Create(Math::Vector pos, float angle, ObjectType type, powerCell->SetPosition(powerCellPos); powerCell->SetRotation(Math::Vector(0.0f, powerCellAngle, 0.0f)); - dynamic_cast(powerCell)->SetTransporter(m_object); + dynamic_cast(*powerCell).SetTransporter(m_object); assert(m_object->Implements(ObjectInterfaceType::Powered)); m_object->SetPower(powerCell); } diff --git a/src/object/object_manager.cpp b/src/object/object_manager.cpp index fd335014..58c7475e 100644 --- a/src/object/object_manager.cpp +++ b/src/object/object_manager.cpp @@ -142,6 +142,8 @@ CObject* CObjectManager::CreateObject(ObjectCreateParams params) } } + params.power = ClampPower(params.type,params.power); + assert(m_objects.find(params.id) == m_objects.end()); auto objectUPtr = m_objectFactory->CreateObject(params); @@ -163,10 +165,20 @@ CObject* CObjectManager::CreateObject(Math::Vector pos, float angle, ObjectType params.angle = angle; params.type = type; params.power = power; - return CreateObject(params); } +float CObjectManager::ClampPower(ObjectType type, float power) +{ + float min = 0; + float max = 100; + if (type == OBJECT_POWER || type == OBJECT_ATOMIC) + { + max = 1; + } + return Math::Clamp(power, min, max); +} + std::vector CObjectManager::GetObjectsOfTeam(int team) { std::vector result; @@ -205,7 +217,7 @@ void CObjectManager::DestroyTeam(int team, DestructionType destructionType) { if (object->Implements(ObjectInterfaceType::Destroyable)) { - dynamic_cast(object)->DestroyObject(destructionType); + dynamic_cast(*object).DestroyObject(destructionType); } else { @@ -303,7 +315,7 @@ std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector this oType == OBJECT_RUINmobiler1 || oType == OBJECT_RUINmobiler2 ) { - oType = OBJECT_RUINmobilew1; // any ruin + oType = OBJECT_RUINmobilew1; // any wreck } if ( oType == OBJECT_BARRIER2 || @@ -313,6 +325,33 @@ std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector this { oType = OBJECT_BARRIER1; // any barrier } + + if ( oType == OBJECT_RUINdoor || + oType == OBJECT_RUINsupport || + oType == OBJECT_RUINradar || + oType == OBJECT_RUINconvert ) // ruins? + { + oType = OBJECT_RUINfactory; // any ruin + } + + if ( oType == OBJECT_PLANT1 || + oType == OBJECT_PLANT2 || + oType == OBJECT_PLANT3 || + oType == OBJECT_PLANT4 || + oType == OBJECT_PLANT15 || + oType == OBJECT_PLANT16 || + oType == OBJECT_PLANT17 || + oType == OBJECT_PLANT18 ) // bushes? + { + oType = OBJECT_PLANT0; // any bush + } + + if ( oType == OBJECT_QUARTZ1 || + oType == OBJECT_QUARTZ2 || + oType == OBJECT_QUARTZ3 ) // crystals? + { + oType = OBJECT_QUARTZ0; // any crystal + } // END OF TODO } @@ -324,7 +363,7 @@ std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector this { if ( pObj->Implements(ObjectInterfaceType::Movable) ) { - CPhysics* physics = dynamic_cast(pObj)->GetPhysics(); + CPhysics* physics = dynamic_cast(*pObj).GetPhysics(); if ( physics != nullptr ) { if ( !physics->GetLand() ) continue; @@ -334,7 +373,7 @@ std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector this if ( filter_flying == FILTER_ONLYFLYING ) { if ( !pObj->Implements(ObjectInterfaceType::Movable) ) continue; - CPhysics* physics = dynamic_cast(pObj)->GetPhysics(); + CPhysics* physics = dynamic_cast(*pObj).GetPhysics(); if ( physics == nullptr ) continue; if ( physics->GetLand() ) continue; } diff --git a/src/object/object_manager.h b/src/object/object_manager.h index b5296156..3ae55c84 100644 --- a/src/object/object_manager.h +++ b/src/object/object_manager.h @@ -303,6 +303,8 @@ public: //@} private: + //! Prevents creation of overcharged power cells + float ClampPower(ObjectType type, float power); void CleanRemovedObjectsIfNeeded(); private: diff --git a/src/object/object_type.cpp b/src/object/object_type.cpp new file mode 100644 index 00000000..ca452bc0 --- /dev/null +++ b/src/object/object_type.cpp @@ -0,0 +1,236 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, 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 "object/object_type.h" + +#include + +bool IsValidObjectTypeId(int id) +{ + static const std::unordered_set validIds{ + OBJECT_NULL, + OBJECT_PORTICO, + OBJECT_BASE, + OBJECT_DERRICK, + OBJECT_FACTORY, + OBJECT_STATION, + OBJECT_CONVERT, + OBJECT_REPAIR, + OBJECT_TOWER, + OBJECT_NEST, + OBJECT_RESEARCH, + OBJECT_RADAR, + OBJECT_ENERGY, + OBJECT_LABO, + OBJECT_NUCLEAR, + OBJECT_START, + OBJECT_END, + OBJECT_INFO, + OBJECT_PARA, + OBJECT_TARGET1, + OBJECT_TARGET2, + OBJECT_SAFE, + OBJECT_HUSTON, + OBJECT_DESTROYER, + OBJECT_STONE, + OBJECT_URANIUM, + OBJECT_METAL, + OBJECT_POWER, + OBJECT_ATOMIC, + OBJECT_BULLET, + OBJECT_BBOX, + OBJECT_TNT, + OBJECT_MARKPOWER, + OBJECT_MARKSTONE, + OBJECT_MARKURANIUM, + OBJECT_MARKKEYa, + OBJECT_MARKKEYb, + OBJECT_MARKKEYc, + OBJECT_MARKKEYd, + OBJECT_BOMB, + OBJECT_WINFIRE, + OBJECT_SHOW, + OBJECT_BAG, + OBJECT_PLANT0, + OBJECT_PLANT1, + OBJECT_PLANT2, + OBJECT_PLANT3, + OBJECT_PLANT4, + OBJECT_PLANT5, + OBJECT_PLANT6, + OBJECT_PLANT7, + OBJECT_PLANT8, + OBJECT_PLANT9, + OBJECT_PLANT10, + OBJECT_PLANT11, + OBJECT_PLANT12, + OBJECT_PLANT13, + OBJECT_PLANT14, + OBJECT_PLANT15, + OBJECT_PLANT16, + OBJECT_PLANT17, + OBJECT_PLANT18, + OBJECT_PLANT19, + OBJECT_TREE0, + OBJECT_TREE1, + OBJECT_TREE2, + OBJECT_TREE3, + OBJECT_TREE4, + OBJECT_TREE5, + OBJECT_MOBILEwt, + OBJECT_MOBILEtt, + OBJECT_MOBILEft, + OBJECT_MOBILEit, + OBJECT_MOBILErp, + OBJECT_MOBILEst, + OBJECT_MOBILEwa, + OBJECT_MOBILEta, + OBJECT_MOBILEfa, + OBJECT_MOBILEia, + OBJECT_MOBILEwc, + OBJECT_MOBILEtc, + OBJECT_MOBILEfc, + OBJECT_MOBILEic, + OBJECT_MOBILEwi, + OBJECT_MOBILEti, + OBJECT_MOBILEfi, + OBJECT_MOBILEii, + OBJECT_MOBILEws, + OBJECT_MOBILEts, + OBJECT_MOBILEfs, + OBJECT_MOBILEis, + OBJECT_MOBILErt, + OBJECT_MOBILErc, + OBJECT_MOBILErr, + OBJECT_MOBILErs, + OBJECT_MOBILEsa, + OBJECT_MOBILEtg, + OBJECT_MOBILEdr, + OBJECT_CONTROLLER, + OBJECT_MOBILEwb, + OBJECT_MOBILEtb, + OBJECT_MOBILEfb, + OBJECT_MOBILEib, + OBJECT_MOBILEpr, + OBJECT_WAYPOINT, + OBJECT_FLAGb, + OBJECT_FLAGr, + OBJECT_FLAGg, + OBJECT_FLAGy, + OBJECT_FLAGv, + OBJECT_KEYa, + OBJECT_KEYb, + OBJECT_KEYc, + OBJECT_KEYd, + OBJECT_HUMAN, + OBJECT_TOTO, + OBJECT_TECH, + OBJECT_BARRIER0, + OBJECT_BARRIER1, + OBJECT_BARRIER2, + OBJECT_BARRIER3, + OBJECT_BARRICADE0, + OBJECT_BARRICADE1, + OBJECT_MOTHER, + OBJECT_EGG, + OBJECT_ANT, + OBJECT_SPIDER, + OBJECT_BEE, + OBJECT_WORM, + OBJECT_RUINmobilew1, + OBJECT_RUINmobilew2, + OBJECT_RUINmobilet1, + OBJECT_RUINmobilet2, + OBJECT_RUINmobiler1, + OBJECT_RUINmobiler2, + OBJECT_RUINfactory, + OBJECT_RUINdoor, + OBJECT_RUINsupport, + OBJECT_RUINradar, + OBJECT_RUINconvert, + OBJECT_RUINbase, + OBJECT_RUINhead, + OBJECT_TEEN0, + OBJECT_TEEN1, + OBJECT_TEEN2, + OBJECT_TEEN3, + OBJECT_TEEN4, + OBJECT_TEEN5, + OBJECT_TEEN6, + OBJECT_TEEN7, + OBJECT_TEEN8, + OBJECT_TEEN9, + OBJECT_TEEN10, + OBJECT_TEEN11, + OBJECT_TEEN12, + OBJECT_TEEN13, + OBJECT_TEEN14, + OBJECT_TEEN15, + OBJECT_TEEN16, + OBJECT_TEEN17, + OBJECT_TEEN18, + OBJECT_TEEN19, + OBJECT_TEEN20, + OBJECT_TEEN21, + OBJECT_TEEN22, + OBJECT_TEEN23, + OBJECT_TEEN24, + OBJECT_TEEN25, + OBJECT_TEEN26, + OBJECT_TEEN27, + OBJECT_TEEN28, + OBJECT_TEEN29, + OBJECT_TEEN30, + OBJECT_TEEN31, + OBJECT_TEEN32, + OBJECT_TEEN33, + OBJECT_TEEN34, + OBJECT_TEEN35, + OBJECT_TEEN36, + OBJECT_TEEN37, + OBJECT_TEEN38, + OBJECT_TEEN39, + OBJECT_TEEN40, + OBJECT_TEEN41, + OBJECT_TEEN42, + OBJECT_TEEN43, + OBJECT_TEEN44, + OBJECT_QUARTZ0, + OBJECT_QUARTZ1, + OBJECT_QUARTZ2, + OBJECT_QUARTZ3, + OBJECT_ROOT0, + OBJECT_ROOT1, + OBJECT_ROOT2, + OBJECT_ROOT3, + OBJECT_ROOT4, + OBJECT_ROOT5, + OBJECT_MUSHROOM1, + OBJECT_MUSHROOM2, + OBJECT_APOLLO1, + OBJECT_APOLLO2, + OBJECT_APOLLO3, + OBJECT_APOLLO4, + OBJECT_APOLLO5, + OBJECT_HOME1, + + OBJECT_MAX + }; + return validIds.count(id); +} diff --git a/src/object/object_type.h b/src/object/object_type.h index d4a8f062..7f78e304 100644 --- a/src/object/object_type.h +++ b/src/object/object_type.h @@ -248,3 +248,5 @@ struct ObjectTypeHash return std::hash()(t); } }; + +bool IsValidObjectTypeId(int id); diff --git a/src/object/old_object.cpp b/src/object/old_object.cpp index 0304fec5..7dedda8a 100644 --- a/src/object/old_object.cpp +++ b/src/object/old_object.cpp @@ -285,8 +285,8 @@ void COldObject::DeleteObject(bool bAll) { if (m_power->Implements(ObjectInterfaceType::Old)) { - dynamic_cast(m_power)->SetTransporter(nullptr); - dynamic_cast(m_power)->DeleteObject(bAll); + dynamic_cast(*m_power).SetTransporter(nullptr); + dynamic_cast(*m_power).DeleteObject(bAll); } m_power = nullptr; } @@ -294,8 +294,8 @@ void COldObject::DeleteObject(bool bAll) { if (m_cargo->Implements(ObjectInterfaceType::Old)) { - dynamic_cast(m_cargo)->SetTransporter(nullptr); - dynamic_cast(m_cargo)->DeleteObject(bAll); + dynamic_cast(*m_cargo).SetTransporter(nullptr); + dynamic_cast(*m_cargo).DeleteObject(bAll); } m_cargo = nullptr; } @@ -351,6 +351,7 @@ bool COldObject::DamageObject(DamageType type, float force, CObject* killer) assert(!Implements(ObjectInterfaceType::Destroyable) || Implements(ObjectInterfaceType::Shielded) || Implements(ObjectInterfaceType::Fragile)); if ( IsDying() ) return false; + if ( Implements(ObjectInterfaceType::Jostleable) ) return false; if ( m_type == OBJECT_ANT || m_type == OBJECT_WORM || @@ -569,6 +570,11 @@ void COldObject::DestroyObject(DestructionType type, CObject* killer) { pyroType = Gfx::PT_WPCHECK; } + else if ( type == DestructionType::Squash ) + { + pyroType = Gfx::PT_SQUASH; + DeleteAllCrashSpheres(); + } assert(pyroType != Gfx::PT_NULL); if (pyroType == Gfx::PT_FRAGT || pyroType == Gfx::PT_FRAGO || @@ -879,6 +885,21 @@ void COldObject::SetType(ObjectType type) m_implementedInterfaces[static_cast(ObjectInterfaceType::Fragile)] = true; m_implementedInterfaces[static_cast(ObjectInterfaceType::Shielded)] = false; } + else if (m_type == OBJECT_PLANT0 || + m_type == OBJECT_PLANT1 || + m_type == OBJECT_PLANT2 || + m_type == OBJECT_PLANT3 || + m_type == OBJECT_PLANT4 || + m_type == OBJECT_PLANT15 || + m_type == OBJECT_PLANT16 || + m_type == OBJECT_PLANT17 || + m_type == OBJECT_PLANT18 ) + { + m_implementedInterfaces[static_cast(ObjectInterfaceType::Damageable)] = true; + m_implementedInterfaces[static_cast(ObjectInterfaceType::Destroyable)] = true; + m_implementedInterfaces[static_cast(ObjectInterfaceType::Fragile)] = true; + m_implementedInterfaces[static_cast(ObjectInterfaceType::Shielded)] = false; + } else { m_implementedInterfaces[static_cast(ObjectInterfaceType::Damageable)] = false; @@ -1274,7 +1295,7 @@ int COldObject::SearchDescendant(int parent, int n) void COldObject::TransformCrashSphere(Math::Sphere& crashSphere) { - crashSphere.radius *= GetScaleX(); + if(!Implements(ObjectInterfaceType::Jostleable)) crashSphere.radius *= GetScaleX(); // Returns to the sphere collisions, // which ignores the tilt of the vehicle. @@ -1606,6 +1627,11 @@ bool COldObject::GetTrainer() return m_bTrainer; } +bool COldObject::GetPlusTrainer() +{ + return m_main->GetPlusTrainer(); +} + void COldObject::SetToy(bool bEnable) { m_bToy = bEnable; @@ -2479,7 +2505,7 @@ float COldObject::GetAbsTime() float COldObject::GetCapacity() { - return m_type == OBJECT_ATOMIC ? 10.0f : 1.0f; + return m_type == OBJECT_ATOMIC ? m_main->GetGlobalNuclearCapacity() : m_main->GetGlobalCellCapacity() ; } bool COldObject::IsRechargeable() diff --git a/src/object/old_object.h b/src/object/old_object.h index 9a3799be..cbb25684 100644 --- a/src/object/old_object.h +++ b/src/object/old_object.h @@ -165,6 +165,7 @@ public: void SetTrainer(bool bEnable) override; bool GetTrainer() override; + bool GetPlusTrainer(); void SetToy(bool bEnable); bool GetToy(); diff --git a/src/object/subclass/base_building.cpp b/src/object/subclass/base_building.cpp index 70ce6c73..e60c2381 100644 --- a/src/object/subclass/base_building.cpp +++ b/src/object/subclass/base_building.cpp @@ -64,7 +64,7 @@ std::unique_ptr CBaseBuilding::Create( { auto obj = MakeUnique(params.id, params.type); - obj->SetTrainer(params.trainer); + obj->SetTrainer(params.trainer || obj->GetPlusTrainer()); obj->SetTeam(params.team); float height = params.height; diff --git a/src/object/subclass/base_robot.cpp b/src/object/subclass/base_robot.cpp index 253c6fa8..3f5a21f6 100644 --- a/src/object/subclass/base_robot.cpp +++ b/src/object/subclass/base_robot.cpp @@ -65,7 +65,7 @@ std::unique_ptr CBaseRobot::Create( } else { - obj->SetTrainer(params.trainer); + obj->SetTrainer(params.trainer || obj->GetPlusTrainer()); } obj->SetToy(params.toy); diff --git a/src/object/subclass/shielder.cpp b/src/object/subclass/shielder.cpp index 80438b4d..e52b8779 100644 --- a/src/object/subclass/shielder.cpp +++ b/src/object/subclass/shielder.cpp @@ -52,7 +52,7 @@ std::unique_ptr CShielder::Create( auto obj = MakeUnique(params.id); obj->SetTeam(params.team); - obj->SetTrainer(params.trainer); + obj->SetTrainer(params.trainer || obj->GetPlusTrainer()); obj->SetToy(params.toy); auto physics = MakeUnique(obj.get()); diff --git a/src/object/task/taskbuild.cpp b/src/object/task/taskbuild.cpp index 763b8655..b1ee0037 100644 --- a/src/object/task/taskbuild.cpp +++ b/src/object/task/taskbuild.cpp @@ -111,6 +111,7 @@ void CTaskBuild::CreateBuilding(Math::Vector pos, float angle, bool trainer) if ( m_type == OBJECT_NUCLEAR ) m_buildingHeight = 40.0f; if ( m_type == OBJECT_PARA ) m_buildingHeight = 68.0f; if ( m_type == OBJECT_INFO ) m_buildingHeight = 19.0f; + if ( m_type == OBJECT_SAFE ) m_buildingHeight = 16.0f; if ( m_type == OBJECT_DESTROYER) m_buildingHeight = 35.0f; if ( m_type == OBJECT_HUSTON ) m_buildingHeight = 45.0f; m_buildingHeight *= 0.25f; @@ -682,6 +683,7 @@ Error CTaskBuild::FlatFloor() if ( m_type == OBJECT_NUCLEAR ) radius = 20.0f; if ( m_type == OBJECT_PARA ) radius = 20.0f; if ( m_type == OBJECT_INFO ) radius = 5.0f; + if ( m_type == OBJECT_SAFE ) radius = 20.0f; if ( m_type == OBJECT_DESTROYER) radius = 20.0f; //if ( radius == 0.0f ) return ERR_UNKNOWN; diff --git a/src/object/task/taskdeletemark.cpp b/src/object/task/taskdeletemark.cpp index b45bd7a0..4b9ced5e 100644 --- a/src/object/task/taskdeletemark.cpp +++ b/src/object/task/taskdeletemark.cpp @@ -23,6 +23,7 @@ #include "common/global.h" #include "graphics/engine/particle.h" +#include "graphics/engine/pyro_manager.h" #include "graphics/engine/terrain.h" #include "level/robotmain.h" @@ -88,6 +89,6 @@ void CTaskDeleteMark::DeleteMark() if (obj != nullptr) { - CObjectManager::GetInstancePointer()->DeleteObject(obj); + m_engine->GetPyroManager()->Create(Gfx::PT_WPCHECK, obj); } } diff --git a/src/object/task/taskfire.cpp b/src/object/task/taskfire.cpp index 72247447..87730880 100644 --- a/src/object/task/taskfire.cpp +++ b/src/object/task/taskfire.cpp @@ -317,7 +317,7 @@ Error CTaskFire::Start(float delay) CObject* power = dynamic_cast(m_object)->GetPower(); if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_FIRE_ENERGY; - energy = dynamic_cast(power)->GetEnergy(); + energy = dynamic_cast(*power).GetEnergy(); if ( m_bOrganic ) fire = m_delay*ENERGY_FIREi; else if ( m_bRay ) fire = m_delay*ENERGY_FIREr; else fire = m_delay*ENERGY_FIRE; diff --git a/src/object/task/taskfireant.cpp b/src/object/task/taskfireant.cpp index a1ee384f..99bc7d0b 100644 --- a/src/object/task/taskfireant.cpp +++ b/src/object/task/taskfireant.cpp @@ -60,7 +60,7 @@ bool CTaskFireAnt::EventProcess(const Event &event) if ( event.type != EVENT_FRAME ) return true; if ( m_bError ) return false; - if ( dynamic_cast(m_object)->GetFixed() ) // insect on its back? + if ( dynamic_cast(*m_object).GetFixed() ) // insect on its back? { m_bError = true; return false; @@ -100,7 +100,7 @@ Error CTaskFireAnt::Start(Math::Vector impact) if ( type != OBJECT_ANT ) return ERR_WRONG_BOT; // Insect on its back? - if ( dynamic_cast(m_object)->GetFixed() ) return ERR_WRONG_BOT; + if ( dynamic_cast(*m_object).GetFixed() ) return ERR_WRONG_BOT; m_physics->SetMotorSpeed(Math::Vector(0.0f, 0.0f, 0.0f)); @@ -130,7 +130,7 @@ Error CTaskFireAnt::IsEnded() if ( m_engine->GetPause() ) return ERR_CONTINUE; if ( m_bError ) return ERR_STOP; - if ( dynamic_cast(m_object)->GetFixed() ) return ERR_STOP; // insect on its back? + if ( dynamic_cast(*m_object).GetFixed() ) return ERR_STOP; // insect on its back? if ( m_phase == TFA_TURN ) // rotation ? { diff --git a/src/object/task/taskflag.cpp b/src/object/task/taskflag.cpp index c9790ebf..b2872159 100644 --- a/src/object/task/taskflag.cpp +++ b/src/object/task/taskflag.cpp @@ -61,6 +61,24 @@ bool CTaskFlag::EventProcess(const Event &event) m_time += event.rTime; + ObjectType type = m_object->GetType(); + if ( type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis ) + { + float angle = 110.0f*Math::PI/180.0f; + float diff = -10.0f*Math::PI/180.0f; + if ( m_time <= 0.5f ) + { + m_object->SetPartRotationZ(1, angle+diff*m_time*2.0f); + } + else if ( m_time >= 1.5f && m_time < 2.0f ) + { + m_object->SetPartRotationZ(1, angle+diff*(2.0f-m_time)*2.0f); + } + } + return true; } @@ -104,7 +122,32 @@ Error CTaskFlag::Start(TaskFlagOrder order, int rank) m_bError = false; - m_motion->SetAction(MHS_FLAG); // sets/removes flag + switch ( m_object->GetType() ) // sets/removes flag + { + case OBJECT_HUMAN: + case OBJECT_TECH: + m_motion->SetAction(MHS_FLAG); + break; + + case OBJECT_MOBILEws: + case OBJECT_MOBILEts: + case OBJECT_MOBILEfs: + case OBJECT_MOBILEis: + { + int i = m_sound->Play(SOUND_MANIP, m_object->GetPosition(), 0.0f, 0.3f, true); + m_sound->AddEnvelope(i, 0.5f, 1.0f, 0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.5f, 1.0f, 0.3f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.3f, 0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.3f, 1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.5f, 1.0f, 0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.5f, 1.0f, 0.3f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.3f, 0.1f, SOPER_STOP); + break; + } + + default: + break; + } m_camera->StartCentering(m_object, Math::PI*0.3f, 99.9f, 0.0f, 0.5f); return ERR_OK; @@ -127,7 +170,23 @@ Error CTaskFlag::IsEnded() bool CTaskFlag::Abort() { - m_motion->SetAction(-1); + switch ( m_object->GetType() ) + { + case OBJECT_HUMAN: + case OBJECT_TECH: + m_motion->SetAction(-1); + break; + + case OBJECT_MOBILEws: + case OBJECT_MOBILEts: + case OBJECT_MOBILEfs: + case OBJECT_MOBILEis: + m_object->SetPartRotationZ(1, 110.0f*Math::PI/180.0f); + break; + + default: + break; + } m_camera->StopCentering(m_object, 2.0f); return true; } @@ -190,7 +249,18 @@ Error CTaskFlag::CreateFlag(int rank) }; Math::Matrix* mat = m_object->GetWorldMatrix(0); - Math::Vector pos = Transform(*mat, Math::Vector(4.0f, 0.0f, 0.0f)); + Math::Vector pos; + switch ( m_object->GetType() ) + { + case OBJECT_HUMAN: + case OBJECT_TECH: + pos = Transform(*mat, Math::Vector(4.0f, 0.0f, 0.0f)); + break; + + default: + pos = Transform(*mat, Math::Vector(6.0f, 0.0f, 0.0f)); + break; + } CObject* pObj = SearchNearest(pos, OBJECT_NULL); if ( pObj != nullptr ) diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index 1224b391..d2e64667 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -1202,7 +1202,7 @@ bool CTaskGoto::AdjustTarget(CObject* pObj, Math::Vector &pos, float &distance) type == OBJECT_MOBILEdr ) { assert(pObj->Implements(ObjectInterfaceType::Powered)); - pos = dynamic_cast(pObj)->GetPowerPosition(); + pos = dynamic_cast(*pObj).GetPowerPosition(); pos.x -= TAKE_DIST+TAKE_DIST_OTHER+distance; mat = pObj->GetWorldMatrix(0); pos = Transform(*mat, pos); diff --git a/src/object/task/taskmanip.cpp b/src/object/task/taskmanip.cpp index ff55962f..aa62ef9e 100644 --- a/src/object/task/taskmanip.cpp +++ b/src/object/task/taskmanip.cpp @@ -304,8 +304,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) assert(other->Implements(ObjectInterfaceType::Transportable)); m_object->SetCargo(other); // takes the ball - dynamic_cast(other)->SetTransporter(m_object); - dynamic_cast(other)->SetTransporterPart(0); // taken with the base + dynamic_cast(*other).SetTransporter(m_object); + dynamic_cast(*other).SetTransporterPart(0); // taken with the base other->SetPosition(Math::Vector(0.0f, -3.0f, 0.0f)); } else @@ -314,7 +314,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) assert(other->Implements(ObjectInterfaceType::Transportable)); m_object->SetCargo(nullptr); // lick the ball - dynamic_cast(other)->SetTransporter(nullptr); + dynamic_cast(*other).SetTransporter(nullptr); pos = m_object->GetPosition(); pos.y -= 3.0f; other->SetPosition(pos); @@ -903,7 +903,7 @@ CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos, ObjectType type = pObj->GetType(); if ( !pObj->Implements(ObjectInterfaceType::Powered) ) continue; - CObject* power = dynamic_cast(pObj)->GetPower(); + CObject* power = dynamic_cast(*pObj).GetPower(); if (power != nullptr) { if (power->GetLock()) continue; @@ -911,7 +911,7 @@ CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos, } mat = pObj->GetWorldMatrix(0); - Math::Vector oPos = Transform(*mat, dynamic_cast(pObj)->GetPowerPosition()); + Math::Vector oPos = Transform(*mat, dynamic_cast(*pObj).GetPowerPosition()); oAngle = pObj->GetRotationY(); if ( type == OBJECT_TOWER || @@ -946,7 +946,7 @@ CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos, angle = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) { - Math::Vector powerPos = dynamic_cast(pObj)->GetPowerPosition(); + Math::Vector powerPos = dynamic_cast(*pObj).GetPowerPosition(); height = powerPos.y; pos = oPos; return pObj; @@ -974,8 +974,8 @@ bool CTaskManip::TransporterTakeObject() if ( m_object->GetType() == OBJECT_HUMAN || m_object->GetType() == OBJECT_TECH ) { - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(4); // takes with the hand + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(4); // takes with the hand cargo->SetPosition(Math::Vector(1.7f, -0.5f, 1.1f)); cargo->SetRotationY(0.1f); @@ -984,8 +984,8 @@ bool CTaskManip::TransporterTakeObject() } else if ( m_bSubm ) { - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(2); // takes with the right claw + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(2); // takes with the right claw Math::Vector pos = Math::Vector(1.1f, -1.0f, 1.0f); // relative cargo->SetPosition(pos); @@ -995,8 +995,8 @@ bool CTaskManip::TransporterTakeObject() } else { - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(3); // takes with the hand + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(3); // takes with the hand Math::Vector pos = Math::Vector(4.7f, 0.0f, 0.0f); // relative to the hand (lem4) cargo->SetPosition(pos); @@ -1020,8 +1020,8 @@ bool CTaskManip::TransporterTakeObject() if ( m_bSubm ) { - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(2); // takes with the right claw + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(2); // takes with the right claw pos = Math::Vector(1.1f, -1.0f, 1.0f); // relative cargo->SetPosition(pos); @@ -1031,8 +1031,8 @@ bool CTaskManip::TransporterTakeObject() } else { - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(3); // takes with the hand + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(3); // takes with the hand pos = Math::Vector(4.7f, 0.0f, 0.0f); // relative to the hand (lem4) cargo->SetPosition(pos); @@ -1054,8 +1054,8 @@ bool CTaskManip::TransporterTakeObject() m_cargoType = cargo->GetType(); - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(3); // takes with the hand + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(3); // takes with the hand pos = Math::Vector(4.7f, 0.0f, 0.0f); // relative to the hand (lem4) cargo->SetPosition(pos); @@ -1080,7 +1080,7 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationX(0.0f); cargo->SetRotationZ(Math::PI/2.0f); cargo->SetRotationY(0.0f); - dynamic_cast(cargo)->SetTransporterPart(3); // takes with the hand + dynamic_cast(*cargo).SetTransporterPart(3); // takes with the hand m_object->SetPower(nullptr); m_object->SetCargo(cargo); // takes @@ -1094,15 +1094,15 @@ bool CTaskManip::TransporterTakeObject() if (other == nullptr) return false; assert(other->Implements(ObjectInterfaceType::Powered)); - CObject* cargo = dynamic_cast(other)->GetPower(); + CObject* cargo = dynamic_cast(*other).GetPower(); if (cargo == nullptr) return false; // the other does not have a battery? assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(other)->SetPower(nullptr); - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(3); // takes with the hand + dynamic_cast(*other).SetPower(nullptr); + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(3); // takes with the hand pos = Math::Vector(4.7f, 0.0f, 0.0f); // relative to the hand (lem4) cargo->SetPosition(pos); @@ -1137,7 +1137,7 @@ bool CTaskManip::TransporterDeposeObject() cargo->SetRotationZ(0.0f); cargo->FloorAdjust(); // plate well on the ground - dynamic_cast(cargo)->SetTransporter(nullptr); + dynamic_cast(*cargo).SetTransporter(nullptr); m_object->SetCargo(nullptr); // deposit } @@ -1157,7 +1157,7 @@ bool CTaskManip::TransporterDeposeObject() cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.0f); - dynamic_cast(cargo)->SetTransporter(nullptr); + dynamic_cast(*cargo).SetTransporter(nullptr); m_object->SetCargo(nullptr); // deposit } @@ -1172,8 +1172,8 @@ bool CTaskManip::TransporterDeposeObject() if (m_object->GetPower() != nullptr) return false; - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(0); // carried by the base + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(0); // carried by the base cargo->SetPosition(m_object->GetPowerPosition()); cargo->SetRotationY(0.0f); @@ -1193,7 +1193,7 @@ bool CTaskManip::TransporterDeposeObject() if (other == nullptr) return false; assert(other->Implements(ObjectInterfaceType::Powered)); - CObject* cargo = dynamic_cast(other)->GetPower(); + CObject* cargo = dynamic_cast(*other).GetPower(); if (cargo != nullptr) return false; // the other already has a battery? cargo = m_object->GetCargo(); @@ -1202,14 +1202,14 @@ bool CTaskManip::TransporterDeposeObject() m_cargoType = cargo->GetType(); - dynamic_cast(other)->SetPower(cargo); - dynamic_cast(cargo)->SetTransporter(other); + dynamic_cast(*other).SetPower(cargo); + dynamic_cast(*cargo).SetTransporter(other); - cargo->SetPosition(dynamic_cast(other)->GetPowerPosition()); + cargo->SetPosition(dynamic_cast(*other).GetPowerPosition()); cargo->SetRotationY(0.0f); cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.0f); - dynamic_cast(cargo)->SetTransporterPart(0); // carried by the base + dynamic_cast(*cargo).SetTransporterPart(0); // carried by the base m_object->SetCargo(nullptr); // deposit } diff --git a/src/object/task/taskrecover.cpp b/src/object/task/taskrecover.cpp index 021a9f89..f56503d5 100644 --- a/src/object/task/taskrecover.cpp +++ b/src/object/task/taskrecover.cpp @@ -194,7 +194,7 @@ Error CTaskRecover::Start() CObject* power = dynamic_cast(m_object)->GetPower(); if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_RECOVER_ENERGY; - float energy = dynamic_cast(power)->GetEnergy(); + float energy = dynamic_cast(*power).GetEnergy(); if ( energy < ENERGY_RECOVER+0.05f ) return ERR_RECOVER_ENERGY; Math::Matrix* mat = m_object->GetWorldMatrix(0); @@ -376,5 +376,5 @@ bool CTaskRecover::Abort() CObject* CTaskRecover::SearchRuin() { - return CObjectManager::GetInstancePointer()->FindNearest(nullptr, m_recoverPos, {OBJECT_RUINmobilew1, OBJECT_RUINmobilew2, OBJECT_RUINmobilet1, OBJECT_RUINmobilet2, OBJECT_RUINmobiler1, OBJECT_RUINmobiler2, OBJECT_RUINdoor, OBJECT_RUINsupport, OBJECT_RUINradar}, 40.0f/g_unit); + return CObjectManager::GetInstancePointer()->FindNearest(nullptr, m_recoverPos, {OBJECT_RUINmobilew1, OBJECT_RUINmobilew2, OBJECT_RUINmobilet1, OBJECT_RUINmobilet2, OBJECT_RUINmobiler1, OBJECT_RUINmobiler2}, 40.0f/g_unit); } diff --git a/src/object/task/taskshield.cpp b/src/object/task/taskshield.cpp index ad39a688..916a5158 100644 --- a/src/object/task/taskshield.cpp +++ b/src/object/task/taskshield.cpp @@ -309,7 +309,7 @@ Error CTaskShield::Start(TaskShieldMode mode, float delay) CObject* power = m_object->GetPower(); if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_SHIELD_ENERGY; - float energy = dynamic_cast(power)->GetEnergy(); + float energy = dynamic_cast(*power).GetEnergy(); if ( energy == 0.0f ) return ERR_SHIELD_ENERGY; Math::Matrix* mat = m_object->GetWorldMatrix(0); diff --git a/src/object/task/taskspiderexplo.cpp b/src/object/task/taskspiderexplo.cpp index ed1b8ef7..2acab5fd 100644 --- a/src/object/task/taskspiderexplo.cpp +++ b/src/object/task/taskspiderexplo.cpp @@ -57,7 +57,7 @@ bool CTaskSpiderExplo::EventProcess(const Event &event) if ( event.type != EVENT_FRAME ) return true; // Momentarily stationary object (ant on the back)? - if ( dynamic_cast(m_object)->GetFixed() ) + if ( dynamic_cast(*m_object).GetFixed() ) { m_bError = true; return true; diff --git a/src/object/task/tasktake.cpp b/src/object/task/tasktake.cpp index c1c0de9e..6f7360bd 100644 --- a/src/object/task/tasktake.cpp +++ b/src/object/task/tasktake.cpp @@ -131,9 +131,9 @@ Error CTaskTake::Start() CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f); if (other != nullptr) assert(other->Implements(ObjectInterfaceType::Powered)); - if (other != nullptr && dynamic_cast(other)->GetPower() != nullptr) + if (other != nullptr && dynamic_cast(*other).GetPower() != nullptr) { - CObject* power = dynamic_cast(other)->GetPower(); + CObject* power = dynamic_cast(*other).GetPower(); type = power->GetType(); if ( type == OBJECT_URANIUM ) return ERR_MANIP_RADIO; assert(power->Implements(ObjectInterfaceType::Transportable)); @@ -161,7 +161,7 @@ Error CTaskTake::Start() CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f); if (other != nullptr) assert(other->Implements(ObjectInterfaceType::Powered)); - if (other != nullptr && dynamic_cast(other)->GetPower() == nullptr ) + if (other != nullptr && dynamic_cast(*other).GetPower() == nullptr ) { //? m_camera->StartCentering(m_object, Math::PI*0.3f, -Math::PI*0.1f, 0.0f, 0.8f); m_arm = TTA_FRIEND; @@ -390,7 +390,7 @@ CObject* CTaskTake::SearchFriendObject(float &angle, assert(pObj->Implements(ObjectInterfaceType::Powered)); - CObject* power = dynamic_cast(pObj)->GetPower(); + CObject* power = dynamic_cast(*pObj).GetPower(); if (power != nullptr) { if ( power->GetLock() ) continue; @@ -398,7 +398,7 @@ CObject* CTaskTake::SearchFriendObject(float &angle, } Math::Matrix* mat = pObj->GetWorldMatrix(0); - Math::Vector oPos = Math::Transform(*mat, dynamic_cast(pObj)->GetPowerPosition()); + Math::Vector oPos = Math::Transform(*mat, dynamic_cast(*pObj).GetPowerPosition()); float distance = fabs(Math::Distance(oPos, iPos) - (iRad+1.0f)); if ( distance <= dLimit ) @@ -406,7 +406,7 @@ CObject* CTaskTake::SearchFriendObject(float &angle, angle = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) { - Math::Vector powerPos = dynamic_cast(pObj)->GetPowerPosition(); + Math::Vector powerPos = dynamic_cast(*pObj).GetPowerPosition(); m_height = powerPos.y; return pObj; } @@ -430,8 +430,8 @@ bool CTaskTake::TransporterTakeObject() m_cargoType = cargo->GetType(); - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(4); // takes with the hand + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(4); // takes with the hand //? cargo->SetPosition(Math::Vector(2.2f, -1.0f, 1.1f)); cargo->SetPosition(Math::Vector(1.7f, -0.5f, 1.1f)); @@ -449,15 +449,15 @@ bool CTaskTake::TransporterTakeObject() if (other == nullptr) return false; assert(other->Implements(ObjectInterfaceType::Powered)); - CObject* cargo = dynamic_cast(other)->GetPower(); + CObject* cargo = dynamic_cast(*other).GetPower(); if (cargo == nullptr) return false; // the other does not have a battery? assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(other)->SetPower(nullptr); - dynamic_cast(cargo)->SetTransporter(m_object); - dynamic_cast(cargo)->SetTransporterPart(4); // takes with the hand + dynamic_cast(*other).SetPower(nullptr); + dynamic_cast(*cargo).SetTransporter(m_object); + dynamic_cast(*cargo).SetTransporterPart(4); // takes with the hand //? cargo->SetPosition(Math::Vector(2.2f, -1.0f, 1.1f)); cargo->SetPosition(Math::Vector(1.7f, -0.5f, 1.1f)); @@ -492,7 +492,7 @@ bool CTaskTake::TransporterDeposeObject() cargo->SetRotationZ(0.0f); cargo->FloorAdjust(); // plate well on the ground - dynamic_cast(cargo)->SetTransporter(nullptr); + dynamic_cast(*cargo).SetTransporter(nullptr); m_object->SetCargo(nullptr); // deposit } @@ -503,7 +503,7 @@ bool CTaskTake::TransporterDeposeObject() if (other == nullptr) return false; assert(other->Implements(ObjectInterfaceType::Powered)); - CObject* cargo = dynamic_cast(other)->GetPower(); + CObject* cargo = dynamic_cast(*other).GetPower(); if (cargo != nullptr) return false; // the other already has a battery? cargo = m_object->GetCargo(); @@ -511,14 +511,14 @@ bool CTaskTake::TransporterDeposeObject() assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(other)->SetPower(cargo); - dynamic_cast(cargo)->SetTransporter(other); + dynamic_cast(*other).SetPower(cargo); + dynamic_cast(*cargo).SetTransporter(other); - cargo->SetPosition(dynamic_cast(other)->GetPowerPosition()); + cargo->SetPosition(dynamic_cast(*other).GetPowerPosition()); cargo->SetRotationY(0.0f); cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.0f); - dynamic_cast(cargo)->SetTransporterPart(0); // carried by the base + dynamic_cast(*cargo).SetTransporterPart(0); // carried by the base m_object->SetCargo(nullptr); // deposit } diff --git a/src/object/task/taskterraform.cpp b/src/object/task/taskterraform.cpp index c0267230..c0160bf8 100644 --- a/src/object/task/taskterraform.cpp +++ b/src/object/task/taskterraform.cpp @@ -215,7 +215,7 @@ Error CTaskTerraform::Start() power = m_object->GetPower(); if ( power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer) ) return ERR_TERRA_ENERGY; - energy = dynamic_cast(power)->GetEnergy(); + energy = dynamic_cast(*power).GetEnergy(); if ( energy < ENERGY_TERRA+0.05f ) return ERR_TERRA_ENERGY; speed = m_physics->GetMotorSpeed(); @@ -426,7 +426,7 @@ bool CTaskTerraform::Terraform() { if ( dist > 5.0f ) continue; m_engine->GetPyroManager()->Create(Gfx::PT_EXPLOT, pObj); - dynamic_cast(m_object)->DamageObject(DamageType::Explosive, 0.9f); + dynamic_cast(*m_object).DamageObject(DamageType::Explosive, 0.9f); } else if ( type == OBJECT_PLANT0 || type == OBJECT_PLANT1 || @@ -444,6 +444,7 @@ bool CTaskTerraform::Terraform() { if ( dist > 7.5f ) continue; m_engine->GetPyroManager()->Create(Gfx::PT_FRAGV, pObj); + } else // Other? { @@ -454,7 +455,7 @@ bool CTaskTerraform::Terraform() else { if ( !pObj->Implements(ObjectInterfaceType::Movable) ) continue; - motion = dynamic_cast(pObj)->GetMotion(); + motion = dynamic_cast(*pObj).GetMotion(); dist = Math::Distance(m_terraPos, pObj->GetPosition()); if ( dist > ACTION_RADIUS ) continue; @@ -462,13 +463,13 @@ bool CTaskTerraform::Terraform() if ( type == OBJECT_ANT || type == OBJECT_SPIDER ) { assert(pObj->Implements(ObjectInterfaceType::TaskExecutor)); - dynamic_cast(pObj)->StopForegroundTask(); + dynamic_cast(*pObj).StopForegroundTask(); int actionType = -1; if (type == OBJECT_ANT) actionType = MAS_BACK1; if (type == OBJECT_SPIDER) actionType = MSS_BACK1; motion->SetAction(actionType, 0.8f+Math::Rand()*0.3f); - dynamic_cast(pObj)->SetFixed(true); // not moving + dynamic_cast(*pObj).SetFixed(true); // not moving if ( dist > 5.0f ) continue; m_engine->GetPyroManager()->Create(Gfx::PT_EXPLOO, pObj); diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 6917992a..d3b69cd8 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -804,7 +804,7 @@ void CPhysics::MotorUpdate(float aTime, float rTime) if (m_object->Implements(ObjectInterfaceType::Powered)) { - power = dynamic_cast(dynamic_cast(m_object)->GetPower()); // searches for the object battery uses + power = dynamic_cast(dynamic_cast(*m_object).GetPower()); // searches for the object battery uses if ( GetObjectEnergy(m_object) == 0.0f ) // no battery or flat? { motorSpeed.x = 0.0f; @@ -822,7 +822,7 @@ void CPhysics::MotorUpdate(float aTime, float rTime) } } - if ( m_object->GetType() == OBJECT_HUMAN && dynamic_cast(m_object)->GetDying() == DeathType::Dead ) // dead man? + if ( m_object->GetType() == OBJECT_HUMAN && dynamic_cast(*m_object).GetDying() == DeathType::Dead ) // dead man? { motorSpeed.x = 0.0f; motorSpeed.z = 0.0f; @@ -852,7 +852,7 @@ void CPhysics::MotorUpdate(float aTime, float rTime) } if ( m_object->Implements(ObjectInterfaceType::JetFlying) && - dynamic_cast(m_object)->GetRange() > 0.0f ) // limited flight range? + dynamic_cast(*m_object).GetRange() > 0.0f ) // limited flight range? { CJetFlyingObject* jetFlying = dynamic_cast(m_object); if ( m_bLand || m_bSwim || m_bObstacle ) // on the ground or in the water? @@ -960,7 +960,7 @@ void CPhysics::MotorUpdate(float aTime, float rTime) bool reactorCool = true; if ( m_object->Implements(ObjectInterfaceType::JetFlying) ) { - reactorCool = dynamic_cast(m_object)->GetReactorRange() > 0.1f; + reactorCool = dynamic_cast(*m_object).GetReactorRange() > 0.1f; } if ( motorSpeed.y > 0.0f && reactorCool && pos.y < h ) { @@ -1463,7 +1463,7 @@ bool CPhysics::EventFrame(const Event &event) iAngle = angle = m_object->GetRotation(); // Accelerate is the descent, brake is the ascent. - if ( m_bFreeze || (m_object->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(m_object)->IsDying()) ) + if ( m_bFreeze || (m_object->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(*m_object).IsDying()) ) { m_linMotion.terrainSpeed.x = 0.0f; m_linMotion.terrainSpeed.z = 0.0f; @@ -1618,8 +1618,8 @@ void CPhysics::SoundMotor(float rTime) else if ( type == OBJECT_ANT ) { assert(m_object->Implements(ObjectInterfaceType::Destroyable)); - if ( dynamic_cast(m_object)->GetDying() == DeathType::Burning || - dynamic_cast(m_object)->GetFixed() ) + if ( dynamic_cast(*m_object).GetDying() == DeathType::Burning || + dynamic_cast(*m_object).GetFixed() ) { if ( m_lastSoundInsect <= 0.0f ) { @@ -1649,7 +1649,7 @@ void CPhysics::SoundMotor(float rTime) else m_lastSoundInsect = 1.5f+Math::Rand()*4.0f; } } - else if ( dynamic_cast(m_object)->GetDying() == DeathType::Burning ) + else if ( dynamic_cast(*m_object).GetDying() == DeathType::Burning ) { if ( m_lastSoundInsect <= 0.0f ) { @@ -1670,7 +1670,7 @@ void CPhysics::SoundMotor(float rTime) else m_lastSoundInsect = 1.5f+Math::Rand()*4.0f; } } - else if ( dynamic_cast(m_object)->GetDying() == DeathType::Burning ) + else if ( dynamic_cast(*m_object).GetDying() == DeathType::Burning ) { if ( m_lastSoundInsect <= 0.0f ) { @@ -1682,8 +1682,8 @@ void CPhysics::SoundMotor(float rTime) else if ( type == OBJECT_SPIDER ) { assert(m_object->Implements(ObjectInterfaceType::Destroyable)); - if ( dynamic_cast(m_object)->GetDying() == DeathType::Burning || - dynamic_cast(m_object)->GetFixed() ) + if ( dynamic_cast(*m_object).GetDying() == DeathType::Burning || + dynamic_cast(*m_object).GetFixed() ) { if ( m_lastSoundInsect <= 0.0f ) { @@ -2506,7 +2506,7 @@ int CPhysics::ObjectAdapt(const Math::Vector &pos, const Math::Vector &angle) int colType; ObjectType iType, oType; - if ( m_object->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(m_object)->IsDying() ) return 0; // is burning or exploding? + if ( m_object->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(*m_object).IsDying() ) return 0; // is burning or exploding? if ( !m_object->GetCollisions() ) return 0; // iiPos = sphere center is the old position. @@ -2525,7 +2525,7 @@ int CPhysics::ObjectAdapt(const Math::Vector &pos, const Math::Vector &angle) { if ( pObj == m_object ) continue; // yourself? if (IsObjectBeingTransported(pObj)) continue; - //if ( pObj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(pObj)->IsDying() ) continue; // is burning or exploding? + if ( pObj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(*pObj).GetDying() == DeathType::Exploding ) continue; // is exploding? oType = pObj->GetType(); if ( oType == OBJECT_TOTO ) continue; @@ -2627,7 +2627,7 @@ int CPhysics::ObjectAdapt(const Math::Vector &pos, const Math::Vector &angle) CPhysics* ph = nullptr; if (pObj->Implements(ObjectInterfaceType::Movable)) - ph = dynamic_cast(pObj)->GetPhysics(); + ph = dynamic_cast(*pObj).GetPhysics(); if ( ph != nullptr ) { oAngle = pObj->GetRotation(); @@ -2727,7 +2727,7 @@ bool CPhysics::ExploOther(ObjectType iType, if ( force > destructionForce && destructionForce >= 0.0f ) { // TODO: implement "killer"? - dynamic_cast(pObj)->DamageObject(damageType); + dynamic_cast(*pObj).DamageObject(damageType); } } @@ -2753,7 +2753,7 @@ bool CPhysics::ExploOther(ObjectType iType, { assert(pObj->Implements(ObjectInterfaceType::Damageable)); // TODO: implement "killer"? - dynamic_cast(pObj)->DamageObject(DamageType::Collision, force/400.0f); + dynamic_cast(*pObj).DamageObject(DamageType::Collision, force/400.0f); } if (oType == OBJECT_MOBILEwa || @@ -2790,10 +2790,24 @@ bool CPhysics::ExploOther(ObjectType iType, { assert(pObj->Implements(ObjectInterfaceType::Damageable)); // TODO: implement "killer"? - dynamic_cast(pObj)->DamageObject(DamageType::Collision, force/200.0f); + dynamic_cast(*pObj).DamageObject(DamageType::Collision, force/200.0f); } } + if((oType == OBJECT_PLANT0 || + oType == OBJECT_PLANT1 || + oType == OBJECT_PLANT2 || + oType == OBJECT_PLANT3 || + oType == OBJECT_PLANT4 || + oType == OBJECT_PLANT15 || + oType == OBJECT_PLANT16 || + oType == OBJECT_PLANT17 || + oType == OBJECT_PLANT18)&& + GetDriveFromObject(iType)==DriveType::Heavy) + { + dynamic_cast(pObj)->DestroyObject(DestructionType::Squash); + } + return false; } @@ -2816,7 +2830,7 @@ int CPhysics::ExploHimself(ObjectType iType, ObjectType oType, float force) if ( force > destructionForce && destructionForce >= 0.0f ) { // TODO: implement "killer"? - dynamic_cast(m_object)->DamageObject(DamageType::Explosive); + dynamic_cast(*m_object).DamageObject(DamageType::Explosive); return 2; } @@ -2903,7 +2917,7 @@ int CPhysics::ExploHimself(ObjectType iType, ObjectType oType, float force) } // TODO: implement "killer"? - if ( dynamic_cast(m_object)->DamageObject(DamageType::Collision, force) ) return 2; + if ( dynamic_cast(*m_object).DamageObject(DamageType::Collision, force) ) return 2; } } @@ -2955,9 +2969,9 @@ void CPhysics::PowerParticle(float factor, bool bBreak) bCarryPower = false; if (m_object->Implements(ObjectInterfaceType::Carrier)) { - CObject* cargo = dynamic_cast(m_object)->GetCargo(); + CObject* cargo = dynamic_cast(*m_object).GetCargo(); if ( cargo != nullptr && cargo->Implements(ObjectInterfaceType::PowerContainer) && - dynamic_cast(cargo)->IsRechargeable() && + dynamic_cast(*cargo).IsRechargeable() && m_object->GetPartRotationZ(1) == ARM_STOCK_ANGLE1 ) { bCarryPower = true; // carries a battery @@ -3250,7 +3264,7 @@ void CPhysics::MotorParticle(float aTime, float rTime) } else // in flight? { - if ( !m_bMotor || (m_object->Implements(ObjectInterfaceType::JetFlying) && dynamic_cast(m_object)->GetReactorRange() == 0.0f) ) return; + if ( !m_bMotor || (m_object->Implements(ObjectInterfaceType::JetFlying) && dynamic_cast(*m_object).GetReactorRange() == 0.0f) ) return; if ( m_reactorTemperature < 1.0f ) // not too hot? { @@ -3380,7 +3394,7 @@ void CPhysics::MotorParticle(float aTime, float rTime) } else // in flight? { - if ( !m_bMotor || (m_object->Implements(ObjectInterfaceType::JetFlying) && dynamic_cast(m_object)->GetReactorRange() == 0.0f) ) return; + if ( !m_bMotor || (m_object->Implements(ObjectInterfaceType::JetFlying) && dynamic_cast(*m_object).GetReactorRange() == 0.0f) ) return; if ( aTime-m_lastMotorParticle < m_engine->ParticleAdapt(0.02f) ) return; m_lastMotorParticle = aTime; @@ -3441,7 +3455,7 @@ void CPhysics::MotorParticle(float aTime, float rTime) if ( (type == OBJECT_HUMAN || type == OBJECT_TECH) && m_bSwim ) { - if ( !m_object->Implements(ObjectInterfaceType::Destroyable) || dynamic_cast(m_object)->GetDying() != DeathType::Dead ) + if ( !m_object->Implements(ObjectInterfaceType::Destroyable) || dynamic_cast(*m_object).GetDying() != DeathType::Dead ) { h = Math::Mod(aTime, 5.0f); if ( h < 3.5f && ( h < 1.5f || h > 1.6f ) ) return; @@ -3764,7 +3778,7 @@ Error CPhysics::GetError() if (m_object->Implements(ObjectInterfaceType::ProgramStorage)) { - if ( dynamic_cast(m_object)->GetActiveVirus() ) + if ( dynamic_cast(*m_object).GetActiveVirus() ) { return ERR_VEH_VIRUS; } @@ -3772,14 +3786,14 @@ Error CPhysics::GetError() if (m_object->Implements(ObjectInterfaceType::Powered)) { - CObject* power = dynamic_cast(m_object)->GetPower(); // searches for the object battery used + CObject* power = dynamic_cast(*m_object).GetPower(); // searches for the object battery used if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) { return ERR_VEH_POWER; } else { - if ( dynamic_cast(power)->GetEnergy() == 0.0f ) return ERR_VEH_ENERGY; + if ( dynamic_cast(*power).GetEnergy() == 0.0f ) return ERR_VEH_ENERGY; } } diff --git a/src/script/cbottoken.cpp b/src/script/cbottoken.cpp index 54b4352d..46e01eb4 100644 --- a/src/script/cbottoken.cpp +++ b/src/script/cbottoken.cpp @@ -125,6 +125,12 @@ const char* GetObjectName(ObjectType type) if ( type == OBJECT_BEE ) return "AlienWasp"; if ( type == OBJECT_WORM ) return "AlienWorm"; if ( type == OBJECT_RUINmobilew1) return "Wreck"; + if ( type == OBJECT_RUINfactory ) return "Ruin"; + if ( type == OBJECT_PLANT0 ) return "Bush"; + if ( type == OBJECT_ROOT5 ) return "GraviPlant"; + if ( type == OBJECT_QUARTZ0 ) return "Crystal"; + if ( type == OBJECT_MUSHROOM1 ) return "BrownMushroom"; + if ( type == OBJECT_MUSHROOM2 ) return "GreenMushroom"; return ""; } @@ -233,6 +239,12 @@ std::string GetHelpFilename(ObjectType type) if ( type == OBJECT_BEE ) helpfile = "object/wasp"; if ( type == OBJECT_WORM ) helpfile = "object/worm"; if ( type == OBJECT_RUINmobilew1) helpfile = "object/wreck"; + if ( type == OBJECT_RUINfactory ) helpfile = "object/ruin"; + if ( type == OBJECT_PLANT0 ) helpfile = "object/bush"; + if ( type == OBJECT_ROOT5 ) helpfile = "object/gravi"; + if ( type == OBJECT_QUARTZ0 ) helpfile = "object/crystal"; + if ( type == OBJECT_MUSHROOM1 ) helpfile = "object/bromush"; + if ( type == OBJECT_MUSHROOM2 ) helpfile = "object/gremush"; if (helpfile.empty()) return ""; @@ -249,6 +261,7 @@ std::string GetHelpFilename(const char *token) if ( strcmp(token, "if" ) == 0 ) helpfile = "cbot/if"; if ( strcmp(token, "else" ) == 0 ) helpfile = "cbot/if"; + if ( strcmp(token, "repeat" ) == 0 ) helpfile = "cbot/repeat"; if ( strcmp(token, "for" ) == 0 ) helpfile = "cbot/for"; if ( strcmp(token, "while" ) == 0 ) helpfile = "cbot/while"; if ( strcmp(token, "do" ) == 0 ) helpfile = "cbot/do"; @@ -310,6 +323,8 @@ std::string GetHelpFilename(const char *token) if ( strcmp(token, "researched" ) == 0 ) helpfile = "cbot/researched"; if ( strcmp(token, "buildingenabled") == 0 ) helpfile = "cbot/buildingenabled"; if ( strcmp(token, "build" ) == 0 ) helpfile = "cbot/build"; + if ( strcmp(token, "flag" ) == 0 ) helpfile = "cbot/flag"; + if ( strcmp(token, "deflag" ) == 0 ) helpfile = "cbot/deflag"; if ( strcmp(token, "wait" ) == 0 ) helpfile = "cbot/wait"; if ( strcmp(token, "move" ) == 0 ) helpfile = "cbot/move"; if ( strcmp(token, "turn" ) == 0 ) helpfile = "cbot/turn"; @@ -332,23 +347,24 @@ std::string GetHelpFilename(const char *token) if ( strcmp(token, "topo" ) == 0 ) helpfile = "cbot/topo"; if ( strcmp(token, "message" ) == 0 ) helpfile = "cbot/message"; if ( strcmp(token, "abstime" ) == 0 ) helpfile = "cbot/abstime"; + if ( strcmp(token, "Blue" ) == 0 ) helpfile = "cbot/flag"; + if ( strcmp(token, "Red" ) == 0 ) helpfile = "cbot/flag"; + if ( strcmp(token, "Green" ) == 0 ) helpfile = "cbot/flag"; + if ( strcmp(token, "Yellow" ) == 0 ) helpfile = "cbot/flag"; + if ( strcmp(token, "Violet" ) == 0 ) helpfile = "cbot/flag"; if ( strcmp(token, "BlackArrow" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "RedArrow" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "White" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "Black" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "Gray" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "LightGray" ) == 0 ) helpfile = "cbot/pendown"; - if ( strcmp(token, "Red" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "Pink" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "Purple" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "Orange" ) == 0 ) helpfile = "cbot/pendown"; - if ( strcmp(token, "Yellow" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "Beige" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "Brown" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "Skin" ) == 0 ) helpfile = "cbot/pendown"; - if ( strcmp(token, "Green" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "LightGreen" ) == 0 ) helpfile = "cbot/pendown"; - if ( strcmp(token, "Blue" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "LightBlue" ) == 0 ) helpfile = "cbot/pendown"; if ( strcmp(token, "InFront" ) == 0 ) helpfile = "cbot/grab"; if ( strcmp(token, "Behind" ) == 0 ) helpfile = "cbot/grab"; @@ -470,6 +486,8 @@ bool IsFunction(const char *token) if ( strcmp(token, "researched" ) == 0 ) return true; if ( strcmp(token, "buildingenabled") == 0 ) return true; if ( strcmp(token, "build" ) == 0 ) return true; + if ( strcmp(token, "flag" ) == 0 ) return true; + if ( strcmp(token, "deflag" ) == 0 ) return true; if ( strcmp(token, "wait" ) == 0 ) return true; if ( strcmp(token, "move" ) == 0 ) return true; if ( strcmp(token, "turn" ) == 0 ) return true; @@ -526,6 +544,7 @@ const char* GetHelpText(const char *token) { if ( strcmp(token, "if" ) == 0 ) return "if ( condition ) { code }"; if ( strcmp(token, "else" ) == 0 ) return "else { code }"; + if ( strcmp(token, "repeat" ) == 0 ) return "repeat ( number )"; if ( strcmp(token, "for" ) == 0 ) return "for ( before ; condition ; end )"; if ( strcmp(token, "while" ) == 0 ) return "while ( condition ) { code }"; if ( strcmp(token, "do" ) == 0 ) return "do { code } while ( condition );"; @@ -576,6 +595,8 @@ const char* GetHelpText(const char *token) if ( strcmp(token, "researched" ) == 0 ) return "researched ( research );"; if ( strcmp(token, "buildingenabled") == 0 ) return "buildingenabled ( category );"; if ( strcmp(token, "build" ) == 0 ) return "build ( category );"; + if ( strcmp(token, "flag" ) == 0 ) return "flag ( color );"; + if ( strcmp(token, "deflag" ) == 0 ) return "deflag ( );"; if ( strcmp(token, "wait" ) == 0 ) return "wait ( time );"; if ( strcmp(token, "move" ) == 0 ) return "move ( distance );"; if ( strcmp(token, "turn" ) == 0 ) return "turn ( angle );"; diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index ba217a55..f35c7a2f 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -595,7 +595,8 @@ bool CScriptFunctions::rResearch(CBotVar* var, CBotVar* result, int& exception, { bool ok = false; if ( type == RESEARCH_iPAW || - type == RESEARCH_iGUN ) + type == RESEARCH_iGUN || + type == RESEARCH_TARGET ) { if ( center->GetType() != OBJECT_LABO ) err = ERR_WRONG_OBJ; @@ -728,15 +729,18 @@ bool CScriptFunctions::rDelete(CBotVar* var, CBotVar* result, int& exception, vo } CObject* obj = CObjectManager::GetInstancePointer()->GetObjectById(rank); - if ( obj == nullptr || (obj->Implements(ObjectInterfaceType::Old) && dynamic_cast(obj)->IsDying()) ) + if ( obj == nullptr || (obj->Implements(ObjectInterfaceType::Old) && dynamic_cast(*obj).IsDying()) ) { return true; } else { + CScript* script = static_cast(user); + bool deleteSelf = (obj == script->m_object); + if ( exploType != DestructionType::NoEffect && obj->Implements(ObjectInterfaceType::Destroyable) ) { - dynamic_cast(obj)->DestroyObject(static_cast(exploType)); + dynamic_cast(*obj).DestroyObject(static_cast(exploType)); } else { @@ -752,12 +756,13 @@ bool CScriptFunctions::rDelete(CBotVar* var, CBotVar* result, int& exception, vo } CObjectManager::GetInstancePointer()->DeleteObject(obj); } + // Returning "false" here makes sure the program doesn't try to keep executing + // if the robot just destroyed itself using delete(this.id) + // See issue #925 + return !deleteSelf; } - // Returning "false" here makes sure the program doesn't try to keep executing if the robot just destroyed itself - // using delete(this.id) - // See issue #925 - return false; + return true; } static CBotTypResult compileSearch(CBotVar* &var, void* user, CBotTypResult returnValue) @@ -1439,6 +1444,102 @@ bool CScriptFunctions::rBuild(CBotVar* var, CBotVar* result, int& exception, voi } +// Instruction "flag(color)" + +bool CScriptFunctions::rFlag(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = static_cast(user); + CObject* pThis = script->m_object; + ObjectType oType; + int color; + Error err; + + exception = 0; + + if ( !script->m_taskExecutor->IsForegroundTask() ) + { + oType = pThis->GetType(); + if ( oType != OBJECT_MOBILEfs && // allowed only for sniffer bots && humans + oType != OBJECT_MOBILEts && + oType != OBJECT_MOBILEws && + oType != OBJECT_MOBILEis && + oType != OBJECT_HUMAN && + oType != OBJECT_TECH ) + { + err = ERR_WRONG_BOT; // Wrong object + } + else + { + if ( var == nullptr ) + { + color = 0; + } + else + { + color = var->GetValInt(); + if ( color < 0 || color > static_cast(TraceColor::Violet) ) color = 0; + } + err = script->m_taskExecutor->StartTaskFlag(TFL_CREATE, color); + } + + 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; + } + } + return WaitForForegroundTask(script, result, exception); +} + +// Instruction "deflag()" + +bool CScriptFunctions::rDeflag(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = static_cast(user); + CObject* pThis = script->m_object; + ObjectType oType; + Error err; + + exception = 0; + + if ( !script->m_taskExecutor->IsForegroundTask() ) + { + oType = pThis->GetType(); + if ( oType != OBJECT_MOBILEfs && // allowed only for sniffer bots && humans + oType != OBJECT_MOBILEts && + oType != OBJECT_MOBILEws && + oType != OBJECT_MOBILEis && + oType != OBJECT_HUMAN && + oType != OBJECT_TECH ) + { + err = ERR_WRONG_BOT; // Wrong object + } + else + { + err = script->m_taskExecutor->StartTaskFlag(TFL_DELETE, 0); + } + + 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; + } + } + return WaitForForegroundTask(script, result, exception); +} + // Compilation of the instruction "produce(pos, angle, type[, scriptName[, power]])" // or "produce(type[, power])". @@ -1556,7 +1657,7 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v CObjectManager::GetInstancePointer()->CreateObject(pos, angle, OBJECT_EGG); if (object->Implements(ObjectInterfaceType::Programmable)) { - dynamic_cast(object)->SetActivity(false); + dynamic_cast(*object).SetActivity(false); } } else @@ -1565,7 +1666,11 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v { power = 1.0f; } - object = CObjectManager::GetInstancePointer()->CreateObject(pos, angle, type, power); + bool exists = IsValidObjectTypeId(type) && type != OBJECT_NULL && type != OBJECT_MAX && type != OBJECT_MOBILEpr; + if (exists) + { + object = CObjectManager::GetInstancePointer()->CreateObject(pos, angle, type, power); + } if (object == nullptr) { result->SetValInt(1); // error @@ -1574,7 +1679,7 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v if (type == OBJECT_MOBILEdr) { assert(object->Implements(ObjectInterfaceType::Old)); // TODO: temporary hack - dynamic_cast(object)->SetManual(true); + dynamic_cast(*object).SetManual(true); } script->m_main->CreateShortcuts(); } @@ -1589,7 +1694,7 @@ bool CScriptFunctions::rProduce(CBotVar* var, CBotVar* result, int& exception, v programStorage->ReadProgram(program, name2.c_str()); program->readOnly = true; program->filename = name; - dynamic_cast(object)->RunProgram(program); + dynamic_cast(*object).RunProgram(program); } } @@ -2195,7 +2300,7 @@ bool CScriptFunctions::rReceive(CBotVar* var, CBotVar* result, int& exception, v return true; } - CExchangePost* exchangePost = dynamic_cast(script->m_taskExecutor->GetForegroundTask())->FindExchangePost(power); + CExchangePost* exchangePost = dynamic_cast(*script->m_taskExecutor->GetForegroundTask()).FindExchangePost(power); script->m_returnValue = exchangePost->GetInfoValue(p); } if ( !WaitForForegroundTask(script, result, exception) ) return false; // not finished @@ -2480,7 +2585,7 @@ bool CScriptFunctions::rShield(CBotVar* var, CBotVar* result, int& exception, vo } else // up ? { - dynamic_cast(pThis)->SetShieldRadius(radius); + dynamic_cast(*pThis).SetShieldRadius(radius); err = script->m_taskExecutor->StartTaskShield(TSM_UP, 1000.0f); if ( err != ERR_OK ) { @@ -2498,7 +2603,7 @@ bool CScriptFunctions::rShield(CBotVar* var, CBotVar* result, int& exception, vo else // up? { //? result->SetValInt(1); // shows the error - dynamic_cast(pThis)->SetShieldRadius(radius); + dynamic_cast(*pThis).SetShieldRadius(radius); script->m_taskExecutor->StartTaskShield(TSM_UPDATE, 0.0f); } } @@ -2672,7 +2777,7 @@ bool CScriptFunctions::rMotor(CBotVar* var, CBotVar* result, int& exception, voi if ( turn < -1.0f ) turn = -1.0f; if ( turn > 1.0f ) turn = 1.0f; - if ( dynamic_cast(pThis) != nullptr && dynamic_cast(pThis)->GetFixed() ) // ant on the back? + if ( dynamic_cast(pThis) != nullptr && dynamic_cast(*pThis).GetFixed() ) // ant on the back? { speed = 0.0f; turn = 0.0f; @@ -2781,7 +2886,7 @@ bool CScriptFunctions::rCmdline(CBotVar* var, CBotVar* result, int& exception, v assert(pThis->Implements(ObjectInterfaceType::Programmable)); rank = var->GetValInt(); - value = dynamic_cast(pThis)->GetCmdLine(rank); + value = dynamic_cast(*pThis).GetCmdLine(rank); result->SetValFloat(value); return true; @@ -3306,7 +3411,6 @@ private: void CScriptFunctions::Init() { - CBotProgram::SetTimer(100); CBotProgram::Init(); for (int i = 0; i < OBJECT_MAX; i++) @@ -3369,6 +3473,7 @@ void CScriptFunctions::Init() CBotProgram::DefineNum("BuildAutoLab", BUILD_LABO); CBotProgram::DefineNum("BuildPowerCaptor", BUILD_PARA); CBotProgram::DefineNum("BuildExchangePost", BUILD_INFO); + CBotProgram::DefineNum("BuildVault", BUILD_SAFE); CBotProgram::DefineNum("BuildDestroyer", BUILD_DESTROYER); CBotProgram::DefineNum("FlatGround", BUILD_GFLAT); CBotProgram::DefineNum("UseFlags", BUILD_FLAG); @@ -3436,6 +3541,8 @@ void CScriptFunctions::Init() CBotProgram::AddFunction("buildingenabled", rBuildingEnabled, cOneIntReturnBool); CBotProgram::AddFunction("build", rBuild, cOneInt); + CBotProgram::AddFunction("flag", rFlag, cGrabDrop); + CBotProgram::AddFunction("deflag", rDeflag, cNull); CBotProgram::AddFunction("retobject", rGetObject, cGetObject); CBotProgram::AddFunction("retobjectbyid", rGetObjectById, cGetObject); diff --git a/src/script/scriptfunc.h b/src/script/scriptfunc.h index 2ecbf8b2..2a2cd718 100644 --- a/src/script/scriptfunc.h +++ b/src/script/scriptfunc.h @@ -110,6 +110,8 @@ private: static bool rResearched(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rBuildingEnabled(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rBuild(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); + static bool rFlag(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); + static bool rDeflag(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rProduce(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rDistance(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); static bool rDistance2d(CBot::CBotVar* var, CBot::CBotVar* result, int& exception, void* user); diff --git a/src/sound/oalsound/alsound.cpp b/src/sound/oalsound/alsound.cpp index f27a462b..26006198 100644 --- a/src/sound/oalsound/alsound.cpp +++ b/src/sound/oalsound/alsound.cpp @@ -47,22 +47,7 @@ void CALSound::CleanUp() if (m_enabled) { GetLogger()->Info("Unloading files and closing device...\n"); - StopAll(); - StopMusic(); - - m_channels.clear(); - - m_currentMusic.reset(); - - m_oldMusic.clear(); - - m_previousMusic.music.reset(); - - m_sounds.clear(); - - m_music.clear(); - - m_enabled = false; + Reset(); alcDestroyContext(m_context); alcCloseDevice(m_device); @@ -99,6 +84,24 @@ bool CALSound::Create() return true; } +void CALSound::Reset() +{ + StopAll(); + StopMusic(); + + m_channels.clear(); + + m_currentMusic.reset(); + + m_oldMusic.clear(); + + m_previousMusic.music.reset(); + + m_sounds.clear(); + + m_music.clear(); +} + bool CALSound::GetEnable() { return m_enabled; diff --git a/src/sound/oalsound/alsound.h b/src/sound/oalsound/alsound.h index 6f76c268..75e3d7f6 100644 --- a/src/sound/oalsound/alsound.h +++ b/src/sound/oalsound/alsound.h @@ -84,6 +84,7 @@ public: ~CALSound(); bool Create() override; + void Reset() override; bool Cache(SoundType, const std::string &) override; void CacheMusic(const std::string &) override; bool IsCached(SoundType) override; diff --git a/src/sound/sound.cpp b/src/sound/sound.cpp index 1cc23c4b..87d7266c 100644 --- a/src/sound/sound.cpp +++ b/src/sound/sound.cpp @@ -52,6 +52,10 @@ void CSoundInterface::CacheAll() } } +void CSoundInterface::Reset() +{ +} + bool CSoundInterface::Cache(SoundType sound, const std::string &file) { return true; diff --git a/src/sound/sound.h b/src/sound/sound.h index 78a5449d..314eb856 100644 --- a/src/sound/sound.h +++ b/src/sound/sound.h @@ -72,6 +72,10 @@ public: */ void CacheAll(); + /** Stop all sounds and music and clean cache. + */ + virtual void Reset(); + /** Function called to cache sound effect file. * This function is called by plugin interface for each file. * \param sound - id of a file, will be used to identify sound files diff --git a/src/ui/controls/edit.cpp b/src/ui/controls/edit.cpp index 62987308..570bd518 100644 --- a/src/ui/controls/edit.cpp +++ b/src/ui/controls/edit.cpp @@ -1299,7 +1299,9 @@ void CEdit::SetText(const std::string& text, bool bNew) if( m_len >= GetMaxChar() ) m_len = GetMaxChar(); m_text.resize( m_len + 1, '\0' ); + m_text[m_len] = '\0'; m_format.resize( m_len + 1, m_fontType ); + m_format[m_len] = m_fontType; font = m_fontType; j = 0; diff --git a/src/ui/controls/map.cpp b/src/ui/controls/map.cpp index ef485d82..9d9ebb48 100644 --- a/src/ui/controls/map.cpp +++ b/src/ui/controls/map.cpp @@ -1197,7 +1197,7 @@ void CMap::UpdateObject(CObject* pObj) type != OBJECT_WORM && type != OBJECT_MOBILEtg ) { - if (pObj->Implements(ObjectInterfaceType::Controllable) && !dynamic_cast(pObj)->GetSelectable()) return; + if (pObj->Implements(ObjectInterfaceType::Controllable) && !dynamic_cast(*pObj).GetSelectable()) return; } if ( pObj->GetProxyActivate() ) return; if (IsObjectBeingTransported(pObj)) return; @@ -1330,7 +1330,7 @@ void CMap::UpdateObject(CObject* pObj) color != MAPCOLOR_MOVE ) return; }*/ - if ( pObj->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(pObj)->GetSelect() ) + if ( pObj->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(*pObj).GetSelect() ) { m_map[MAPMAXOBJECT-1].type = type; m_map[MAPMAXOBJECT-1].object = pObj; diff --git a/src/ui/controls/target.cpp b/src/ui/controls/target.cpp index 2260aea1..24fa3c9a 100644 --- a/src/ui/controls/target.cpp +++ b/src/ui/controls/target.cpp @@ -143,13 +143,13 @@ CObject* CTarget::DetectFriendObject(Math::Point pos) CObject* target = obj; if ( obj->Implements(ObjectInterfaceType::PowerContainer) && IsObjectBeingTransported(obj) ) { - target = dynamic_cast(obj)->GetTransporter(); + target = dynamic_cast(*obj).GetTransporter(); } if ( !target->GetDetectable() ) continue; if ( target->GetProxyActivate() ) continue; - if ( target->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(target)->GetSelect() ) continue; - if ( !target->Implements(ObjectInterfaceType::Controllable) || !dynamic_cast(target)->GetSelectable() ) continue; + if ( target->Implements(ObjectInterfaceType::Controllable) && dynamic_cast(*target).GetSelect() ) 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/displayinfo.cpp b/src/ui/displayinfo.cpp index 107e6b07..09f6d513 100644 --- a/src/ui/displayinfo.cpp +++ b/src/ui/displayinfo.cpp @@ -109,7 +109,7 @@ bool CDisplayInfo::EventProcess(const Event &event) if ( m_toto != nullptr ) { assert(m_toto->Implements(ObjectInterfaceType::Movable)); - CMotionToto* toto = static_cast(dynamic_cast(m_toto)->GetMotion()); + CMotionToto* toto = static_cast(dynamic_cast(*m_toto).GetMotion()); assert(toto != nullptr); toto->SetMousePos(event.mousePos); } @@ -449,7 +449,7 @@ void CDisplayInfo::StartDisplayInfo(std::string filename, int index, bool bSoluc m_toto->SetDrawFront(true); assert(m_toto->Implements(ObjectInterfaceType::Movable)); - CMotionToto* toto = static_cast(dynamic_cast(m_toto)->GetMotion()); + CMotionToto* toto = static_cast(dynamic_cast(*m_toto).GetMotion()); assert(toto != nullptr); toto->StartDisplayInfo(); } @@ -840,7 +840,7 @@ void CDisplayInfo::StopDisplayInfo() if ( m_toto != nullptr ) { assert(m_toto->Implements(ObjectInterfaceType::Movable)); - CMotionToto* toto = static_cast(dynamic_cast(m_toto)->GetMotion()); + CMotionToto* toto = static_cast(dynamic_cast(*m_toto).GetMotion()); assert(toto != nullptr); toto->StopDisplayInfo(); } diff --git a/src/ui/displaytext.cpp b/src/ui/displaytext.cpp index 58d085f5..b21263b4 100644 --- a/src/ui/displaytext.cpp +++ b/src/ui/displaytext.cpp @@ -258,7 +258,7 @@ void CDisplayText::DisplayText(const char *text, Math::Vector goal, float height if ( toto != nullptr ) { assert(toto->Implements(ObjectInterfaceType::Movable)); - motion = dynamic_cast(toto)->GetMotion(); + motion = dynamic_cast(*toto).GetMotion(); if ( type == TT_ERROR ) { diff --git a/src/ui/maindialog.cpp b/src/ui/maindialog.cpp index 5cdfdd47..9321619f 100644 --- a/src/ui/maindialog.cpp +++ b/src/ui/maindialog.cpp @@ -201,6 +201,7 @@ void CMainDialog::StartPauseMenu() if ( (m_main->GetLevelCategory() == LevelCategory::Missions || // missions ? m_main->GetLevelCategory() == LevelCategory::FreeGame || // free games? + m_main->GetLevelCategory() == LevelCategory::GamePlus || // new game plus? m_main->GetLevelCategory() == LevelCategory::CustomLevels ) && // user ? m_main->GetMissionType() != MISSION_CODE_BATTLE ) { diff --git a/src/ui/mainshort.cpp b/src/ui/mainshort.cpp index a8fbbe29..9e2a51a0 100644 --- a/src/ui/mainshort.cpp +++ b/src/ui/mainshort.cpp @@ -150,7 +150,7 @@ bool CMainShort::CreateShortcuts() for (CObject* pObj : CObjectManager::GetInstancePointer()->GetAllObjects()) { if ( !pObj->GetDetectable() ) continue; - if ( pObj->Implements(ObjectInterfaceType::Controllable) && !dynamic_cast(pObj)->GetSelectable() ) continue; + if ( pObj->Implements(ObjectInterfaceType::Controllable) && !dynamic_cast(*pObj).GetSelectable() ) continue; if ( pObj->GetProxyActivate() ) continue; int icon = GetShortcutIcon(pObj->GetType()); @@ -274,9 +274,9 @@ bool CMainShort::UpdateShortcuts() if ( pc != nullptr ) { assert(m_shortcuts[i]->Implements(ObjectInterfaceType::Controllable)); - pc->SetState(STATE_CHECK, dynamic_cast(m_shortcuts[i])->GetSelect()); - pc->SetState(STATE_RUN, m_shortcuts[i]->Implements(ObjectInterfaceType::Programmable) && dynamic_cast(m_shortcuts[i])->IsProgram()); - pc->SetState(STATE_DAMAGE, dynamic_cast(m_shortcuts[i])->IsDamaging()); + pc->SetState(STATE_CHECK, dynamic_cast(*m_shortcuts[i]).GetSelect()); + pc->SetState(STATE_RUN, m_shortcuts[i]->Implements(ObjectInterfaceType::Programmable) && dynamic_cast(*m_shortcuts[i]).IsProgram()); + pc->SetState(STATE_DAMAGE, dynamic_cast(*m_shortcuts[i]).IsDamaging()); } } return true; diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp index 4070e5ec..80fd5bc5 100644 --- a/src/ui/mainui.cpp +++ b/src/ui/mainui.cpp @@ -49,6 +49,7 @@ #include "ui/screen/screen_level_list.h" #include "ui/screen/screen_loading.h" #include "ui/screen/screen_main_menu.h" +#include "ui/screen/screen_mod_list.h" #include "ui/screen/screen_player_select.h" #include "ui/screen/screen_quit.h" #include "ui/screen/screen_setup_controls.h" @@ -80,6 +81,7 @@ CMainUserInterface::CMainUserInterface() m_screenIORead = MakeUnique(m_screenLevelList.get()); m_screenIOWrite = MakeUnique(m_screenLevelList.get()); m_screenLoading = MakeUnique(); + m_screenModList = MakeUnique(m_dialog.get(), m_app->GetModManager()); m_screenSetupControls = MakeUnique(); m_screenSetupDisplay = MakeUnique(); m_screenSetupGame = MakeUnique(); @@ -184,6 +186,10 @@ void CMainUserInterface::ChangePhase(Phase phase) m_screenLevelList->SetLevelCategory(m_main->GetLevelCategory()); m_currentScreen = m_screenLevelList.get(); } + if (m_phase == PHASE_MOD_LIST) + { + m_currentScreen = m_screenModList.get(); + } if (m_phase >= PHASE_SETUPd && m_phase <= PHASE_SETUPs) { CScreenSetup* screenSetup = GetSetupScreen(m_phase); @@ -531,6 +537,7 @@ void CMainUserInterface::FrameParticle(float rTime) } else if ( m_phase == PHASE_PLAYER_SELECT || m_phase == PHASE_LEVEL_LIST || + m_phase == PHASE_MOD_LIST || m_phase == PHASE_SETUPd || m_phase == PHASE_SETUPg || m_phase == PHASE_SETUPp || @@ -762,6 +769,21 @@ bool CMainUserInterface::GetSceneSoluce() return m_screenLevelList->GetSceneSoluce(); } +bool CMainUserInterface::GetPlusTrainer() +{ + return m_screenLevelList->GetPlusTrainer(); +} + +bool CMainUserInterface::GetPlusResearch() +{ + return m_screenLevelList->GetPlusResearch(); +} + +bool CMainUserInterface::GetPlusExplorer() +{ + return m_screenLevelList->GetPlusExplorer(); +} + bool CMainUserInterface::GetGamerOnlyHead() { if (m_phase == PHASE_APPERANCE) diff --git a/src/ui/mainui.h b/src/ui/mainui.h index 50888f5b..1a33331e 100644 --- a/src/ui/mainui.h +++ b/src/ui/mainui.h @@ -50,6 +50,7 @@ class CScreenIOWrite; class CScreenLevelList; class CScreenLoading; class CScreenMainMenu; +class CScreenModList; class CScreenPlayerSelect; class CScreenQuit; class CScreenSetup; @@ -76,6 +77,9 @@ public: void ChangePhase(Phase phase); bool GetSceneSoluce(); + bool GetPlusTrainer(); + bool GetPlusResearch(); + bool GetPlusExplorer(); void UpdateChapterPassed(); void NextMission(); @@ -114,6 +118,7 @@ protected: std::unique_ptr m_screenLevelList; std::unique_ptr m_screenLoading; std::unique_ptr m_screenMainMenu; + std::unique_ptr m_screenModList; std::unique_ptr m_screenPlayerSelect; std::unique_ptr m_screenQuit; std::unique_ptr m_screenSetupControls; diff --git a/src/ui/object_interface.cpp b/src/ui/object_interface.cpp index 834523e5..9ca2c8b1 100644 --- a/src/ui/object_interface.cpp +++ b/src/ui/object_interface.cpp @@ -536,9 +536,9 @@ bool CObjectInterface::EventProcess(const Event &event) { err = m_taskExecutor->StartTaskBuild(OBJECT_INFO); } - if ( action == EVENT_OBJECT_BDESTROYER ) + if ( action == EVENT_OBJECT_BSAFE ) { - err = m_taskExecutor->StartTaskBuild(OBJECT_DESTROYER); + err = m_taskExecutor->StartTaskBuild(OBJECT_SAFE); } if ( action == EVENT_OBJECT_GFLAT ) @@ -595,7 +595,7 @@ bool CObjectInterface::EventProcess(const Event &event) ps = static_cast< CSlider* >(pw->SearchControl(EVENT_OBJECT_DIMSHIELD)); if ( ps != nullptr ) { - dynamic_cast(m_object)->SetShieldRadius((ps->GetVisibleValue()-(RADIUS_SHIELD_MIN/g_unit))/((RADIUS_SHIELD_MAX-RADIUS_SHIELD_MIN)/g_unit)); + dynamic_cast(*m_object).SetShieldRadius((ps->GetVisibleValue()-(RADIUS_SHIELD_MIN/g_unit))/((RADIUS_SHIELD_MAX-RADIUS_SHIELD_MIN)/g_unit)); } } } @@ -943,8 +943,9 @@ bool CObjectInterface::CreateInterface(bool bSelect) } } - if ( type == OBJECT_HUMAN || - type == OBJECT_TECH ) + if ( (type == OBJECT_HUMAN || + type == OBJECT_TECH ) && + !m_main->GetPlusExplorer() ) { pos.x = ox+sx*7.7f; pos.y = oy+sy*0.5f; @@ -986,7 +987,7 @@ bool CObjectInterface::CreateInterface(bool bSelect) DefaultEnter(pw, EVENT_OBJECT_MTAKE); } - if ( type == OBJECT_HUMAN ) // builder? + if ( type == OBJECT_HUMAN && !m_main->GetPlusExplorer()) // builder? { pos.x = 1.0f/640.0f; pos.y = 4.0f/480.0f; @@ -1064,8 +1065,8 @@ bool CObjectInterface::CreateInterface(bool bSelect) pos.x = ox+sx*5.4f; pos.y = oy+sy*0.1f; - pw->CreateButton(pos, ddim, 128+41, EVENT_OBJECT_BDESTROYER); - DeadInterface(pw, EVENT_OBJECT_BDESTROYER, m_main->CanBuild(OBJECT_DESTROYER, m_object->GetTeam())); + pw->CreateButton(pos, ddim, 128+47, EVENT_OBJECT_BSAFE); + DeadInterface(pw, EVENT_OBJECT_BSAFE, m_main->CanBuild(OBJECT_SAFE, m_object->GetTeam())); if ( m_main->IsBuildingEnabled(BUILD_GFLAT) ) { @@ -1116,16 +1117,39 @@ bool CObjectInterface::CreateInterface(bool bSelect) pw->CreateButton(pos, dim, 40, EVENT_OBJECT_SEARCH); DefaultEnter(pw, EVENT_OBJECT_SEARCH); - if ( m_main->IsBuildingEnabled(BUILD_GFLAT) ) - { - pos.x = ox+sx*9.0f; - pos.y = oy+sy*0.5f; - pw->CreateButton(pos, dim, 111, EVENT_OBJECT_GFLAT); - } - - pos.x = ox+sx*10.1f; + pos.x = ox+sx*9.0f; pos.y = oy+sy*0.5f; pw->CreateButton(pos, dim, 11, EVENT_OBJECT_DELSEARCH); + + if ( m_main->IsBuildingEnabled(BUILD_FLAG) ) + { + pos.x = ox+sx*10.1f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 64+54, EVENT_OBJECT_FCREATE); + + pos.x = ox+sx*11.1f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 64+55, EVENT_OBJECT_FDELETE); + + ddim.x = dim.x*0.4f; + ddim.y = dim.y*0.4f; + pos.x = ox+sx*10.1f; + pos.y = oy+sy*2.0f-ddim.y; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORb); + pc->SetColor(Gfx::Color(0.28f, 0.56f, 1.0f, 0.0f)); + pos.x += ddim.x; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORr); + pc->SetColor(Gfx::Color(1.0f, 0.0f, 0.0f, 0.0f)); + pos.x += ddim.x; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORg); + pc->SetColor(Gfx::Color(0.0f, 0.8f, 0.0f, 0.0f)); + pos.x += ddim.x; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORy); + pc->SetColor(Gfx::Color(1.0f, 0.93f, 0.0f, 0.0f)); //0x00ffec00 + pos.x += ddim.x; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORv); + pc->SetColor(Gfx::Color(0.82f, 0.004f, 0.99f, 0.0f)); //0x00d101fe + } } if ( type == OBJECT_MOBILErt && // Terraformer? @@ -1496,9 +1520,15 @@ bool CObjectInterface::CreateInterface(bool bSelect) DeadInterface(pw, EVENT_OBJECT_BPARA, m_main->CanBuild(OBJECT_PARA, m_object->GetTeam())); pos.x = ox+sx*5.4f; - pw->CreateButton(pos, ddim, 128+41, EVENT_OBJECT_BDESTROYER); - DeadInterface(pw, EVENT_OBJECT_BDESTROYER, m_main->CanBuild(OBJECT_DESTROYER, m_object->GetTeam())); + pw->CreateButton(pos, ddim, 128+47, EVENT_OBJECT_BSAFE); + DeadInterface(pw, EVENT_OBJECT_BSAFE, m_main->CanBuild(OBJECT_SAFE, m_object->GetTeam())); + if ( m_main->IsBuildingEnabled(BUILD_GFLAT) ) + { + pos.x = ox+sx*9.0f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 64+47, EVENT_OBJECT_GFLAT); + } } UpdateInterface(); m_lastUpdateTime = 0.0f; @@ -1793,11 +1823,15 @@ void CObjectInterface::UpdateInterface() EnableInterface(pw, EVENT_OBJECT_BNUCLEAR, bEnable); EnableInterface(pw, EVENT_OBJECT_BPARA, bEnable); EnableInterface(pw, EVENT_OBJECT_BINFO, bEnable); - EnableInterface(pw, EVENT_OBJECT_BDESTROYER,bEnable); + EnableInterface(pw, EVENT_OBJECT_BSAFE, bEnable); } - if ( type == OBJECT_HUMAN || // builder? - type == OBJECT_TECH ) + if ( type == OBJECT_HUMAN || // can create flags? + type == OBJECT_TECH || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis ) { CheckInterface(pw, EVENT_OBJECT_FCOLORb, m_flagColor==0); CheckInterface(pw, EVENT_OBJECT_FCOLORr, m_flagColor==1); @@ -1826,7 +1860,7 @@ void CObjectInterface::UpdateInterface() ps = static_cast< CSlider* >(pw->SearchControl(EVENT_OBJECT_DIMSHIELD)); if ( ps != nullptr ) { - ps->SetVisibleValue((RADIUS_SHIELD_MIN/g_unit)+dynamic_cast(m_object)->GetShieldRadius()*((RADIUS_SHIELD_MAX-RADIUS_SHIELD_MIN)/g_unit)); + ps->SetVisibleValue((RADIUS_SHIELD_MIN/g_unit)+dynamic_cast(*m_object).GetShieldRadius()*((RADIUS_SHIELD_MAX-RADIUS_SHIELD_MIN)/g_unit)); } } @@ -1866,7 +1900,7 @@ void CObjectInterface::UpdateInterface() pb->SetState(STATE_VISIBLE, m_buildInterface); pb = static_cast< CButton* >(pw->SearchControl(EVENT_OBJECT_BPARA)); pb->SetState(STATE_VISIBLE, m_buildInterface); - pb = static_cast< CButton* >(pw->SearchControl(EVENT_OBJECT_BDESTROYER)); + pb = static_cast< CButton* >(pw->SearchControl(EVENT_OBJECT_BSAFE)); pb->SetState(STATE_VISIBLE, m_buildInterface); pb = static_cast< CButton* >(pw->SearchControl(EVENT_OBJECT_BINFO)); pb->SetState(STATE_VISIBLE, m_buildInterface); diff --git a/src/ui/screen/screen_level_list.cpp b/src/ui/screen/screen_level_list.cpp index 2ae5e29d..b0ed6460 100644 --- a/src/ui/screen/screen_level_list.cpp +++ b/src/ui/screen/screen_level_list.cpp @@ -48,6 +48,9 @@ CScreenLevelList::CScreenLevelList(CMainDialog* mainDialog) : m_dialog(mainDialog), m_category{}, m_sceneSoluce{false}, + m_plusTrainer{false}, + m_plusResearch{false}, + m_plusExplorer{false}, m_maxList{0}, m_accessChap{0} { @@ -86,6 +89,7 @@ void CScreenLevelList::CreateInterface() if ( m_category == LevelCategory::Missions ) res = RT_TITLE_MISSION; if ( m_category == LevelCategory::FreeGame ) res = RT_TITLE_FREE; if ( m_category == LevelCategory::CodeBattles ) res = RT_TITLE_CODE_BATTLES; + if ( m_category == LevelCategory::GamePlus ) res = RT_TITLE_PLUS; if ( m_category == LevelCategory::CustomLevels ) res = RT_TITLE_USER; GetResource(RES_TEXT, res, name); pw->SetName(name); @@ -109,6 +113,7 @@ void CScreenLevelList::CreateInterface() res = RT_PLAY_CHAP_CHAPTERS; if ( m_category == LevelCategory::Missions ) res = RT_PLAY_CHAP_PLANETS; if ( m_category == LevelCategory::FreeGame ) res = RT_PLAY_CHAP_PLANETS; + if ( m_category == LevelCategory::GamePlus ) res = RT_PLAY_CHAP_PLANETS; if ( m_category == LevelCategory::CustomLevels ) res = RT_PLAY_CHAP_USERLVL; GetResource(RES_TEXT, res, name); pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, name); @@ -138,6 +143,7 @@ void CScreenLevelList::CreateInterface() if ( m_category == LevelCategory::Challenges ) res = RT_PLAY_LIST_CHALLENGES; if ( m_category == LevelCategory::Missions ) res = RT_PLAY_LIST_MISSIONS; if ( m_category == LevelCategory::FreeGame ) res = RT_PLAY_LIST_FREEGAME; + if ( m_category == LevelCategory::GamePlus ) res = RT_PLAY_LIST_MISSIONS; GetResource(RES_TEXT, res, name); pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, name); pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); @@ -179,6 +185,7 @@ void CScreenLevelList::CreateInterface() // Button displays the "soluce": if ( m_category != LevelCategory::Exercises && + m_category != LevelCategory::GamePlus && m_category != LevelCategory::FreeGame ) { pos.x = ox+sx*9.5f; @@ -191,10 +198,36 @@ void CScreenLevelList::CreateInterface() } m_sceneSoluce = false; + if ( m_category == LevelCategory::GamePlus ) + { + pos.x = ox+sx*9.5f; + pos.y = oy+sy*6.1f; + ddim.x = dim.x*3.4f; + ddim.y = dim.y*0.5f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_PLUS_TRAINER); + pc->SetState(STATE_SHADOW); + pc->ClearState(STATE_CHECK); + + pos.y = oy+sy*5.5f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_PLUS_RESEARCH); + pc->SetState(STATE_SHADOW); + pc->ClearState(STATE_CHECK); + + pos.x = ox+sx*12.9f; + pos.y = oy+sy*6.1f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_PLUS_EXPLORER); + pc->SetState(STATE_SHADOW); + pc->ClearState(STATE_CHECK); + } + m_plusTrainer = false; + m_plusResearch = false; + m_plusExplorer = false; + UpdateSceneResume(m_chap[m_category]+1, m_sel[m_category]+1); if ( m_category == LevelCategory::Missions || m_category == LevelCategory::FreeGame || + m_category == LevelCategory::GamePlus || m_category == LevelCategory::CustomLevels ) { pos.x = ox+sx*9.5f; @@ -295,6 +328,27 @@ bool CScreenLevelList::EventProcess(const Event &event) pb->SetState(STATE_CHECK, m_sceneSoluce); break; + case EVENT_INTERFACE_PLUS_TRAINER: + pb = static_cast(pw->SearchControl(EVENT_INTERFACE_PLUS_TRAINER)); + if ( pb == nullptr ) break; + m_plusTrainer = !m_plusTrainer; + pb->SetState(STATE_CHECK, m_plusTrainer); + break; + + case EVENT_INTERFACE_PLUS_RESEARCH: + pb = static_cast(pw->SearchControl(EVENT_INTERFACE_PLUS_RESEARCH)); + if ( pb == nullptr ) break; + m_plusResearch = !m_plusResearch; + pb->SetState(STATE_CHECK, m_plusResearch); + break; + + case EVENT_INTERFACE_PLUS_EXPLORER: + pb = static_cast(pw->SearchControl(EVENT_INTERFACE_PLUS_EXPLORER)); + if ( pb == nullptr ) break; + m_plusExplorer = !m_plusExplorer; + pb->SetState(STATE_CHECK, m_plusExplorer); + break; + case EVENT_INTERFACE_PLAY: m_main->SetLevel(m_category, m_chap[m_category]+1, m_sel[m_category]+1); m_main->ChangePhase(PHASE_SIMUL); @@ -331,6 +385,21 @@ bool CScreenLevelList::GetSceneSoluce() return m_sceneSoluce; } +bool CScreenLevelList::GetPlusTrainer() +{ + return m_plusTrainer; +} + +bool CScreenLevelList::GetPlusResearch() +{ + return m_plusResearch; +} + +bool CScreenLevelList::GetPlusExplorer() +{ + return m_plusExplorer; +} + // Updates the chapters of exercises or missions. void CScreenLevelList::UpdateSceneChap(int &chap) @@ -392,7 +461,7 @@ void CScreenLevelList::UpdateSceneChap(int &chap) pl->SetCheck(j, bPassed); pl->SetEnable(j, true); - if ( m_category == LevelCategory::Missions && !m_main->GetShowAll() && !bPassed ) + if ( (m_category == LevelCategory::Missions || m_category == LevelCategory::GamePlus) && !m_main->GetShowAll() && !bPassed ) { j ++; break; @@ -459,7 +528,7 @@ void CScreenLevelList::UpdateSceneList(int chap, int &sel) pl->SetCheck(j, bPassed); pl->SetEnable(j, true); - if ( m_category == LevelCategory::Missions && !m_main->GetShowAll() && !bPassed ) + if ( (m_category == LevelCategory::Missions || m_category == LevelCategory::GamePlus) && !m_main->GetShowAll() && !bPassed ) { readAll = false; } diff --git a/src/ui/screen/screen_level_list.h b/src/ui/screen/screen_level_list.h index 09eef79a..089dc558 100644 --- a/src/ui/screen/screen_level_list.h +++ b/src/ui/screen/screen_level_list.h @@ -43,6 +43,9 @@ public: void SetSelection(LevelCategory category, int chap, int rank); bool GetSceneSoluce(); + bool GetPlusTrainer(); + bool GetPlusResearch(); + bool GetPlusExplorer(); void AllMissionUpdate(); void ShowSoluceUpdate(); @@ -65,6 +68,9 @@ protected: LevelCategory m_category; bool m_sceneSoluce; + bool m_plusTrainer; + bool m_plusResearch; + bool m_plusExplorer; std::map m_chap; // selected chapter (0..8) std::map m_sel; // chosen mission (0..98) diff --git a/src/ui/screen/screen_main_menu.cpp b/src/ui/screen/screen_main_menu.cpp index 2d69ae1c..c66e61a7 100644 --- a/src/ui/screen/screen_main_menu.cpp +++ b/src/ui/screen/screen_main_menu.cpp @@ -91,14 +91,22 @@ void CScreenMainMenu::CreateInterface() pg = pw->CreateGroup(pos, ddim, 26, EVENT_LABEL1); // quit pg->SetState(STATE_SHADOW); - ddim.x = 0.18f; + ddim.x = 0.12f; ddim.y = dim.y*0.8f; pos.x = 0.41f; - pos.y = oy+sy*10.5f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MISSION); pb->SetState(STATE_SHADOW); + ddim.x = 0.06f; + pos.x = 0.53f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PLUS); + pb->SetState(STATE_SHADOW); + + ddim.x = 0.18f; + pos.x = 0.41f; + pos.y = oy+sy*9.6f; pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_FREE); pb->SetState(STATE_SHADOW); @@ -170,6 +178,13 @@ void CScreenMainMenu::CreateInterface() pb = pw->CreateButton(pos, ddim, 128+60, EVENT_INTERFACE_SATCOM); pb->SetState(STATE_SHADOW); + // Mods button + pos.x = 447.0f/640.0f; + pos.y = 313.0f/480.0f; + ddim.x = 0.09f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MODS); + pb->SetState(STATE_SHADOW); + SetBackground("textures/interface/interface.png"); CreateVersionDisplay(); } @@ -218,6 +233,11 @@ bool CScreenMainMenu::EventProcess(const Event &event) m_main->ChangePhase(PHASE_LEVEL_LIST); break; + case EVENT_INTERFACE_PLUS: + m_main->SetLevel(LevelCategory::GamePlus, 0, 0); + m_main->ChangePhase(PHASE_LEVEL_LIST); + break; + case EVENT_INTERFACE_USER: m_main->SetLevel(LevelCategory::CustomLevels, 0, 0); m_main->ChangePhase(PHASE_LEVEL_LIST); @@ -235,6 +255,9 @@ bool CScreenMainMenu::EventProcess(const Event &event) m_main->ChangePhase(PHASE_SATCOM); break; + case EVENT_INTERFACE_MODS: + m_main->ChangePhase(PHASE_MOD_LIST); + default: return true; } diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp new file mode 100644 index 00000000..856f2a20 --- /dev/null +++ b/src/ui/screen/screen_mod_list.cpp @@ -0,0 +1,582 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, 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 "ui/screen/screen_mod_list.h" + +#include "common/config.h" + +#include "app/app.h" +#include "app/modman.h" + +#include "common/logger.h" +#include "common/restext.h" +#include "common/stringutils.h" + +#include "common/resources/resourcemanager.h" + +#include "common/system/system.h" + +#include "level/robotmain.h" + +#include "math/func.h" + +#include "sound/sound.h" + +#include "ui/controls/button.h" +#include "ui/controls/edit.h" +#include "ui/controls/interface.h" +#include "ui/controls/label.h" +#include "ui/controls/list.h" +#include "ui/controls/window.h" + +#include + +namespace Ui +{ + +CScreenModList::CScreenModList(CMainDialog* dialog, CModManager* modManager) + : m_dialog(dialog), + m_modManager(modManager) +{ +} + +void CScreenModList::CreateInterface() +{ + CWindow* pw; + CEdit* pe; + CLabel* pl; + CButton* pb; + CList* pli; + Math::Point pos, ddim; + std::string name; + + // Display the window + pos.x = 0.10f; + pos.y = 0.10f; + ddim.x = 0.80f; + ddim.y = 0.80f; + pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5); + pw->SetClosable(true); + GetResource(RES_TEXT, RT_TITLE_MODS, name); + pw->SetName(name); + + pos.x = 0.10f; + pos.y = 0.40f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // orange corner + pos.x = 0.40f; + pos.y = 0.10f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // blue corner + + // Display the list of mods + pos.x = ox+sx*3; + pos.y = oy+sy*10.5f; + ddim.x = dim.x*7.5f; + ddim.y = dim.y*0.6f; + GetResource(RES_TEXT, RT_MOD_LIST, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, name); + pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); + + pos.y = oy+sy*6.7f; + ddim.y = dim.y*4.6f; + ddim.x = dim.x*6.5f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_MOD_LIST); + pli->SetState(STATE_SHADOW); + pli->SetState(STATE_EXTEND); + + // Displays the mod details + pos.x = ox+sx*9.5f; + pos.y = oy+sy*10.5f; + ddim.x = dim.x*7.5f; + ddim.y = dim.y*0.6f; + GetResource(RES_TEXT, RT_MOD_DETAILS, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, name); + pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); + + pos.y = oy+sy*6.7f; + ddim.y = dim.y*4.3f; + ddim.x = dim.x*6.5f; + pe = pw->CreateEdit(pos, ddim, 0, EVENT_INTERFACE_MOD_DETAILS); + pe->SetState(STATE_SHADOW); + pe->SetMaxChar(500); + pe->SetEditCap(false); // just to see + pe->SetHighlightCap(true); + + pos = pli->GetPos(); + ddim = pli->GetDim(); + + // Displays the mod summary + pos.x = ox+sx*3; + pos.y = oy+sy*5.4f; + ddim.x = dim.x*6.5f; + ddim.y = dim.y*0.6f; + GetResource(RES_TEXT, RT_MOD_SUMMARY, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL13, name); + pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); + + pos.x = ox+sx*3; + pos.y = oy+sy*3.6f; + ddim.x = dim.x*13.4f; + ddim.y = dim.y*1.9f; + pe = pw->CreateEdit(pos, ddim, 0, EVENT_INTERFACE_MOD_SUMMARY); + pe->SetState(STATE_SHADOW); + pe->SetMaxChar(500); + pe->SetEditCap(false); // just to see + pe->SetHighlightCap(true); + + // Apply button + pos.x = ox+sx*13.75f; + pos.y = oy+sy*2; + ddim.x = dim.x*2.0f; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MODS_APPLY); + pb->SetState(STATE_SHADOW); + + // Display the enable/disable button + pos.x -= dim.x*2.3f; + ddim.x = dim.x*2.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE); + pb->SetState(STATE_SHADOW); + + // Display the move up button + pos.x -= dim.x*0.8f; + pos.y = oy+sy*2.48; + ddim.x = dim.x*0.5; + ddim.y = dim.y*0.5; + pb = pw->CreateButton(pos, ddim, 49, EVENT_INTERFACE_MOD_MOVE_UP); + pb->SetState(STATE_SHADOW); + + // Display the move down button + pos.y = oy+sy*2; + pb = pw->CreateButton(pos, ddim, 50, EVENT_INTERFACE_MOD_MOVE_DOWN); + pb->SetState(STATE_SHADOW); + + // Display the refresh button + pos.x -= dim.x*1.3f; + pos.y = oy+sy*2; + ddim.x = dim.x*1; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, 87, EVENT_INTERFACE_MODS_REFRESH); + pb->SetState(STATE_SHADOW); + + // Display the open website button + pos.x -= dim.x*1.3f; + pb = pw->CreateButton(pos, ddim, 40, EVENT_INTERFACE_WORKSHOP); + pb->SetState(STATE_SHADOW); + + // Display the open directory button + pos.x -= dim.x*1.3f; + pb = pw->CreateButton(pos, ddim, 57, EVENT_INTERFACE_MODS_DIR); + pb->SetState(STATE_SHADOW); + + // Back button + pos.x = ox+sx*3; + ddim.x = dim.x*4; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK); + pb->SetState(STATE_SHADOW); + + FindMods(); + UpdateAll(); + + // Background + SetBackground("textures/interface/interface.png"); + CreateVersionDisplay(); +} + +bool CScreenModList::EventProcess(const Event &event) +{ + CWindow* pw; + CList* pl; + + const std::string workshopUrl = "https://www.moddb.com/games/colobot-gold-edition"; + const std::string modDir = CResourceManager::GetSaveLocation() + "/mods"; + + auto systemUtils = CSystemUtils::Create(); // platform-specific utils + + Mod const * mod; + + pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return false; + + if (event.type == pw->GetEventTypeClose() || + event.type == EVENT_INTERFACE_BACK || + (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(ESCAPE))) + { + if (m_modManager->Changes()) + { + m_dialog->StartQuestion(RT_DIALOG_CHANGES_QUESTION, true, true, false, + [this]() + { + ApplyChanges(); + CloseWindow(); + }, + [this]() + { + CloseWindow(); + }); + } + else + { + CloseWindow(); + } + return false; + } + + switch( event.type ) + { + case EVENT_INTERFACE_MOD_LIST: + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_LIST)); + if (pl == nullptr) break; + m_modSelectedIndex = pl->GetSelect(); + UpdateModSummary(); + UpdateModDetails(); + UpdateEnableDisableButton(); + UpdateUpDownButtons(); + break; + + case EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE: + mod = &m_modManager->GetMod(m_modSelectedIndex); + if (mod->enabled) + { + m_modManager->DisableMod(m_modSelectedIndex); + } + else + { + m_modManager->EnableMod(m_modSelectedIndex); + } + UpdateModList(); + UpdateEnableDisableButton(); + UpdateApplyButton(); + break; + + case EVENT_INTERFACE_MOD_MOVE_UP: + m_modSelectedIndex = m_modManager->MoveUp(m_modSelectedIndex); + UpdateModList(); + UpdateUpDownButtons(); + UpdateApplyButton(); + break; + + case EVENT_INTERFACE_MOD_MOVE_DOWN: + m_modSelectedIndex = m_modManager->MoveDown(m_modSelectedIndex); + UpdateModList(); + UpdateUpDownButtons(); + UpdateApplyButton(); + break; + + case EVENT_INTERFACE_MODS_REFRESH: + // Apply any changes before refresh so that the config file + // is better synchronized with the state of the game + case EVENT_INTERFACE_MODS_APPLY: + ApplyChanges(); + UpdateAll(); + // Start playing the main menu music again + if (!m_app->GetSound()->IsPlayingMusic()) + { + m_app->GetSound()->PlayMusic("music/Intro1.ogg", false); + m_app->GetSound()->CacheMusic("music/Intro2.ogg"); + } + break; + + case EVENT_INTERFACE_MODS_DIR: + if (!systemUtils->OpenPath(modDir)) + { + std::string title, text; + GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TITLE, title); + GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TEXT, text); + + // Workaround for Windows: the label skips everything after the first \\ character + std::string modDirWithoutBackSlashes = modDir; + std::replace(modDirWithoutBackSlashes.begin(), modDirWithoutBackSlashes.end(), '\\', '/'); + + m_dialog->StartInformation(title, title, StrUtils::Format(text.c_str(), modDirWithoutBackSlashes.c_str())); + } + break; + + case EVENT_INTERFACE_WORKSHOP: + if (!systemUtils->OpenWebsite(workshopUrl)) + { + std::string title, text; + GetResource(RES_TEXT, RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE, title); + GetResource(RES_TEXT, RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT, text); + m_dialog->StartInformation(title, title, StrUtils::Format(text.c_str(), workshopUrl.c_str())); + } + break; + + default: + return true; + } + return false; +} + +void CScreenModList::FindMods() +{ + m_modManager->FindMods(); + if (m_modManager->CountMods() != 0) + { + m_modSelectedIndex = Math::Clamp(m_modSelectedIndex, static_cast(0), m_modManager->CountMods() - 1); + } +} + +void CScreenModList::ApplyChanges() +{ + m_modManager->SaveMods(); + m_modManager->ReloadMods(); +} + +void CScreenModList::CloseWindow() +{ + m_main->ChangePhase(PHASE_MAIN_MENU); +} + +void CScreenModList::UpdateAll() +{ + UpdateModList(); + UpdateModDetails(); + UpdateModSummary(); + UpdateEnableDisableButton(); + UpdateApplyButton(); + UpdateUpDownButtons(); +} + +void CScreenModList::UpdateModList() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CList* pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_LIST)); + if (pl == nullptr) return; + + pl->Flush(); + + if (m_modManager->CountMods() == 0) + { + return; + } + + const auto& mods = m_modManager->GetMods(); + for (size_t i = 0; i < mods.size(); ++i) + { + const auto& mod = mods[i]; + const auto& name = mod.data.displayName; + pl->SetItemName(i, name); + pl->SetCheck(i, mod.enabled); + pl->SetEnable(i, true); + } + + pl->SetSelect(m_modSelectedIndex); + pl->ShowSelect(false); +} + +void CScreenModList::UpdateModDetails() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CEdit* pe = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_DETAILS)); + if (pe == nullptr) return; + + if (m_modManager->CountMods() == 0) + { + pe->SetText("No information"); + return; + } + + std::string details{}; + + const auto& mod = m_modManager->GetMod(m_modSelectedIndex); + const auto data = mod.data; + + details += "\\b;" + data.displayName + '\n'; + + std::string authorFieldName; + GetResource(RES_TEXT, RT_MOD_AUTHOR_FIELD_NAME, authorFieldName); + details += "\\s;" + authorFieldName + " "; + if (!data.author.empty()) + { + details += data.author; + } + else + { + std::string unknownAuthor; + GetResource(RES_TEXT, RT_MOD_UNKNOWN_AUTHOR, unknownAuthor); + details += unknownAuthor; + } + details += '\n'; + + details += '\n'; + + if (!data.version.empty()) + { + std::string versionFieldName; + GetResource(RES_TEXT, RT_MOD_VERSION_FIELD_NAME, versionFieldName); + details += "\\t;" + versionFieldName + '\n' + data.version + '\n'; + } + + if (!data.website.empty()) + { + std::string websiteFieldName; + GetResource(RES_TEXT, RT_MOD_WEBSITE_FIELD_NAME, websiteFieldName); + details += "\\t;" + websiteFieldName + '\n' + data.website + '\n'; + } + + std::string changesFieldName; + GetResource(RES_TEXT, RT_MOD_CHANGES_FIELD_NAME, changesFieldName); + details += "\\t;" + changesFieldName + '\n'; + if (!data.changes.empty()) + { + for (const auto& change : data.changes) + { + details += change + '\n'; + } + } + else + { + std::string noChanges; + GetResource(RES_TEXT, RT_MOD_NO_CHANGES, noChanges); + details += noChanges; + } + + pe->SetText(details); + + pe->SetFirstLine(0); +} + +void CScreenModList::UpdateModSummary() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CEdit* pe = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_SUMMARY)); + if (pe == nullptr) return; + + std::string noSummary; + GetResource(RES_TEXT, RT_MOD_NO_SUMMARY, noSummary); + + if (m_modManager->CountMods() == 0) + { + pe->SetText(noSummary); + return; + } + + const auto& mod = m_modManager->GetMod(m_modSelectedIndex); + + if (!mod.data.summary.empty()) + { + pe->SetText(mod.data.summary); + } + else + { + pe->SetText(noSummary); + } +} + +void CScreenModList::UpdateEnableDisableButton() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE)); + if (pb == nullptr) return; + + std::string buttonName{}; + + if (m_modManager->CountMods() == 0) + { + pb->ClearState(STATE_ENABLE); + + // Set some default name + GetResource(RES_TEXT, RT_MOD_ENABLE, buttonName); + pb->SetName(buttonName); + + return; + } + + const auto& mod = m_modManager->GetMod(m_modSelectedIndex); + + if (mod.enabled) + { + GetResource(RES_TEXT, RT_MOD_DISABLE, buttonName); + pb->SetName(buttonName); + } + else + { + GetResource(RES_TEXT, RT_MOD_ENABLE, buttonName); + pb->SetName(buttonName); + } +} + +void CScreenModList::UpdateApplyButton() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_APPLY)); + if (pb == nullptr) return; + + if (m_modManager->Changes()) + { + pb->SetState(STATE_ENABLE); + } + else + { + pb->ClearState(STATE_ENABLE); + } +} + +void CScreenModList::UpdateUpDownButtons() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CButton* pb_up = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_MOVE_UP)); + if (pb_up == nullptr) return; + + CButton* pb_down = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_MOVE_DOWN)); + if (pb_down == nullptr) return; + + if (m_modManager->CountMods() == 0) + { + pb_up->ClearState(STATE_ENABLE); + pb_down->ClearState(STATE_ENABLE); + return; + } + + if (m_modSelectedIndex == 0) + { + pb_up->ClearState(STATE_ENABLE); + } + else + { + pb_up->SetState(STATE_ENABLE); + } + + if (m_modSelectedIndex >= m_modManager->CountMods() - 1) + { + pb_down->ClearState(STATE_ENABLE); + } + else + { + pb_down->SetState(STATE_ENABLE); + } +} + +} // namespace Ui diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h new file mode 100644 index 00000000..ca2c130b --- /dev/null +++ b/src/ui/screen/screen_mod_list.h @@ -0,0 +1,66 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, 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 "app/modman.h" + +#include "ui/maindialog.h" + +#include "ui/screen/screen.h" + +#include + +namespace Ui +{ + +/** + * \class CScreenModList + * \brief This class is the front-end for the \ref CModManager. + */ +class CScreenModList : public CScreen +{ +public: + CScreenModList(CMainDialog* dialog, CModManager* modManager); + + void CreateInterface() override; + bool EventProcess(const Event &event) override; + +protected: + void FindMods(); + void ApplyChanges(); + void CloseWindow(); + + void UpdateAll(); + void UpdateModList(); + void UpdateModDetails(); + void UpdateModSummary(); + void UpdateEnableDisableButton(); + void UpdateApplyButton(); + void UpdateUpDownButtons(); + +protected: + Ui::CMainDialog* m_dialog; + + CModManager* m_modManager; + + size_t m_modSelectedIndex = 0; +}; + +} // namespace Ui diff --git a/src/ui/screen/screen_setup_display.cpp b/src/ui/screen/screen_setup_display.cpp index 219c7f29..f465a488 100644 --- a/src/ui/screen/screen_setup_display.cpp +++ b/src/ui/screen/screen_setup_display.cpp @@ -293,6 +293,7 @@ void CScreenSetupDisplay::UpdateApply() CWindow* pw; CButton* pb; CList* pl; + CList* pvl; CCheck* pc; int sel2; bool bFull; @@ -309,6 +310,22 @@ void CScreenSetupDisplay::UpdateApply() pc = static_cast(pw->SearchControl(EVENT_INTERFACE_FULL)); bFull = pc->TestState(STATE_CHECK); + pvl = static_cast(pw->SearchControl(EVENT_INTERFACE_VSYNC)); + if (pvl == nullptr) return; + + switch (m_engine->GetVSync()) + { + case -1: //Adaptive? + pvl->SetSelect(1); + break; + case 0: //Off? + pvl->SetSelect(0); + break; + case 1: //On? + pvl->SetSelect(2); + break; + } + if ( sel2 == m_setupSelMode && bFull == m_setupFull ) { diff --git a/src/ui/screen/screen_setup_sound.cpp b/src/ui/screen/screen_setup_sound.cpp index a963aac4..4f72b7bf 100644 --- a/src/ui/screen/screen_setup_sound.cpp +++ b/src/ui/screen/screen_setup_sound.cpp @@ -173,6 +173,8 @@ void CScreenSetupSound::UpdateSetupButtons() { pc->SetState(STATE_CHECK, m_settings->GetFocusLostMute()); } + + m_settings->SaveAudioSettings(); } // Updates the engine function of the buttons after the setup phase. @@ -199,6 +201,8 @@ void CScreenSetupSound::ChangeSetupButtons() value = ps->GetVisibleValue(); m_sound->SetMusicVolume(static_cast(value)); } + + m_settings->SaveAudioSettings(); } } // namespace Ui diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 4f4f37c7..1e4aac06 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -726,6 +726,71 @@ TEST_F(CBotUT, TestSwitchCase) ); } +TEST_F(CBotUT, TestRepeatInstruction) +{ + ExecuteTest( + "extern void TestRepeat() {\n" + " int c = 0;\n" + " for (int i = 1; i < 11; ++i)\n" + " {\n" + " repeat (i) ++c;\n" + " }\n" + " ASSERT(c == 55);\n" + "}\n" + "extern void TestRepeatBreakAndContinue() {\n" + " int c = 0;\n" + " repeat (10)\n" + " {\n" + " if (++c == 5) break;\n" + " continue;\n" + " FAIL();\n" + " }\n" + " ASSERT(c == 5);\n" + " label:repeat (10)\n" + " {\n" + " if (++c == 10) break label;\n" + " continue label;\n" + " FAIL();\n" + " }\n" + " ASSERT(c == 10);\n" + "}\n" + "extern void NoRepeatNumberLessThanOne() {\n" + " repeat (0) FAIL();\n" + " repeat (-1) FAIL();\n" + " repeat (-2) FAIL();\n" + "}\n" + "extern void EvaluateExpressionOnlyOnce() {\n" + " int c = 0;\n" + " repeat (c + 5) ASSERT(++c < 6);\n" + " ASSERT(c == 5);\n" + "}\n" + ); + ExecuteTest( + "extern void MissingOpenParen() {\n" + " repeat ;\n" + "}\n", + CBotErrOpenPar + ); + ExecuteTest( + "extern void MissingNumber() {\n" + " repeat (;\n" + "}\n", + CBotErrBadNum + ); + ExecuteTest( + "extern void WrongType() {\n" + " repeat (\"not number\");\n" + "}\n", + CBotErrBadType1 + ); + ExecuteTest( + "extern void MissingCloseParen() {\n" + " repeat (2;\n" + "}\n", + CBotErrClosePar + ); +} + TEST_F(CBotUT, ToString) { ExecuteTest( @@ -747,6 +812,48 @@ TEST_F(CBotUT, ToString) "}\n" ); + ExecuteTest( + "extern void ClassToString_2()\n" + "{\n" + " string s = new TestClass;\n" + " ASSERT(s == \"Pointer to TestClass( )\");\n" + "}\n" + "public class TestClass { /* no fields */ }\n" + ); + + ExecuteTest( + "extern void ClassInheritanceToString()\n" + "{\n" + " string s = new SubClass;\n" + " ASSERT(s == \"Pointer to SubClass( c=7, d=8, e=9 ) extends MidClass( b=4, c=5, d=6 ) extends BaseClass( a=1, b=2, c=3 )\");\n" + "}\n" + "public class BaseClass { int a = 1, b = 2, c = 3; }\n" + "public class MidClass extends BaseClass { int b = 4, c = 5, d = 6; }\n" + "public class SubClass extends MidClass { int c = 7, d = 8, e = 9; }\n" + ); + + ExecuteTest( + "extern void ClassInheritanceToString_2()\n" + "{\n" + " string s = new SubClass;\n" + " ASSERT(s == \"Pointer to SubClass( c=7, d=8, e=9 ) extends MidClass( ) extends BaseClass( a=1, b=2, c=3 )\");\n" + "}\n" + "public class BaseClass { int a = 1, b = 2, c = 3; }\n" + "public class MidClass extends BaseClass { /* no fields */ }\n" + "public class SubClass extends MidClass { int c = 7, d = 8, e = 9; }\n" + ); + + ExecuteTest( + "extern void ClassInheritanceToString_3()\n" + "{\n" + " string s = new SubClass;\n" + " ASSERT(s == \"Pointer to SubClass( c=7, d=8, e=9 ) extends MidClass( ) extends BaseClass( )\");\n" + "}\n" + "public class BaseClass { /* no fields */ }\n" + "public class MidClass extends BaseClass { /* no fields */ }\n" + "public class SubClass extends MidClass { int c = 7, d = 8, e = 9; }\n" + ); + // TODO: IntrinsicClassToString ? (e.g. point) } @@ -3197,3 +3304,25 @@ TEST_F(CBotUT, ClassTestPrivateMethod) CBotErrPrivate ); } + +TEST_F(CBotUT, ClassTestSaveInheritedMembers) +{ + auto publicProgram = ExecuteTest( + "public class TestClass { int a = 123; }\n" + "public class TestClass2 extends TestClass { int b = 456; }\n" + ); + // Note: Use --CBotUT_TestSaveState command line arg. + ExecuteTest( + "extern void TestSaveInheritedMembers()\n" + "{\n" + " TestClass2 t();\n" + " ASSERT(t.a == 123);\n" + " ASSERT(t.b == 456);\n" + " t.a = 789; t.b = 1011;\n" + " ASSERT(t.a == 789);\n" + " ASSERT(t.b == 1011);\n" + " ASSERT(789 == t.a);\n" + " ASSERT(1011 == t.b);\n" + "}\n" + ); +} diff --git a/test/unit/app/app_test.cpp b/test/unit/app/app_test.cpp index 7258f480..aad94e39 100644 --- a/test/unit/app/app_test.cpp +++ b/test/unit/app/app_test.cpp @@ -55,9 +55,9 @@ public: SDL_Quit(); } - Event CreateUpdateEvent() override + Event CreateUpdateEvent(SystemTimeStamp *timestamp) override { - return CApplication::CreateUpdateEvent(); + return CApplication::CreateUpdateEvent(timestamp); } }; @@ -157,7 +157,9 @@ void CApplicationUT::TestCreateUpdateEvent(long long relTimeExact, long long abs float relTime, float absTime, long long relTimeReal, long long absTimeReal) { - Event event = m_app->CreateUpdateEvent(); + SystemTimeStamp *now = CreateTimeStamp(); + GetCurrentTimeStamp(now); + Event event = m_app->CreateUpdateEvent(now); EXPECT_EQ(EVENT_FRAME, event.type); EXPECT_FLOAT_EQ(relTime, event.rTime); EXPECT_FLOAT_EQ(relTime, m_app->GetRelTime()); @@ -172,7 +174,11 @@ void CApplicationUT::TestCreateUpdateEvent(long long relTimeExact, long long abs TEST_F(CApplicationUT, UpdateEventTimeCalculation_SimulationSuspended) { m_app->SuspendSimulation(); - Event event = m_app->CreateUpdateEvent(); + + SystemTimeStamp *now = CreateTimeStamp(); + GetCurrentTimeStamp(now); + Event event = m_app->CreateUpdateEvent(now); + EXPECT_EQ(EVENT_NULL, event.type); } @@ -224,7 +230,9 @@ TEST_F(CApplicationUT, UpdateEventTimeCalculation_NegativeTimeOperation) NextInstant(-1111); - Event event = m_app->CreateUpdateEvent(); + SystemTimeStamp *now = CreateTimeStamp(); + GetCurrentTimeStamp(now); + Event event = m_app->CreateUpdateEvent(now); EXPECT_EQ(EVENT_NULL, event.type); } diff --git a/test/unit/common/colobot.ini b/test/unit/common/colobot.ini index c4d21623..7cc649b5 100644 --- a/test/unit/common/colobot.ini +++ b/test/unit/common/colobot.ini @@ -6,3 +6,8 @@ string_value=Hello world [test_int] int_value=42 + +[test_array] +string_array=AAA,Hello world,Gold Edition +int_array=2,3,1 +bool_array=1,0,1 diff --git a/test/unit/common/config_file_test.cpp b/test/unit/common/config_file_test.cpp index fcd8f036..762c3307 100644 --- a/test/unit/common/config_file_test.cpp +++ b/test/unit/common/config_file_test.cpp @@ -52,3 +52,25 @@ TEST_F(CConfigFileTest, ReadTest) ASSERT_TRUE(m_configFile.GetFloatProperty("test_float", "float_value", float_value)); ASSERT_FLOAT_EQ(1.5, float_value); } + +TEST_F(CConfigFileTest, ReadArrayTest) +{ + m_configFile.SetUseCurrentDirectory(true); + + ASSERT_TRUE(m_configFile.Init()); // load colobot.ini file + + std::vector expected_string_values = { "AAA", "Hello world", "Gold Edition" }; + std::vector string_values; + ASSERT_TRUE(m_configFile.GetArrayProperty("test_array", "string_array", string_values)); + ASSERT_EQ(expected_string_values, string_values); + + std::vector expected_int_values = { 2, 3, 1 }; + std::vector int_values; + ASSERT_TRUE(m_configFile.GetArrayProperty("test_array", "int_array", int_values)); + ASSERT_EQ(expected_int_values, int_values); + + std::vector expected_bool_values = { true, false, true }; + std::vector bool_values; + ASSERT_TRUE(m_configFile.GetArrayProperty("test_array", "bool_array", bool_values)); + ASSERT_EQ(expected_bool_values, bool_values); +} diff --git a/tools/blender-scripts.py b/tools/blender-scripts.py index d2273d67..b4104793 100644 --- a/tools/blender-scripts.py +++ b/tools/blender-scripts.py @@ -8,7 +8,7 @@ bl_info = { "name": "Colobot Model Format (.txt)", "author": "TerranovaTeam", - "version": (0, 0, 2), + "version": (0, 0, 3), "blender": (2, 6, 4), "location": "File > Export > Colobot (.txt)", "description": "Export Colobot Model Format (.txt)", @@ -35,7 +35,7 @@ FUZZY_TOLERANCE = 1e-5 class ColobotError(Exception): """Exception in I/O operations""" - def __init__(self, value): + def __init__(self, value, errcode=None): self.value = value def __str__(self): return repr(self.value) @@ -199,7 +199,8 @@ def write_colobot_model(filename, model): file.write('tex1 ' + t.mat.tex1 + '\n') file.write('tex2 ' + t.mat.tex2 + '\n') file.write('var_tex2 ' + ( 'Y' if t.mat.var_tex2 else 'N' + '\n' ) ) - file.write('lod_level ' + str(t.lod_level) + '\n') + if model.version == 1: + file.write('lod_level ' + str(t.lod_level) + '\n') file.write('state ' + str(t.mat.state) + '\n') file.write('\n') @@ -281,8 +282,8 @@ def read_colobot_model(filename): if (tokens[0] != 'version'): raise ColobotError("Invalid header", "version") model.version = int(tokens[1]) - if (model.version != 1): - raise ColobotError("Unknown model file version") + if (model.version != 1 and model.version != 2): + raise ColobotError("Unknown model file version "+str(model.version)) tokens, index = token_next_line(lines, index) if (tokens[0] != 'total_triangles'): @@ -329,10 +330,13 @@ def read_colobot_model(filename): raise ColobotError("Invalid triangle", "var_tex2") t.mat.var_tex2 = tokens[1] == 'Y' - tokens, index = token_next_line(lines, index) - if (tokens[0] != 'lod_level'): - raise ColobotError("Invalid triangle", "lod_level") - t.lod_level = int(tokens[1]) + if (model.version == 1): + tokens, index = token_next_line(lines, index) + if (tokens[0] != 'lod_level'): + raise ColobotError("Invalid triangle", "lod_level") + t.lod_level = int(tokens[1]) + else: + t.lod_level = 0 # constant tokens, index = token_next_line(lines, index) if (tokens[0] != 'state'): @@ -384,9 +388,9 @@ def append_obj_to_colobot_model(obj, model, scene, defaults): t.mat.specular[3] = mat.specular_alpha if (mat.texture_slots[0] != None): - t.tex1 = bpy.path.basename(mat.texture_slots[0].texture.image.filepath) + t.mat.tex1 = bpy.path.basename(mat.texture_slots[0].texture.image.filepath) if (mat.texture_slots[1] != None): - t.tex2 = bpy.path.basename(mat.texture_slots[1].texture.image.filepath) + t.mat.tex2 = bpy.path.basename(mat.texture_slots[1].texture.image.filepath) t.var_tex2 = mat.get('var_tex2', defaults['var_tex2']) t.state = mat.get('state', defaults['state']) @@ -589,7 +593,7 @@ class ExportColobotDialog(bpy.types.Operator): write_colobot_model(EXPORT_FILEPATH, model) except ColobotError as e: - self.report({'ERROR'}, e.args.join(": ")) + self.report({'ERROR'}, ": ".join(e.args)) return {'FINISHED'} self.report({'INFO'}, 'Export OK') @@ -665,7 +669,7 @@ class ImportColobotDialog(bpy.types.Operator): obj.layers = layers except ColobotError as e: - self.report({'ERROR'}, e.args.join(": ")) + self.report({'ERROR'}, ": ".join(e.args)) return {'FINISHED'} self.report({'INFO'}, 'Import OK')