├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── android ├── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── res │ ├── drawable-hdpi │ └── icon.png │ ├── drawable-ldpi │ └── icon.png │ ├── drawable-mdpi │ └── icon.png │ ├── drawable-xhdpi │ └── icon.png │ ├── drawable-xxhdpi │ └── icon.png │ ├── drawable-xxxhdpi │ └── icon.png │ └── values │ └── libs.xml ├── game_core ├── about_dialog.cpp ├── about_dialog.h ├── constants.h ├── game_object.cpp ├── game_object.h ├── mainwindow.cpp ├── mainwindow.h ├── map.cpp ├── map.h ├── new_game_dialog.cpp ├── new_game_dialog.h ├── settings_dialog.cpp └── settings_dialog.h ├── main.cpp ├── movable_objects ├── boom.cpp ├── boom.h ├── bot.cpp ├── bot.h ├── clever_bot.cpp ├── clever_bot.h ├── improved_bot.cpp ├── improved_bot.h ├── movable.cpp ├── movable.h ├── rocket.cpp ├── rocket.h ├── tank.cpp └── tank.h ├── resources ├── app_icon.ico ├── game_objects_data │ ├── rockets.json │ └── tanks.json ├── maps │ ├── map1.json │ ├── map2.json │ ├── map3.json │ └── map4.json ├── resources.qrc ├── rules │ ├── about.png │ ├── new_game.png │ ├── pause.png │ ├── rules.html │ ├── screenshot1.png │ ├── screenshot2.png │ ├── settings.png │ ├── style.css │ └── timer.png ├── sounds │ ├── backgroundmusic1.mp3 │ ├── backgroundmusic2.mp3 │ ├── backgroundmusic3.mp3 │ ├── backgroundmusic4.mp3 │ └── boom.mp3 ├── textures │ ├── 0.png │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── boom.png │ ├── bot.png │ ├── charge.png │ ├── clever_bot.png │ ├── improved_bot.png │ ├── log.png │ ├── medicalkit.png │ ├── portal.png │ ├── rocket.png │ └── tank.png └── translations │ ├── tanks_be_BY.qm │ ├── tanks_be_BY.ts │ ├── tanks_en_US.qm │ ├── tanks_en_US.ts │ ├── tanks_ru_RU.qm │ └── tanks_ru_RU.ts ├── static_objects ├── portal.cpp ├── portal.h ├── static_object.cpp └── static_object.h └── tanks.pro /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | check: 7 | runs-on: ubuntu-20.04 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Update packages list 13 | run: sudo apt update 14 | 15 | - name: Install packages 16 | run: sudo apt install cppcheck python3-pip -y 17 | 18 | - name: Install cpplint 19 | run: pip3 install cpplint 20 | 21 | - name: Run cppcheck (unused functions) 22 | working-directory: ${{runner.workspace}}/${{github.event.repository.name}} 23 | shell: bash 24 | run: cppcheck --enable=unusedFunction --language=c++ $(find -name "*.h" -o -name "*.cpp") 25 | continue-on-error: true 26 | 27 | - name: Run cppcheck (warnings and preformance issues) 28 | working-directory: ${{runner.workspace}}/${{github.event.repository.name}} 29 | shell: bash 30 | run: cppcheck --enable=warning,performance --language=c++ $(find -name "*.h" -o -name "*.cpp") 31 | continue-on-error: true 32 | 33 | - name: Run cpplint 34 | working-directory: ${{runner.workspace}}/${{github.event.repository.name}} 35 | shell: bash 36 | run: $HOME/.local/bin/cpplint --filter=-legal/copyright,-build/include_subdir,-build/c++11,-build/include_order $(find -name "*.h" -o -name "*.cpp") 37 | 38 | build: 39 | runs-on: ubuntu-20.04 40 | 41 | steps: 42 | - uses: actions/checkout@v2 43 | 44 | - name: Add PPA 45 | run: sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-focal -y 46 | 47 | - name: Update packages list 48 | run: sudo apt update 49 | 50 | - name: Install packages 51 | run: sudo apt install libglu1-mesa-dev libgl1-mesa-dev qt515base qt515multimedia -y 52 | 53 | - name: Configure QMake 54 | run: sudo /opt/qt515/bin/qt515-env.sh 55 | 56 | - name: Create Makefile 57 | working-directory: ${{runner.workspace}}/${{github.event.repository.name}} 58 | run: cmake -DCMAKE_PREFIX_PATH=/opt/qt515/lib/cmake -DCMAKE_BUILD_TYPE=Release ./CMakeLists.txt || /opt/qt515/bin/qmake -makefile 59 | 60 | - name: Build 61 | working-directory: ${{runner.workspace}}/${{github.event.repository.name}} 62 | run: make 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/tanks.pro.user* 2 | .idea/ 3 | cmake-build-*/ 4 | .gradle/ 5 | signing_config.gradle 6 | tanks.keystore 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(Tanks) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | set(CMAKE_AUTOMOC ON) # Enable Qt MOC compiler 7 | set(CMAKE_AUTORCC ON) # Enable Qt resources compiler 8 | set(CMAKE_AUTOUIC ON) # Enable Qt UI compiler 9 | 10 | # You can set CMake prefix path here or pass it to CMake executable. 11 | #set(CMAKE_PREFIX_PATH "~/Qt/5.15.2/gcc_64/lib/cmake") 12 | #set(CMAKE_PREFIX_PATH "C:/Qt/5.15.2/mingw81_64/lib/cmake") 13 | 14 | if (NOT CMAKE_PREFIX_PATH) 15 | message(WARNING "CMAKE_PREFIX_PATH is not defined, you may need to set it " 16 | "(-DCMAKE_PREFIX_PATH=\"path/to/Qt/lib/cmake\")") 17 | endif () 18 | 19 | set(QT_VERSION 5) 20 | set(REQUIRED_LIBS Core Widgets Multimedia) 21 | set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Widgets Qt5::Multimedia) 22 | find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED) 23 | 24 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 25 | set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wall -O3") 26 | 27 | set(SOURCES main.cpp 28 | game_core/mainwindow.cpp game_core/about_dialog.cpp 29 | game_core/new_game_dialog.cpp game_core/settings_dialog.cpp 30 | game_core/map.cpp game_core/game_object.cpp 31 | movable_objects/boom.cpp movable_objects/bot.cpp 32 | movable_objects/clever_bot.cpp movable_objects/improved_bot.cpp 33 | movable_objects/movable.cpp movable_objects/rocket.cpp 34 | movable_objects/tank.cpp 35 | static_objects/static_object.cpp static_objects/portal.cpp) 36 | 37 | qt5_add_big_resources(RESOURCES resources/resources.qrc) 38 | add_executable(${PROJECT_NAME} ${SOURCES} ${RESOURCES}) 39 | target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED}) 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Tanks 2 | 3 | Tanks - 2D-игра, написанная на C++ с использованием 4 | [Qt framework (5.15.2+)](https://www.qt.io). Игра предназначена для ПК, но может 5 | работать и на Android-смартфонах. 6 | 7 | Суть игры: на ограниченной карте со стенами, различными типами покрытия, 8 | препятствиями, бонусами и телепортами уничтожить всех ботов. Доступны четыре 9 | карты, три вида ботов (от почти бездействующих до ботов, осуществляющих поиск 10 | игрока), три танка игрока с разными характеристиками, а также различные виды 11 | снарядов. Более подробная информация об объектах игры расположена в секции 12 | "Информация" внутри программы. 13 | 14 | ![Tanks](resources/rules/screenshot1.png) 15 | ![Tanks](resources/rules/screenshot2.png) 16 | 17 | Игра является учебным проектом студентов 4 группы 1 курса факультета 18 | прикладной математики и информатики БГУ (2 семестр 2018-2019): 19 | * Гармаза Александра ([@sashhrmz](https://github.com/sashhrmz)) 20 | * Неверо Андрей ([@anevero](https://github.com/anevero)) 21 | * Тарайкович Алеся ([@Lessyless](https://github.com/Lessyless)) 22 | 23 | Основная стадия разработки завершена в мае 2019 года. После мая 2019 года 24 | несколько раз выпускались небольшие обновления, включающие в себя рефакторинг 25 | кода и обеспечение совместимости проекта с актуальными версиями Qt. 26 | 27 | В программе используется музыка 28 | [David Fesliyan](https://www.fesliyanstudios.com/) и 29 | [spinningmerkaba](http://dig.ccmixter.org/people/jlbrock44). 30 | 31 | ## Установка игры 32 | 33 | В секции [releases](https://github.com/anevero/tanks/releases) 34 | можно найти установщики игры для 32- и 64-разрядной Windows, 64-разрядной 35 | Ubuntu, а также apk-файл для установки игры на Android-смартфон. 36 | 37 | Установщики для Windows и Linux были созданы с использованием утилиты 38 | [CQtDeployer](https://github.com/QuasarApp/CQtDeployer). Для удаления программы 39 | после ее установки на этих операционных системах достаточно запустить файл 40 | *TanksTool* из папки, куда была установлена игра. 41 | 42 | При установке игры на Android вы можете получить предупреждение о том, что 43 | автор приложения неизвестен. Это ожидаемое поведение, можете не обращать на это 44 | внимания. 45 | 46 | Если вы хотите запустить игру на другой ОС, склонируйте репозиторий и 47 | скомпилируйте проект самостоятельно (инструкции можно найти ниже). 48 | 49 | ## Continuous integration 50 | 51 | [![GitHub Actions Status](https://github.com/anevero/tanks/workflows/ci/badge.svg?branch=master)](https://github.com/anevero/tanks/actions) 52 | 53 | После каждого нового коммита запускается автоматическая сборка и статический 54 | анализ кода проекта. Обычно это занимает до пяти минут. Ссылки на результат и 55 | логи процесса доступны на [странице GitHub Actions](https://github.com/anevero/tanks/actions), 56 | а также в статусе коммита в списке коммитов. 57 | 58 | Замечания `cppcheck` (на стадии *check*) не влияют на статус CI (из-за большого 59 | количества ложноположительных срабатываний), но рекомендуется все же обращать 60 | на них внимание. Предупреждения `cpplint` на стадии *check* и ошибки при сборке 61 | на стадии *build* влияют на статус CI, и игнорировать их нельзя. 62 | 63 | ## Сборка проекта с помощью QMake 64 | 65 | Для того, чтобы собрать проект с помощью QMake в составе Qt Creator, 66 | достаточно склонировать репозиторий и запустить файл *tanks.pro*. 67 | 68 | Сборку проекта под Linux рекомендуется осуществлять с помощью **GCC64**, под 69 | Windows - с помощью **MinGW64**. 70 | 71 | Сборка проекта под Android - несколько более сложная процедура. Немного 72 | подробнее об этом можно почитать в 73 | [документации](https://doc.qt.io/qt-5/android-getting-started.html). 74 | Обратите внимание на то, что в проекте используются не совсем стандартные 75 | манифест и скрипты Gradle. В последних можно найти информацию о том, какие 76 | версии некоторых компонентов необходимы для сборки проекта. 77 | 78 | Для редактирования переводов приложения следует использовать входящие в состав 79 | Qt утилиты [lupdate](https://doc.qt.io/qt-5/linguist-manager.html#using-lupdate) 80 | и [lrelease](https://doc.qt.io/qt-5/linguist-manager.html#using-lrelease), а 81 | также [Qt Linguist](https://doc.qt.io/qt-5/qtlinguist-index.html). 82 | 83 | ## Сборка проекта с помощью CMake 84 | 85 | В некоторых случаях с проектом удобнее работать, например, в среде разработки 86 | CLion, которая не поддерживает QMake. В этом случае проект нужно собирать с 87 | помощью CMake. Соответствующий скрипт также доступен в репозитории. 88 | 89 | Для корректной работы скрипта необходимо указать путь к соответствующим 90 | библиотекам Qt на своем компьютере (установить соответствующее значение 91 | переменной `CMAKE_PREFIX_PATH`). Сделать это можно как минимум двумя способами: 92 | 1. В настройках CMake-конфигурации в CLion добавить флаг `-DCMAKE_PREFIX_PATH=path`. 93 | 2. В CMake-скрипте добавить строку `set(CMAKE_PREFIX_PATH path)`. В 94 | CMake-скрипте в репозитории такая строка уже есть, вам достаточно 95 | раскомментировать ее и указать путь к Qt на своем компьютере. 96 | 97 | После этого на Linux проект можно импортировать в CLion для дальнейшей работы. 98 | Под Windows необходимы дополнительные действия. Нужно указать в *Path* 99 | путь к компилятору в составе Qt, а также путь к библиотекам Qt. Например: 100 | 101 | `C:\Qt\Tools\mingw810_64\bin` 102 | 103 | `C:\Qt\5.15.2\mingw81_64\bin` 104 | 105 | Настоятельно рекомендуется переместить эти строки в *Path* как можно 106 | выше. Дело в том, что Qt относительно часто используется в стороннем 107 | ПО, ссылки на которое могут присутствовать в *Path*. При компиляции выбираются 108 | первые найденные по адресам в *Path* библиотеки. В случае, если выбраны 109 | принадлежащие сторонним программам библиотеки, корректно собрать и запустить 110 | проект может и не получиться из-за различий в их версиях. 111 | 112 | После внесения изменений в *Path* необходимо перезагрузить Windows. Далее 113 | рекомендуется выбрать в CLion компилятор, входящий в состав Qt (указать путь к 114 | нему в **Settings - Toolchains**). После этого можно работать с проектом. 115 | 116 | ### Ограничения CLion 117 | 118 | Учитывайте, что при использовании CLion вам придется смириться со следующими 119 | ограничениями: 120 | * По умолчанию приложение нельзя будет скомпилировать, подписать и запустить 121 | под Android. Возможно, существуют решения этой проблемы. 122 | * Для редактирования и генерации файлов перевода все равно нужны утилиты 123 | Qt. 124 | 125 | ## Известные проблемы при сборке проекта 126 | 127 | При сборке проекта под Windows рекомендуется использовать актуальную 128 | версию Qt (как минимум 5.13.2). Если собирать его с помощью более старой 129 | версии Qt (в частности, 5.12.4 - 5.13.1), игра может работать некорректно. 130 | Это связано с багом в библиотеке Qt Multimedia, который был исправлен 131 | в Qt 5.13.2. 132 | 133 | При сборке проекта под Android необходимо использовать самую актуальную 134 | версию Qt (как минимум 5.15.2). Если собирать его с помощью более старой 135 | версии Qt, то: 136 | * Проект может не скомпилироваться из-за изменения структуры манифеста в 137 | Qt 5.14-5.15. 138 | * Игра может работать некорректно из-за бага в реализации класса QToolTip в 139 | Qt 5.14-5.15, который был исправлен в Qt 5.15.2. 140 | -------------------------------------------------------------------------------- /android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 9 | 10 | 12 | 13 | 14 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 57 | 58 | 59 | 60 | 62 | 64 | 66 | 68 | 69 | 71 | 73 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:4.0.2' 9 | } 10 | } 11 | 12 | repositories { 13 | google() 14 | jcenter() 15 | } 16 | 17 | apply plugin: 'com.android.application' 18 | apply from: './signing_config.gradle' 19 | 20 | dependencies { 21 | implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) 22 | } 23 | 24 | android { 25 | /******************************************************* 26 | * The following variables: 27 | * - androidBuildToolsVersion, 28 | * - androidCompileSdkVersion 29 | * - qt5AndroidDir - holds the path to qt android files 30 | * needed to build any Qt application 31 | * on Android. 32 | * 33 | * are defined in gradle.properties file. This file is 34 | * updated by QtCreator and androiddeployqt tools. 35 | * Changing them manually might break the compilation! 36 | *******************************************************/ 37 | 38 | compileSdkVersion androidCompileSdkVersion.toInteger() 39 | buildToolsVersion androidBuildToolsVersion 40 | 41 | signingConfigs { 42 | release { 43 | storeFile file('./tanks.keystore') 44 | storePassword project.ext.storePassword 45 | keyAlias project.ext.keyAlias 46 | keyPassword project.ext.keyPassword 47 | } 48 | } 49 | 50 | sourceSets { 51 | main { 52 | manifest.srcFile 'AndroidManifest.xml' 53 | java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] 54 | aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] 55 | res.srcDirs = [qt5AndroidDir + '/res', 'res'] 56 | resources.srcDirs = ['resources'] 57 | renderscript.srcDirs = ['src'] 58 | assets.srcDirs = ['assets'] 59 | jniLibs.srcDirs = ['libs'] 60 | } 61 | } 62 | 63 | tasks.withType(JavaCompile) { 64 | options.incremental = true 65 | } 66 | 67 | defaultConfig { 68 | resConfigs 'en' 69 | minSdkVersion = qtMinSdkVersion 70 | targetSdkVersion = qtTargetSdkVersion 71 | 72 | applicationId 'com.github.tanks' 73 | versionName '0.8.6' 74 | versionCode 10 75 | // signingConfig signingConfigs.release 76 | } 77 | 78 | compileOptions { 79 | sourceCompatibility JavaVersion.VERSION_1_8 80 | targetCompatibility JavaVersion.VERSION_1_8 81 | } 82 | 83 | lintOptions { 84 | abortOnError false 85 | } 86 | 87 | aaptOptions { 88 | noCompress 'rcc' 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2048m 2 | org.gradle.caching=true 3 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /android/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/android/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /android/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/android/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /android/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/android/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /android/res/drawable-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/android/res/drawable-xhdpi/icon.png -------------------------------------------------------------------------------- /android/res/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/android/res/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /android/res/drawable-xxxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/android/res/drawable-xxxhdpi/icon.png -------------------------------------------------------------------------------- /android/res/values/libs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://download.qt.io/ministro/android/qt5/qt-5.14 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /game_core/about_dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "about_dialog.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | AboutDialog::AboutDialog(QWidget* parent) 8 | : QDialog(parent), 9 | layout_(new QVBoxLayout(this)), 10 | html_widget_(new QTextBrowser(this)), 11 | buttons_(new QDialogButtonBox( 12 | QDialogButtonBox::Ok, Qt::Horizontal, this)) { 13 | html_widget_->setSource(QUrl("qrc:/rules/rules.html")); 14 | html_widget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 15 | html_widget_->setOpenExternalLinks(true); 16 | 17 | #ifdef Q_OS_ANDROID 18 | html_widget_->setTextInteractionFlags( 19 | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); 20 | QScroller::grabGesture(html_widget_, QScroller::TouchGesture); 21 | #endif 22 | 23 | layout_->addWidget(html_widget_); 24 | layout_->addWidget(buttons_); 25 | connect(buttons_, &QDialogButtonBox::accepted, this, &AboutDialog::accept); 26 | connect(buttons_, &QDialogButtonBox::rejected, this, &AboutDialog::reject); 27 | } 28 | 29 | void AboutDialog::accept() { 30 | ResetCursor(); 31 | return QDialog::accept(); 32 | } 33 | 34 | void AboutDialog::reject() { 35 | ResetCursor(); 36 | return QDialog::reject(); 37 | } 38 | 39 | void AboutDialog::ResetCursor() { 40 | QTextCursor cursor = html_widget_->textCursor(); 41 | cursor.setPosition(0); 42 | html_widget_->setTextCursor(cursor); 43 | } 44 | -------------------------------------------------------------------------------- /game_core/about_dialog.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_CORE_ABOUT_DIALOG_H_ 2 | #define GAME_CORE_ABOUT_DIALOG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class AboutDialog : public QDialog { 10 | Q_OBJECT 11 | 12 | public: 13 | explicit AboutDialog(QWidget* parent = nullptr); 14 | ~AboutDialog() override = default; 15 | 16 | void accept() override; 17 | void reject() override; 18 | 19 | private: 20 | void ResetCursor(); 21 | 22 | QVBoxLayout* layout_; 23 | QTextBrowser* html_widget_; 24 | QDialogButtonBox* buttons_; 25 | }; 26 | 27 | #endif // GAME_CORE_ABOUT_DIALOG_H_ 28 | -------------------------------------------------------------------------------- /game_core/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_CORE_CONSTANTS_H_ 2 | #define GAME_CORE_CONSTANTS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace constants { 9 | 10 | // Main application info. 11 | const QString kOrganizationName = "Tanks"; 12 | const QString kApplicationName = "Tanks"; 13 | const int kApplicationRestartCode = 438254263; 14 | 15 | // New game dialog constants. 16 | const int kMapsNumber = 4; 17 | const int kTanksNumber = 3; 18 | const int kDifficultyLevelsNumber = 3; 19 | 20 | // Settings dialog constants. 21 | const QString kVirtualKeysEnabledKey = "virtual_keys_enabled"; 22 | const QString kMobileVirtualKeysStyleEnabledKey = 23 | "mobile_virtual_keys_style_enabled"; 24 | const QString kChargeLineEnabledKey = "charge_line_enabled"; 25 | const QString kMusicEnabledKey = "music_enabled"; 26 | const QString kCurrentFpsOptionKey = "current_fps_option"; 27 | const QString kLanguageKey = "language"; 28 | 29 | // Available settings. 30 | const std::vector kFpsOptions = {40, 50, 60, 90, 120, 240}; 31 | const std::vector kLanguages = {"en_US", "ru_RU", "be_BY"}; 32 | 33 | // Game constants; 34 | const int kChargesNumber = 3; 35 | const int kVirtualKeysNumber = 5; 36 | const int kMinutesPerRound = 10; 37 | 38 | const double kOpacityLevel = 0.6; 39 | const int kMedicalKitSpawnPeriod = 20000; 40 | const int kChargeSpawnPeriod = 15000; 41 | const int kBoomCharge = 25; 42 | 43 | } // namespace constants 44 | 45 | #endif // GAME_CORE_CONSTANTS_H_ 46 | -------------------------------------------------------------------------------- /game_core/game_object.cpp: -------------------------------------------------------------------------------- 1 | #include "game_object.h" 2 | 3 | GameObject::GameObject( 4 | std::shared_ptr map, const QString& texture_path, 5 | Coordinates coordinates) 6 | : map_(std::move(map)), cell_(coordinates), width_(0), height_(0) { 7 | LoadImage(texture_path); 8 | } 9 | 10 | void GameObject::LoadImage(const QString& path) { 11 | image_.load(path); 12 | scaled_pixmap_ = QPixmap::fromImage(image_); 13 | } 14 | 15 | Coordinates GameObject::GetCoordinates() const { 16 | return cell_; 17 | } 18 | 19 | Coordinates GameObject::GetUpperLeftCellCoordinates() const { 20 | return upper_left_cell_coordinates_; 21 | } 22 | 23 | int GameObject::GetWidth() const { 24 | return width_; 25 | } 26 | 27 | int GameObject::GetHeight() const { 28 | return height_; 29 | } 30 | 31 | void GameObject::RescaleImage() { 32 | if (scaled_pixmap_.width() == width_ && scaled_pixmap_.height() == height_) { 33 | return; 34 | } 35 | scaled_pixmap_ = QPixmap::fromImage( 36 | image_.scaled(width_, height_, Qt::KeepAspectRatio)); 37 | } 38 | -------------------------------------------------------------------------------- /game_core/game_object.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_CORE_GAME_OBJECT_H_ 2 | #define GAME_CORE_GAME_OBJECT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "map.h" 13 | 14 | class GameObject { 15 | public: 16 | GameObject(std::shared_ptr map, const QString& texture_path, 17 | Coordinates coordinates); 18 | virtual ~GameObject() = default; 19 | 20 | void LoadImage(const QString& path); 21 | virtual void Draw(QPainter* painter) = 0; 22 | 23 | Coordinates GetCoordinates() const; 24 | Coordinates GetUpperLeftCellCoordinates() const; 25 | 26 | int GetWidth() const; 27 | int GetHeight() const; 28 | 29 | protected: 30 | void RescaleImage(); 31 | 32 | protected: 33 | const std::shared_ptr map_; 34 | 35 | Coordinates cell_; 36 | Coordinates upper_left_cell_coordinates_; 37 | 38 | int width_; 39 | int height_; 40 | 41 | QImage image_; 42 | QPixmap scaled_pixmap_; 43 | }; 44 | 45 | #endif // GAME_CORE_GAME_OBJECT_H_ 46 | -------------------------------------------------------------------------------- /game_core/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_CORE_MAINWINDOW_H_ 2 | #define GAME_CORE_MAINWINDOW_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "game_core/about_dialog.h" 35 | #include "game_core/constants.h" 36 | #include "game_core/map.h" 37 | #include "game_core/new_game_dialog.h" 38 | #include "game_core/settings_dialog.h" 39 | #include "static_objects/static_object.h" 40 | #include "static_objects/portal.h" 41 | #include "movable_objects/boom.h" 42 | #include "movable_objects/bot.h" 43 | #include "movable_objects/clever_bot.h" 44 | #include "movable_objects/improved_bot.h" 45 | #include "movable_objects/rocket.h" 46 | #include "movable_objects/tank.h" 47 | 48 | class MainWindow : public QMainWindow { 49 | Q_OBJECT 50 | 51 | public: 52 | explicit MainWindow(QWidget* parent = nullptr); 53 | ~MainWindow() override; 54 | 55 | private: 56 | [[maybe_unused]] void mouseReleaseEvent(QMouseEvent* event) override; 57 | [[maybe_unused]] void keyReleaseEvent(QKeyEvent* event) override; 58 | [[maybe_unused]] void paintEvent(QPaintEvent*) override; 59 | [[maybe_unused]] void resizeEvent(QResizeEvent*) override; 60 | [[maybe_unused]] void timerEvent(QTimerEvent*) override; 61 | 62 | private: 63 | void PauseOrContinue(); 64 | void ExecNewGameDialog(); 65 | void ExecSettingsDialog(); 66 | void ExecAboutDialog(); 67 | 68 | private: 69 | void LoadApplicationSettings(); 70 | void LoadTanksTypesInfo(); 71 | void LoadRocketsTypesInfo(); 72 | 73 | void SetVirtualKeysEnabled(bool enabled); 74 | void SetMobileVirtualKeysStyleEnabled(bool enabled); 75 | void SetMusicEnabled(bool enabled); 76 | void SetFpsOption(int index, bool start_timer = false); 77 | 78 | private: 79 | // Clears all the data connected with the current round and initializes 80 | // objects with values necessary to start the new round. 81 | void LoadRoundInfo(); 82 | // Resets the state of all Qt objects to start the new round, calls 83 | // LoadRoundInfo for resetting state of game objects, and then starts a new 84 | // round. 85 | void StartRound(); 86 | // Runs game over dialog with the appropriate content (i.e 'you win' or 87 | // 'you lose'). 88 | void FinishRound(bool win); 89 | 90 | private: 91 | // Updates info about size of indents in the current window. 92 | void UpdateIndents(); 93 | // Changes buttons coordinates in the window according to the window size. 94 | void RedrawButtons(); 95 | // Scales buttons fonts (it is not scaled automatically). 96 | void SetButtonsFontPixelSize(int pixel_size); 97 | // Updates charge buttons color according to the state of charge of the 98 | // player's tank. 99 | void RedrawChargeButtons(); 100 | // Updates screen timer according to the current time. 101 | void UpdateScreenTimer(); 102 | 103 | private: 104 | // Checks if rockets are interacting with the tanks at the moment and 105 | // destroys rockets / reduces tanks' health. 106 | void FindInteractingObjects(); 107 | // Checks if some objects are 'dead', i.e. searches for the tanks with 0 108 | // health, rockets, which need to be destroyed. Erases such objects. 109 | // If player's tank is 'dead', calls FinishRound(). 110 | void CheckDeadObjects(); 111 | // Creates a rocket which begins to move from the given tank. Rocket's 112 | // type is defined by the tank current type of charge. Rocket's geometry on 113 | // the map is defined by tank's geometry. 114 | void ShootRocket(const std::shared_ptr& tank); 115 | // Creates and initializes explosion. Its geometry is defined by the 116 | // given object. 117 | void MakeBoom(const std::shared_ptr& object); 118 | // Generates bonus of the given type. Erases old bonuses of such type from 119 | // the map. 120 | template 121 | void RandomBonus(); 122 | // Switches charge button on the screen and type of charge of the player's 123 | // tank. 124 | void SwitchCharge(int type); 125 | 126 | private: 127 | static QJsonObject GetJsonObjectFromFile(const QString& filepath); 128 | 129 | private: 130 | int current_map_number_ = 0; 131 | int current_tank_number_ = 0; 132 | int current_difficulty_level_ = 0; 133 | 134 | std::vector tanks_types_ = {}; 135 | std::vector rockets_types_ = {}; 136 | 137 | private: 138 | std::shared_ptr map_; 139 | // List of all the tanks (including player's tank and all the types of bots). 140 | // The player's tank, if exists, is always the first item in the list. 141 | std::list> tanks_ = {}; 142 | std::list> rockets_ = {}; 143 | std::list> booms_ = {}; 144 | std::vector>> 145 | obstacles_and_bonuses_ = {}; 146 | // List of 'copies' of tanks. Every item is just a pointer to the real tank 147 | // and coordinates of the copy. It's used while a tank is moving through the 148 | // portal (while it must be shown in two places at the same time). 149 | std::list, Coordinates>> 150 | objects_copies_ = {}; 151 | 152 | int timer_id_ = 0; 153 | int time_since_last_medical_kit_ = 0; 154 | int time_since_last_charge_ = 0; 155 | int timer_duration_; 156 | bool paused_ = false; 157 | 158 | private: 159 | bool virtual_keys_enabled_; 160 | bool mobile_virtual_keys_style_enabled_; 161 | bool charge_line_enabled_; 162 | bool music_enabled_; 163 | int current_fps_option_; 164 | 165 | private: 166 | QLCDNumber* screen_timer_; 167 | QPalette standard_lcdnumber_palette_; 168 | QPalette red_lcdnumber_palette_; 169 | int screen_timer_ms_ = 0; 170 | int screen_timer_sec_ = 0; 171 | int screen_timer_min_ = 0; 172 | 173 | NewGameDialog* new_game_dialog_; 174 | SettingsDialog* settings_dialog_; 175 | AboutDialog* about_dialog_; 176 | 177 | QVBoxLayout* main_buttons_layout_; 178 | QPushButton* new_game_button_; 179 | QPushButton* pause_continue_button_; 180 | QPushButton* settings_button_; 181 | QPushButton* about_button_; 182 | 183 | QHBoxLayout* charge_buttons_layout_; 184 | std::vector charge_buttons_; 185 | QPalette standard_button_palette_; 186 | const std::vector charge_colors_ = 187 | {Qt::red, Qt::yellow, Qt::green}; 188 | std::vector charge_palettes_; 189 | 190 | QGridLayout* virtual_buttons_layout_; 191 | std::vector virtual_keys_buttons_; 192 | 193 | const std::vector virtual_keys_encodings_ = 194 | {Qt::Key_Q, Qt::Key_W, Qt::Key_A, Qt::Key_S, Qt::Key_D}; 195 | const int number_of_virtual_keys_in_first_row_ = 2; 196 | 197 | QVBoxLayout* mobile_virtual_buttons_layout_left_; 198 | QHBoxLayout* mobile_virtual_buttons_layout_right_; 199 | 200 | QMediaPlayer* music_player_; 201 | QMediaPlaylist* music_playlist_; 202 | 203 | int view_width_; 204 | int view_height_; 205 | int width_indent_; 206 | int height_indent_; 207 | 208 | static std::mt19937 random_generator_; 209 | }; 210 | 211 | #endif // GAME_CORE_MAINWINDOW_H_ 212 | -------------------------------------------------------------------------------- /game_core/map.cpp: -------------------------------------------------------------------------------- 1 | #include "map.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | bool Coordinates::operator==(Coordinates coordinates) { 11 | return x == coordinates.x && y == coordinates.y; 12 | } 13 | 14 | bool Coordinates::operator!=(Coordinates coordinates) { 15 | return !(*this == coordinates); 16 | } 17 | 18 | Map::Map(int map_number) : current_width_(0), current_height_(0), 19 | current_cell_width_(0), current_cell_height_(0) { 20 | QFile input_file(":/maps/map" + QString::number(map_number + 1) + ".json"); 21 | input_file.open(QIODevice::ReadOnly); 22 | QString text = input_file.readAll(); 23 | input_file.close(); 24 | QJsonDocument json_document(QJsonDocument::fromJson(text.toUtf8())); 25 | QJsonObject json = json_document.object(); 26 | 27 | QJsonObject map = json["map"].toObject(); 28 | int map_width_in_cells = map["width"].toInt(); 29 | int map_height_in_cells = map["height"].toInt(); 30 | map_.resize(map_height_in_cells); 31 | 32 | QJsonArray array2D = map["map"].toArray(); 33 | QJsonArray array; 34 | for (int i = 0; i < map_height_in_cells; ++i) { 35 | array = array2D[i].toArray(); 36 | for (int j = 0; j < map_width_in_cells; ++j) { 37 | map_[i].push_back(static_cast(array[j].toInt())); 38 | } 39 | } 40 | 41 | walls_precalculation_.resize(map_height_in_cells); 42 | WallsPrecalculation(); 43 | 44 | QJsonObject player_tank = json["player_tank"].toObject(); 45 | tank_initial_cell_ = 46 | {player_tank["initial_cell_x"].toInt(), 47 | player_tank["initial_cell_y"].toInt()}; 48 | tank_start_direction_ = 49 | player_tank["initial_direction"].toString().toStdString(); 50 | 51 | for (int i = 0; i < static_cast(CellType::Last); ++i) { 52 | images_.emplace_back(":/textures/" + QString::number(i) + ".png"); 53 | scaled_pixmaps_.emplace_back(":/textures/" + QString::number(i) + ".png"); 54 | } 55 | } 56 | 57 | void Map::UpdateCoordinates(Coordinates upper_left_cell_coordinates, 58 | int width, int height) { 59 | upper_left_cell_coordinates_ = upper_left_cell_coordinates; 60 | current_cell_width_ = width / map_.size(); 61 | current_cell_height_ = height / map_[0].size(); 62 | current_width_ = current_cell_width_ * map_.size(); 63 | current_height_ = current_cell_height_ * map_[0].size(); 64 | RescaleImages(); 65 | FormMapImage(); 66 | } 67 | 68 | void Map::DrawMap(QPainter* painter) { 69 | painter->drawPixmap(upper_left_cell_coordinates_.x, 70 | upper_left_cell_coordinates_.y, 71 | map_scaled_pixmap_); 72 | } 73 | 74 | CellType Map::GetField(Coordinates cell) const { 75 | return map_[cell.x][cell.y]; 76 | } 77 | 78 | int Map::GetWallsPrecalculation(int cell_x, int cell_y) const { 79 | return walls_precalculation_[cell_x][cell_y]; 80 | } 81 | 82 | int Map::GetNumberOfCellsHorizontally() const { 83 | return map_[0].size(); 84 | } 85 | 86 | int Map::GetNumberOfCellsVertically() const { 87 | return map_.size(); 88 | } 89 | 90 | Coordinates Map::GetUpperLeftCellCoordinates() const { 91 | return upper_left_cell_coordinates_; 92 | } 93 | 94 | int Map::GetWidth() const { 95 | return current_width_; 96 | } 97 | 98 | int Map::GetHeight() const { 99 | return current_height_; 100 | } 101 | 102 | int Map::GetCellWidth() const { 103 | return current_cell_width_; 104 | } 105 | 106 | int Map::GetCellHeight() const { 107 | return current_cell_height_; 108 | } 109 | 110 | Coordinates Map::GetTankInitialCoordinates() const { 111 | return tank_initial_cell_; 112 | } 113 | 114 | std::string Map::GetTankStartDirection() const { 115 | return tank_start_direction_; 116 | } 117 | 118 | void Map::RescaleImages() { 119 | if (scaled_pixmaps_[0].width() == current_cell_width_ + 2 && 120 | scaled_pixmaps_[0].height() == current_cell_height_ + 2) { 121 | return; 122 | } 123 | 124 | for (int i = 0; i < static_cast(CellType::Last); ++i) { 125 | scaled_pixmaps_[i] = QPixmap::fromImage(images_[i].scaled( 126 | current_cell_width_ + 2, 127 | current_cell_height_ + 2, 128 | Qt::KeepAspectRatio)); 129 | } 130 | } 131 | 132 | void Map::FormMapImage() { 133 | if (map_scaled_pixmap_.width() == current_width_ && 134 | map_scaled_pixmap_.height() == current_height_) { 135 | return; 136 | } 137 | 138 | QImage temp_image = 139 | QImage(current_width_, current_height_, QImage::Format_ARGB32); 140 | QPainter p; 141 | p.begin(&temp_image); 142 | 143 | int width = map_.size(); 144 | int height = map_[0].size(); 145 | 146 | for (int i = 0; i < width; ++i) { 147 | for (int j = 0; j < height; ++j) { 148 | p.drawPixmap(i * current_cell_width_, j * current_cell_height_, 149 | scaled_pixmaps_[static_cast(map_[i][j])]); 150 | } 151 | } 152 | p.end(); 153 | 154 | map_scaled_pixmap_ = QPixmap::fromImage(std::move(temp_image)); 155 | } 156 | 157 | void Map::WallsPrecalculation() { 158 | int height = GetNumberOfCellsVertically(); 159 | int width = GetNumberOfCellsHorizontally(); 160 | for (int i = 0; i < height - 1; ++i) { 161 | for (int j = 0; j < width - 1; ++j) { 162 | walls_precalculation_[j].push_back(0); 163 | if (i > 0 && j > 0) { 164 | walls_precalculation_[j][i] = walls_precalculation_[j - 1][i] + 165 | walls_precalculation_[j][i - 1] 166 | - walls_precalculation_[j - 1][i - 1]; 167 | if (map_[j][i] == CellType::Wall) { 168 | walls_precalculation_[j][i]++; 169 | } 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /game_core/map.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_CORE_MAP_H_ 2 | #define GAME_CORE_MAP_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | enum class CellType { 12 | Wall = 0, 13 | Grass = 1, 14 | Sand = 2, 15 | Water = 3, 16 | Forest = 4, 17 | Last = 5 18 | }; 19 | 20 | struct Coordinates { 21 | Coordinates() : x(0), y(0) {} 22 | Coordinates(int x, int y) : x(x), y(y) {} 23 | 24 | bool operator==(Coordinates coordinates); 25 | bool operator!=(Coordinates coordinates); 26 | 27 | int x; 28 | int y; 29 | }; 30 | 31 | class Map { 32 | public: 33 | // The constructor creates necessary object by reading corresponding to 34 | // map_number file. 35 | explicit Map(int map_number); 36 | void DrawMap(QPainter* painter); 37 | 38 | void UpdateCoordinates(Coordinates upper_left_cell_coordinates, 39 | int width, int height); 40 | 41 | CellType GetField(Coordinates cell) const; 42 | int GetWallsPrecalculation(int cell_x, int cell_y) const; 43 | 44 | int GetNumberOfCellsHorizontally() const; 45 | int GetNumberOfCellsVertically() const; 46 | 47 | Coordinates GetUpperLeftCellCoordinates() const; 48 | 49 | int GetWidth() const; 50 | int GetHeight() const; 51 | int GetCellWidth() const; 52 | int GetCellHeight() const; 53 | 54 | Coordinates GetTankInitialCoordinates() const; 55 | std::string GetTankStartDirection() const; 56 | 57 | private: 58 | void RescaleImages(); 59 | void FormMapImage(); 60 | 61 | void WallsPrecalculation(); 62 | 63 | private: 64 | std::vector> map_; 65 | 66 | std::vector images_; 67 | std::vector scaled_pixmaps_; 68 | QPixmap map_scaled_pixmap_; 69 | 70 | std::vector> walls_precalculation_; 71 | 72 | int current_width_; 73 | int current_height_; 74 | int current_cell_width_; 75 | int current_cell_height_; 76 | 77 | Coordinates upper_left_cell_coordinates_; 78 | Coordinates tank_initial_cell_; 79 | std::string tank_start_direction_; 80 | }; 81 | 82 | #endif // GAME_CORE_MAP_H_ 83 | -------------------------------------------------------------------------------- /game_core/new_game_dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "new_game_dialog.h" 2 | 3 | #include "constants.h" 4 | 5 | NewGameDialog::NewGameDialog(QWidget* parent) 6 | : QDialog(parent), 7 | layout_(new QVBoxLayout(this)), 8 | switch_map_menu_(new QComboBox(this)), 9 | switch_tank_menu_(new QComboBox(this)), 10 | switch_difficulty_menu_(new QComboBox(this)), 11 | new_game_label_( 12 | new QLabel(tr("Choose map, tank and difficulty"), this)), 13 | switch_map_label_( 14 | new QLabel(QString(tr("Map")) + ":", this)), 15 | switch_tank_label_( 16 | new QLabel(QString(tr("Tank")) + ":", this)), 17 | switch_difficulty_label_( 18 | new QLabel(QString(tr("Difficulty")) + ":", this)), 19 | buttons_(new QDialogButtonBox( 20 | QDialogButtonBox::Ok, Qt::Horizontal, this)) { 21 | for (int i = 1; i <= constants::kMapsNumber; ++i) { 22 | switch_map_menu_->addItem(tr("Map") + " " + QString::number(i)); 23 | } 24 | 25 | for (int i = 1; i <= constants::kTanksNumber; ++i) { 26 | switch_tank_menu_->addItem(tr("Tank") + " " + QString::number(i)); 27 | } 28 | 29 | for (int i = 0; i < constants::kDifficultyLevelsNumber; ++i) { 30 | switch_difficulty_menu_->addItem(kDifficultyLevelsNames[i]); 31 | } 32 | 33 | layout_->addWidget(new_game_label_); 34 | layout_->addWidget(switch_map_label_); 35 | layout_->addWidget(switch_map_menu_); 36 | layout_->addWidget(switch_tank_label_); 37 | layout_->addWidget(switch_tank_menu_); 38 | layout_->addWidget(switch_difficulty_label_); 39 | layout_->addWidget(switch_difficulty_menu_); 40 | layout_->addWidget(buttons_); 41 | 42 | connect(buttons_, &QDialogButtonBox::accepted, this, &NewGameDialog::accept); 43 | connect(buttons_, &QDialogButtonBox::rejected, this, &NewGameDialog::reject); 44 | } 45 | 46 | void NewGameDialog::accept() { 47 | current_map_number_ = switch_map_menu_->currentIndex(); 48 | current_tank_number_ = switch_tank_menu_->currentIndex(); 49 | current_difficulty_level_ = switch_difficulty_menu_->currentIndex(); 50 | return QDialog::accept(); 51 | } 52 | 53 | void NewGameDialog::reject() { 54 | switch_map_menu_->setCurrentIndex(current_map_number_); 55 | switch_tank_menu_->setCurrentIndex(current_tank_number_); 56 | switch_difficulty_menu_->setCurrentIndex(current_difficulty_level_); 57 | return QDialog::reject(); 58 | } 59 | 60 | int NewGameDialog::GetCurrentMapNumber() const { 61 | return current_map_number_; 62 | } 63 | 64 | int NewGameDialog::GetCurrentTankNumber() const { 65 | return current_tank_number_; 66 | } 67 | 68 | int NewGameDialog::GetCurrentDifficultyLevel() const { 69 | return current_difficulty_level_; 70 | } 71 | -------------------------------------------------------------------------------- /game_core/new_game_dialog.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_CORE_NEW_GAME_DIALOG_H_ 2 | #define GAME_CORE_NEW_GAME_DIALOG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class NewGameDialog : public QDialog { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit NewGameDialog(QWidget* parent = nullptr); 17 | ~NewGameDialog() override = default; 18 | 19 | void accept() override; 20 | void reject() override; 21 | 22 | int GetCurrentMapNumber() const; 23 | int GetCurrentTankNumber() const; 24 | int GetCurrentDifficultyLevel() const; 25 | 26 | private: 27 | int current_map_number_ = 0; 28 | int current_tank_number_ = 0; 29 | int current_difficulty_level_ = 0; 30 | 31 | QVBoxLayout* layout_; 32 | QComboBox* switch_map_menu_; 33 | QComboBox* switch_tank_menu_; 34 | QComboBox* switch_difficulty_menu_; 35 | QLabel* new_game_label_; 36 | QLabel* switch_map_label_; 37 | QLabel* switch_tank_label_; 38 | QLabel* switch_difficulty_label_; 39 | QDialogButtonBox* buttons_; 40 | 41 | const std::vector kDifficultyLevelsNames = 42 | {tr("Easy"), tr("Normal"), tr("Hard")}; 43 | }; 44 | 45 | #endif // GAME_CORE_NEW_GAME_DIALOG_H_ 46 | -------------------------------------------------------------------------------- /game_core/settings_dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "settings_dialog.h" 2 | 3 | #include "constants.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | SettingsDialog::SettingsDialog(QWidget* parent) 10 | : QDialog(parent), 11 | layout_(new QVBoxLayout(this)), 12 | virtual_keys_checkbox_(new QCheckBox(tr("Virtual keys"), this)), 13 | mobile_virtual_keys_checkbox_(new QCheckBox(tr( 14 | "Mobile virtual keys layout"), this)), 15 | charge_line_checkbox_(new QCheckBox(tr("Charge line"), this)), 16 | music_checkbox_(new QCheckBox(tr("Music"), this)), 17 | fps_menu_label_(new QLabel(tr("Performance"), this)), 18 | switch_fps_menu_(new QComboBox(this)), 19 | language_menu_label_(new QLabel(tr("Language"), this)), 20 | switch_language_menu_(new QComboBox(this)), 21 | buttons_(new QDialogButtonBox( 22 | QDialogButtonBox::Ok, Qt::Horizontal, this)) { 23 | for (int fps_option : constants::kFpsOptions) { 24 | switch_fps_menu_->addItem(tr("%1 frames per second").arg(fps_option)); 25 | } 26 | 27 | for (auto& language : kLanguagesNames) { 28 | switch_language_menu_->addItem(language); 29 | } 30 | 31 | { 32 | using constants::kVirtualKeysEnabledKey; 33 | using constants::kMobileVirtualKeysStyleEnabledKey; 34 | using constants::kChargeLineEnabledKey; 35 | using constants::kMusicEnabledKey; 36 | using constants::kCurrentFpsOptionKey; 37 | using constants::kLanguageKey; 38 | using constants::kLanguages; 39 | 40 | QSettings settings; 41 | 42 | virtual_keys_enabled_ = settings.value(kVirtualKeysEnabledKey).toBool(); 43 | mobile_virtual_keys_style_enabled_ = settings.value( 44 | kMobileVirtualKeysStyleEnabledKey).toBool(); 45 | charge_line_enabled_ = settings.value(kChargeLineEnabledKey).toBool(); 46 | music_enabled_ = settings.value(kMusicEnabledKey).toBool(); 47 | current_fps_option_ = settings.value(kCurrentFpsOptionKey).toInt(); 48 | 49 | auto language_string = settings.value(kLanguageKey).toString(); 50 | current_language_ = std::find( 51 | kLanguages.begin(), kLanguages.end(), language_string) 52 | - kLanguages.begin(); 53 | } 54 | 55 | ResetSettings(); 56 | 57 | layout_->addWidget(virtual_keys_checkbox_); 58 | layout_->addWidget(mobile_virtual_keys_checkbox_); 59 | layout_->addWidget(charge_line_checkbox_); 60 | layout_->addWidget(music_checkbox_); 61 | layout_->addWidget(fps_menu_label_); 62 | layout_->addWidget(switch_fps_menu_); 63 | layout_->addWidget(language_menu_label_); 64 | layout_->addWidget(switch_language_menu_); 65 | layout_->addWidget(buttons_); 66 | 67 | connect(virtual_keys_checkbox_, &QCheckBox::stateChanged, [this] { 68 | mobile_virtual_keys_checkbox_->setEnabled( 69 | virtual_keys_checkbox_->isChecked()); 70 | }); 71 | connect(buttons_, &QDialogButtonBox::accepted, 72 | this, &SettingsDialog::accept); 73 | connect(buttons_, &QDialogButtonBox::rejected, 74 | this, &SettingsDialog::reject); 75 | } 76 | 77 | void SettingsDialog::accept() { 78 | virtual_keys_enabled_ = virtual_keys_checkbox_->isChecked(); 79 | mobile_virtual_keys_style_enabled_ = 80 | mobile_virtual_keys_checkbox_->isChecked(); 81 | charge_line_enabled_ = charge_line_checkbox_->isChecked(); 82 | music_enabled_ = music_checkbox_->isChecked(); 83 | current_fps_option_ = switch_fps_menu_->currentIndex(); 84 | 85 | WriteSettings(); 86 | 87 | if (current_language_ != switch_language_menu_->currentIndex()) { 88 | SwitchLanguage(); 89 | } 90 | 91 | return QDialog::accept(); 92 | } 93 | 94 | void SettingsDialog::reject() { 95 | ResetSettings(); 96 | return QDialog::reject(); 97 | } 98 | 99 | bool SettingsDialog::AreVirtualKeysEnabled() const { 100 | return virtual_keys_enabled_; 101 | } 102 | 103 | bool SettingsDialog::IsMobileVirtualKeysStyleEnabled() const { 104 | return mobile_virtual_keys_style_enabled_; 105 | } 106 | 107 | bool SettingsDialog::IsChargeLineEnabled() const { 108 | return charge_line_enabled_; 109 | } 110 | 111 | bool SettingsDialog::IsMusicEnabled() const { 112 | return music_enabled_; 113 | } 114 | 115 | int SettingsDialog::GetCurrentFpsOption() const { 116 | return current_fps_option_; 117 | } 118 | 119 | void SettingsDialog::ResetSettings() { 120 | virtual_keys_checkbox_->setChecked(virtual_keys_enabled_); 121 | mobile_virtual_keys_checkbox_->setChecked(mobile_virtual_keys_style_enabled_); 122 | charge_line_checkbox_->setChecked(charge_line_enabled_); 123 | music_checkbox_->setChecked(music_enabled_); 124 | switch_fps_menu_->setCurrentIndex(current_fps_option_); 125 | switch_language_menu_->setCurrentIndex(current_language_); 126 | 127 | mobile_virtual_keys_checkbox_->setEnabled( 128 | virtual_keys_checkbox_->isChecked()); 129 | } 130 | 131 | void SettingsDialog::WriteSettings() { 132 | QSettings settings; 133 | 134 | settings.setValue(constants::kVirtualKeysEnabledKey, 135 | virtual_keys_enabled_); 136 | settings.setValue(constants::kMobileVirtualKeysStyleEnabledKey, 137 | mobile_virtual_keys_style_enabled_); 138 | settings.setValue(constants::kChargeLineEnabledKey, 139 | charge_line_enabled_); 140 | settings.setValue(constants::kMusicEnabledKey, 141 | music_enabled_); 142 | settings.setValue(constants::kCurrentFpsOptionKey, 143 | current_fps_option_); 144 | } 145 | 146 | void SettingsDialog::SwitchLanguage() { 147 | QMessageBox::StandardButton response; 148 | QString message = tr( 149 | "The app needs to be restarted to change the language."); 150 | 151 | #ifdef Q_OS_ANDROID 152 | response = QMessageBox::question(this, tr("Close application?"), message); 153 | #else 154 | response = QMessageBox::question(this, tr("Restart application?"), message); 155 | #endif 156 | 157 | if (response != QMessageBox::Yes) { 158 | switch_language_menu_->setCurrentIndex(current_language_); 159 | return; 160 | } 161 | 162 | QSettings settings; 163 | settings.setValue( 164 | constants::kLanguageKey, 165 | constants::kLanguages[switch_language_menu_->currentIndex()]); 166 | 167 | QCoreApplication::exit(constants::kApplicationRestartCode); 168 | } 169 | -------------------------------------------------------------------------------- /game_core/settings_dialog.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_CORE_SETTINGS_DIALOG_H_ 2 | #define GAME_CORE_SETTINGS_DIALOG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class SettingsDialog : public QDialog { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit SettingsDialog(QWidget* parent = nullptr); 18 | ~SettingsDialog() override = default; 19 | 20 | void accept() override; 21 | void reject() override; 22 | 23 | bool AreVirtualKeysEnabled() const; 24 | bool IsMobileVirtualKeysStyleEnabled() const; 25 | bool IsChargeLineEnabled() const; 26 | bool IsMusicEnabled() const; 27 | int GetCurrentFpsOption() const; 28 | 29 | private: 30 | void ResetSettings(); 31 | void WriteSettings(); 32 | void SwitchLanguage(); 33 | 34 | private: 35 | bool virtual_keys_enabled_ = false; 36 | bool mobile_virtual_keys_style_enabled_ = false; 37 | bool charge_line_enabled_ = false; 38 | bool music_enabled_ = false; 39 | int current_fps_option_ = 0; 40 | int current_language_ = 0; 41 | 42 | QVBoxLayout* layout_; 43 | QCheckBox* virtual_keys_checkbox_; 44 | QCheckBox* mobile_virtual_keys_checkbox_; 45 | QCheckBox* charge_line_checkbox_; 46 | QCheckBox* music_checkbox_; 47 | QLabel* fps_menu_label_; 48 | QComboBox* switch_fps_menu_; 49 | QLabel* language_menu_label_; 50 | QComboBox* switch_language_menu_; 51 | QDialogButtonBox* buttons_; 52 | 53 | const std::vector kLanguagesNames = 54 | {tr("en_US"), tr("ru_RU"), tr("be_BY")}; 55 | }; 56 | 57 | #endif // GAME_CORE_SETTINGS_DIALOG_H_ 58 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "game_core/constants.h" 11 | #include "game_core/mainwindow.h" 12 | 13 | #ifdef Q_OS_ANDROID 14 | #include 15 | #include 16 | #endif 17 | 18 | int main(int argc, char* argv[]) { 19 | #ifdef Q_OS_ANDROID 20 | // Workaround for the bug with random selection controls appearing on 21 | // the screen (Android). 22 | qputenv("QT_QPA_NO_TEXT_HANDLES", "1"); 23 | #endif 24 | 25 | QApplication a(argc, argv); 26 | QApplication::setStyle(QStyleFactory::create("Fusion")); 27 | QApplication::setOrganizationName(constants::kOrganizationName); 28 | QApplication::setApplicationName(constants::kApplicationName); 29 | 30 | // Loading language settings. If no settings found, the en_US locale is 31 | // set as the default. 32 | QSettings settings; 33 | QString language; 34 | if (settings.contains(constants::kLanguageKey)) { 35 | language = settings.value(constants::kLanguageKey, language).toString(); 36 | } else { 37 | language = "en_US"; 38 | settings.setValue(constants::kLanguageKey, language); 39 | } 40 | 41 | // Loading app translations. 42 | QTranslator app_translator; 43 | app_translator.load(":/translations/tanks_" + language); 44 | QApplication::installTranslator(&app_translator); 45 | 46 | // Loading Qt components translations (QMessageBoxes, etc.). 47 | QTranslator internal_translator; 48 | internal_translator.load( 49 | "qtbase_" + language, 50 | QLibraryInfo::location(QLibraryInfo::TranslationsPath)); 51 | QApplication::installTranslator(&internal_translator); 52 | 53 | #ifdef Q_OS_ANDROID 54 | QtAndroid::runOnAndroidThread([] { 55 | auto activity = QtAndroid::androidActivity(); 56 | auto window = 57 | activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); 58 | const int FLAG_KEEP_SCREEN_ON = 128; 59 | window.callMethod("addFlags", "(I)V", FLAG_KEEP_SCREEN_ON); 60 | 61 | QAndroidJniEnvironment env; 62 | if (env->ExceptionCheck()) { 63 | env->ExceptionClear(); 64 | } 65 | }); 66 | #endif 67 | 68 | MainWindow main_window; 69 | main_window.setWindowTitle("Tanks"); 70 | 71 | #ifdef Q_OS_ANDROID 72 | main_window.showFullScreen(); 73 | #else 74 | main_window.show(); 75 | #endif 76 | 77 | int return_code = QApplication::exec(); 78 | if (return_code != constants::kApplicationRestartCode) { 79 | return return_code; 80 | } 81 | 82 | #ifdef Q_OS_ANDROID 83 | // Code after won't work for SDK >= 28/29 84 | qDebug() << QtAndroid::androidSdkVersion(); 85 | 86 | QtAndroid::runOnAndroidThread([] { 87 | auto activity = QtAndroid::androidActivity(); 88 | 89 | auto packageManager = activity.callObjectMethod( 90 | "getPackageManager", 91 | "()Landroid/content/pm/PackageManager;"); 92 | 93 | auto activityIntent = packageManager.callObjectMethod( 94 | "getLaunchIntentForPackage", 95 | "(Ljava/lang/String;)Landroid/content/Intent;", 96 | activity.callObjectMethod("getPackageName", 97 | "()Ljava/lang/String;").object()); 98 | 99 | auto pendingIntent = QAndroidJniObject::callStaticObjectMethod( 100 | "android/app/PendingIntent", 101 | "getActivity", 102 | "(Landroid/content/Context;ILandroid/content/Intent;I)" 103 | "Landroid/app/PendingIntent;", 104 | activity.object(), 105 | jint(0), 106 | activityIntent.object(), 107 | QAndroidJniObject::getStaticField("android/content/Intent", 108 | "FLAG_ACTIVITY_CLEAR_TOP")); 109 | 110 | auto alarmManager = activity.callObjectMethod( 111 | "getSystemService", 112 | "(Ljava/lang/String;)Ljava/lang/Object;", 113 | QAndroidJniObject::getStaticObjectField( 114 | "android/content/Context", 115 | "ALARM_SERVICE", 116 | "Ljava/lang/String;").object()); 117 | 118 | alarmManager.callMethod( 119 | "setExact", 120 | "(IJLandroid/app/PendingIntent;)V", 121 | QAndroidJniObject::getStaticField( 122 | "android/app/AlarmManager", "RTC"), 123 | jlong(QDateTime::currentMSecsSinceEpoch() + 100), 124 | pendingIntent.object()); 125 | 126 | QAndroidJniEnvironment env; 127 | if (env->ExceptionCheck()) { 128 | env->ExceptionClear(); 129 | } 130 | }); 131 | 132 | return 0; 133 | #else 134 | QString executable_path = QApplication::applicationFilePath(); 135 | if (!QProcess::startDetached(executable_path, {})) { 136 | std::cerr << "Starting a new process failed!" << std::endl; 137 | return 1; 138 | } 139 | return 0; 140 | #endif 141 | } 142 | -------------------------------------------------------------------------------- /movable_objects/boom.cpp: -------------------------------------------------------------------------------- 1 | #include "boom.h" 2 | 3 | Boom::Boom(std::shared_ptr map, 4 | const std::shared_ptr& tank, int speed) 5 | : Movable(std::move(map), ":/textures/boom.png", tank->GetCoordinates(), 6 | tank->GetDirection(), speed), 7 | boom_sound_() { 8 | opacity_ = 0.85; 9 | 10 | boom_sound_.setMedia(QUrl("qrc:/sounds/boom.mp3")); 11 | boom_sound_.play(); 12 | } 13 | 14 | void Boom::Draw(QPainter* painter) { 15 | painter->save(); 16 | painter->setOpacity(opacity_); 17 | painter->translate( 18 | upper_left_cell_coordinates_.x + width_ / 2, 19 | upper_left_cell_coordinates_.y + height_ / 2); 20 | painter->drawPixmap(-width_ / 2, 21 | -height_ / 2, 22 | scaled_pixmap_); 23 | painter->restore(); 24 | } 25 | 26 | void Boom::StartMovement( 27 | const std::list>&, 28 | std::list, Coordinates>>*, 29 | std::vector>>*, 30 | int number_of_cells) { 31 | time_to_finish_movement_ = current_speed_; 32 | cells_to_finish_movement_ = number_of_cells - 1; 33 | } 34 | 35 | void Boom::UpdateCoordinates(Coordinates) { 36 | int current_cell_width = 37 | map_->GetWidth() / map_->GetNumberOfCellsHorizontally(); 38 | int current_cell_height = 39 | map_->GetHeight() / map_->GetNumberOfCellsVertically(); 40 | 41 | double movement_proportion = cells_to_finish_movement_ + 1 42 | - 1.0 * time_to_finish_movement_ / current_speed_; 43 | 44 | width_ = 45 | current_cell_width + 2 * current_cell_width * movement_proportion; 46 | height_ = 47 | current_cell_height + 2 * current_cell_height * movement_proportion; 48 | 49 | upper_left_cell_coordinates_.x = map_->GetUpperLeftCellCoordinates().x 50 | + current_cell_width * (cell_.x - movement_proportion); 51 | upper_left_cell_coordinates_.y = map_->GetUpperLeftCellCoordinates().y 52 | + current_cell_height * (cell_.y - movement_proportion); 53 | 54 | RescaleImage(); 55 | } 56 | -------------------------------------------------------------------------------- /movable_objects/boom.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVABLE_OBJECTS_BOOM_H_ 2 | #define MOVABLE_OBJECTS_BOOM_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "tank.h" 13 | 14 | class Boom : public Movable { 15 | public: 16 | Boom(std::shared_ptr map, const std::shared_ptr& tank, 17 | int speed); 18 | ~Boom() override = default; 19 | 20 | void UpdateCoordinates(Coordinates cell) override; 21 | void Draw(QPainter* painter) override; 22 | 23 | void StartMovement( 24 | const std::list>&, 25 | std::list, Coordinates>>*, 26 | std::vector>>*, 27 | int number_of_cells) override; 28 | 29 | private: 30 | QMediaPlayer boom_sound_; 31 | }; 32 | 33 | #endif // MOVABLE_OBJECTS_BOOM_H_ 34 | -------------------------------------------------------------------------------- /movable_objects/bot.cpp: -------------------------------------------------------------------------------- 1 | #include "bot.h" 2 | 3 | Bot::Bot(std::shared_ptr map, const QString& texture_path, 4 | Coordinates initial_cell, TankParameters tank_parameters, 5 | BotParameters bot_parameters, Direction direction) 6 | : Tank(std::move(map), texture_path, initial_cell, tank_parameters, 7 | direction), 8 | moving_length_(bot_parameters.moving_length), 9 | number_of_turns_(bot_parameters.number_of_turns), 10 | side_rotation_frequency_(bot_parameters.side_rotation_frequency) {} 11 | 12 | bool Bot::IsTurnNeeded() const { 13 | return time_to_finish_rotation_ > 0; 14 | } 15 | 16 | bool Bot::IsRotationStartNeeded(const std::shared_ptr&) { 17 | if (time_to_finish_rotation_ <= 0 && time_to_finish_movement_ <= 0) { 18 | if (current_number_of_turns_ > 0) { 19 | current_number_of_turns_--; 20 | return current_number_of_turns_ > 0; 21 | } 22 | if (number_of_cells_to_move_ == 0) { 23 | TryToChangeRotationDirectionAndRotate(); 24 | return true; 25 | } 26 | } 27 | return false; 28 | } 29 | 30 | bool Bot::IsMovingStartNeeded( 31 | const std::list>&, 32 | const std::vector>>&) { 33 | if (time_to_finish_movement_ <= 0 && time_to_finish_rotation_ <= 0) { 34 | if (number_of_cells_to_move_ == 0) { 35 | if (current_number_of_turns_ == 0) { 36 | number_of_cells_to_move_ = moving_length_; 37 | } else { 38 | return false; 39 | } 40 | } else { 41 | number_of_cells_to_move_--; 42 | } 43 | return number_of_cells_to_move_ > 0; 44 | } 45 | return false; 46 | } 47 | 48 | bool Bot::IsShotNeeded(const std::shared_ptr& tank) { 49 | if (time_to_finish_rotation_ == 0 && time_to_finish_movement_ == 0) { 50 | int direction = GetDirectionAsInt(); 51 | Coordinates tank_cell = tank->GetCoordinates(); 52 | Coordinates bot_cell = GetCoordinates(); 53 | if (map_->GetField(tank_cell) == CellType::Forest) { 54 | return false; 55 | } 56 | 57 | if (direction == 0 || direction == 2) { 58 | if (tank_cell.x == bot_cell.x) { 59 | if (IsWallBetweenTankAndBotHorizontally(tank_cell, bot_cell)) { 60 | return false; 61 | } 62 | 63 | if (CheckDirection(tank_cell.y, bot_cell.y, direction)) { 64 | return true; 65 | } 66 | } 67 | } 68 | if (direction == 1 || direction == 3) { 69 | if (tank_cell.y == bot_cell.y) { 70 | if (IsWallBetweenTankAndBotVertically(tank_cell, bot_cell)) { 71 | return false; 72 | } 73 | 74 | if (CheckDirection(tank_cell.x, bot_cell.x, direction)) { 75 | return true; 76 | } 77 | } 78 | } 79 | } 80 | return false; 81 | } 82 | 83 | bool Bot::CheckDirection(int tank_coordinate, int bot_coordinate, 84 | int direction) { 85 | if (tank_coordinate > bot_coordinate) { 86 | if (direction == 0 || direction == 3) { 87 | return false; 88 | } 89 | } else if (direction == 2 || direction == 1) { 90 | return false; 91 | } 92 | return true; 93 | } 94 | 95 | void Bot::TryToChangeRotationDirectionAndRotate() { 96 | if (random_generator_() % side_rotation_frequency_ == 0) { 97 | TurnRotationReverseOn(); 98 | } else { 99 | TurnRotationReverseOff(); 100 | } 101 | current_number_of_turns_ = number_of_turns_; 102 | current_number_of_turns_--; 103 | } 104 | 105 | bool Bot::IsWallBetweenTankAndBotHorizontally( 106 | Coordinates tank_cell, Coordinates bot_cell) const { 107 | return (map_->GetWallsPrecalculation(bot_cell.x, bot_cell.y) + 108 | map_->GetWallsPrecalculation(tank_cell.x - 1, tank_cell.y - 1)) - 109 | map_->GetWallsPrecalculation(tank_cell.x, tank_cell.y - 1) - 110 | map_->GetWallsPrecalculation(bot_cell.x - 1, bot_cell.y) != 0; 111 | } 112 | 113 | bool Bot::IsWallBetweenTankAndBotVertically( 114 | Coordinates tank_cell, Coordinates bot_cell) const { 115 | return (map_->GetWallsPrecalculation(bot_cell.x, bot_cell.y) + 116 | map_->GetWallsPrecalculation(tank_cell.x - 1, tank_cell.y - 1)) - 117 | map_->GetWallsPrecalculation(tank_cell.x - 1, tank_cell.y) - 118 | map_->GetWallsPrecalculation(bot_cell.x, bot_cell.y - 1) != 0; 119 | } 120 | -------------------------------------------------------------------------------- /movable_objects/bot.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVABLE_OBJECTS_BOT_H_ 2 | #define MOVABLE_OBJECTS_BOT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../game_core/map.h" 10 | #include "tank.h" 11 | 12 | struct BotParameters { 13 | int moving_length; 14 | int number_of_turns; 15 | int side_rotation_frequency; 16 | }; 17 | 18 | class Bot : public Tank { 19 | public: 20 | Bot(std::shared_ptr map, const QString& texture_path, 21 | Coordinates initial_cell, TankParameters tank_parameters, 22 | BotParameters bot_parameters, Direction direction); 23 | ~Bot() override = default; 24 | 25 | virtual bool IsTurnNeeded() const; 26 | 27 | virtual bool IsRotationStartNeeded(const std::shared_ptr&); 28 | virtual bool IsMovingStartNeeded( 29 | const std::list>&, 30 | const std::vector>>&); 31 | 32 | virtual bool IsShotNeeded(const std::shared_ptr&); 33 | 34 | protected: 35 | virtual bool CheckDirection(int tank_coordinate, int bot_coordinate, 36 | int direction); 37 | 38 | virtual void TryToChangeRotationDirectionAndRotate(); 39 | 40 | bool IsWallBetweenTankAndBotHorizontally( 41 | Coordinates tank_cell, Coordinates bot_cell) const; 42 | bool IsWallBetweenTankAndBotVertically( 43 | Coordinates tank_cell, Coordinates bot_cell) const; 44 | 45 | protected: 46 | int number_of_cells_to_move_ = 0; 47 | int current_number_of_turns_ = 0; 48 | 49 | const int moving_length_; 50 | const int number_of_turns_; 51 | const int side_rotation_frequency_; 52 | }; 53 | 54 | #endif // MOVABLE_OBJECTS_BOT_H_ 55 | -------------------------------------------------------------------------------- /movable_objects/clever_bot.cpp: -------------------------------------------------------------------------------- 1 | #include "clever_bot.h" 2 | 3 | #include 4 | 5 | CleverBot::CleverBot( 6 | std::shared_ptr map, const QString& texture_path, 7 | Coordinates initial_cell, TankParameters tank_parameters, 8 | BotParameters bot_parameters, Direction direction) 9 | : ImprovedBot(std::move(map), texture_path, initial_cell, 10 | tank_parameters, bot_parameters, direction) { 11 | height_ = map_->GetNumberOfCellsVertically(); 12 | width_ = map_->GetNumberOfCellsHorizontally(); 13 | distance_.resize(width_); 14 | for (int i = 0; i < height_; ++i) { 15 | for (int j = 0; j < width_; ++j) { 16 | distance_[j].push_back(height_ * width_); 17 | } 18 | } 19 | } 20 | 21 | bool CleverBot::IsRotationStartNeeded( 22 | const std::shared_ptr& tank) { 23 | if (time_to_finish_rotation_ <= 0 && time_to_finish_movement_ <= 0) { 24 | if (current_number_of_turns_ > 0) { 25 | current_number_of_turns_--; 26 | return current_number_of_turns_ > 0; 27 | } 28 | if (map_->GetField(tank->GetCoordinates()) == CellType::Forest) { 29 | return Bot::IsRotationStartNeeded(tank); 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | bool CleverBot::IsMovingStartNeeded( 36 | const std::list>& objects, 37 | const std::vector>>& portals) { 38 | auto tank = objects.front(); 39 | if (time_to_finish_movement_ <= 0 && time_to_finish_rotation_ <= 0) { 40 | Bfs(objects, portals, tank->GetCoordinates()); 41 | 42 | int direction = GetDirectionAsInt(); 43 | int delta_x = 0; 44 | int delta_y = 0; 45 | if (direction == 0) { 46 | delta_y = 1; 47 | } else if (direction == 2) { 48 | delta_y = -1; 49 | } else if (direction == 3) { 50 | delta_x = 1; 51 | } else if (direction == 1) { 52 | delta_x = -1; 53 | } 54 | 55 | auto current_cell = GetCoordinates(); 56 | if (map_->GetField(tank->GetCoordinates()) == CellType::Forest) { 57 | return Bot::IsMovingStartNeeded(objects, portals); 58 | } else if (distance_[current_cell.x - delta_x][current_cell.y - delta_y] 59 | == distance_[current_cell.x][current_cell.y] - 1) { 60 | if (distance_[current_cell.x][current_cell.y] - 1 61 | == distance_[tank->GetCoordinates().x][tank->GetCoordinates().y]) { 62 | return false; 63 | } 64 | number_of_cells_to_move_ = 1; 65 | return true; 66 | } else if (distance_[current_cell.x - delta_y][current_cell.y - delta_x] 67 | == distance_[current_cell.x][current_cell.y] - 1) { 68 | MaybeChangeRotationDirection(delta_x, false); 69 | current_number_of_turns_ = 2; 70 | return false; 71 | } else if (distance_[current_cell.x + delta_y][current_cell.y + delta_x] 72 | == distance_[current_cell.x][current_cell.y] - 1) { 73 | MaybeChangeRotationDirection(delta_x, true); 74 | current_number_of_turns_ = 2; 75 | return false; 76 | } else if (distance_[current_cell.x + delta_x][current_cell.y + delta_y] 77 | == distance_[current_cell.x][current_cell.y] - 1) { 78 | current_number_of_turns_ = 3; 79 | return false; 80 | } 81 | } 82 | return false; 83 | } 84 | 85 | void CleverBot::MaybeChangeRotationDirection(int delta_x, bool condition) { 86 | if ((delta_x == 0) ^ condition) { 87 | TurnRotationReverseOn(); 88 | } else { 89 | TurnRotationReverseOff(); 90 | } 91 | } 92 | 93 | void CleverBot::Bfs( 94 | const std::list>& objects, 95 | const std::vector>>& portals, 96 | Coordinates cell) { 97 | std::queue cells; 98 | cells.emplace(cell, cell, 0); 99 | 100 | for (int i = 0; i < height_; ++i) { 101 | for (int j = 0; j < width_; ++j) { 102 | distance_[j][i] = height_ * width_; 103 | } 104 | } 105 | 106 | while (!cells.empty()) { 107 | cell = cells.front().cell; 108 | auto previous_cell = cells.front().previous_cell; 109 | int current_distance = cells.front().distance; 110 | cells.pop(); 111 | 112 | if (cell.x <= 0 || cell.x >= width_ || cell.y <= 0 || cell.y >= height_) { 113 | continue; 114 | } 115 | if (map_->GetField(cell) == CellType::Wall) { 116 | continue; 117 | } 118 | if (distance_[cell.x][cell.y] <= current_distance) { 119 | continue; 120 | } 121 | if (cell == GetCoordinates()) { 122 | distance_[cell.x][cell.y] = current_distance; 123 | break; 124 | } 125 | 126 | bool bad_cell = false; 127 | for (const auto& object : objects) { 128 | if (cell == GetCoordinates() && object != objects.front()) { 129 | bad_cell = true; 130 | break; 131 | } 132 | } 133 | if (bad_cell) { 134 | continue; 135 | } 136 | 137 | if (std::dynamic_pointer_cast(portals[cell.x][cell.y]) != nullptr) { 138 | std::shared_ptr 139 | portal = std::dynamic_pointer_cast(portals[cell.x][cell.y]); 140 | auto portal_cell = portal->GetNewCell(); 141 | distance_[portal_cell.x][portal_cell.y] = current_distance - 1; 142 | 143 | if (cell.x - previous_cell.x == 1) { 144 | portal_cell.x++; 145 | } else if (cell.x - previous_cell.x == -1) { 146 | portal_cell.x--; 147 | } else if (cell.y - previous_cell.y == 1) { 148 | portal_cell.y++; 149 | } else { 150 | portal_cell.y--; 151 | } 152 | 153 | cells.emplace(portal_cell, portal->GetNewCell(), current_distance); 154 | continue; 155 | } 156 | 157 | distance_[cell.x][cell.y] = current_distance; 158 | cells.emplace(Coordinates(cell.x + 1, cell.y), cell, current_distance + 1); 159 | cells.emplace(Coordinates(cell.x - 1, cell.y), cell, current_distance + 1); 160 | cells.emplace(Coordinates(cell.x, cell.y + 1), cell, current_distance + 1); 161 | cells.emplace(Coordinates(cell.x, cell.y - 1), cell, current_distance + 1); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /movable_objects/clever_bot.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVABLE_OBJECTS_CLEVER_BOT_H_ 2 | #define MOVABLE_OBJECTS_CLEVER_BOT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "improved_bot.h" 10 | #include "tank.h" 11 | #include "../game_core/map.h" 12 | #include "../static_objects/portal.h" 13 | 14 | class CleverBot : public ImprovedBot { 15 | public: 16 | CleverBot(std::shared_ptr map, const QString& texture_path, 17 | Coordinates initial_cell, TankParameters tank_parameters, 18 | BotParameters bot_parameters, Direction direction); 19 | ~CleverBot() override = default; 20 | 21 | protected: 22 | bool IsRotationStartNeeded(const std::shared_ptr& tank) override; 23 | bool IsMovingStartNeeded( 24 | const std::list>& objects, 25 | const std::vector>>& portals) 26 | override; 27 | 28 | private: 29 | struct CellInfo { 30 | Coordinates cell; 31 | Coordinates previous_cell; 32 | int distance; 33 | 34 | CellInfo(Coordinates cell, Coordinates previous_cell, int distance) 35 | : cell(cell), previous_cell(previous_cell), distance(distance) {} 36 | }; 37 | std::vector> distance_; 38 | 39 | void MaybeChangeRotationDirection(int delta_x, bool condition); 40 | 41 | void Bfs( 42 | const std::list>& objects, 43 | const std::vector>>& portals, 44 | Coordinates cell); 45 | 46 | int height_; 47 | int width_; 48 | }; 49 | 50 | #endif // MOVABLE_OBJECTS_CLEVER_BOT_H_ 51 | -------------------------------------------------------------------------------- /movable_objects/improved_bot.cpp: -------------------------------------------------------------------------------- 1 | #include "improved_bot.h" 2 | 3 | ImprovedBot::ImprovedBot( 4 | std::shared_ptr map, const QString& texture_path, 5 | Coordinates initial_cell, TankParameters tank_parameters, 6 | BotParameters bot_parameters, Direction direction) 7 | : Bot(std::move(map), texture_path, initial_cell, tank_parameters, 8 | bot_parameters, direction) {} 9 | 10 | bool ImprovedBot::IsRotationStartNeeded( 11 | const std::shared_ptr& tank) { 12 | if (time_to_finish_rotation_ <= 0 && time_to_finish_movement_ <= 0) { 13 | if (current_number_of_turns_ > 0) { 14 | current_number_of_turns_--; 15 | return true; 16 | } 17 | if (number_of_cells_to_move_ == 0) { 18 | if (IsShotNeeded(tank)) { 19 | return false; 20 | } 21 | TryToChangeRotationDirectionAndRotate(); 22 | return true; 23 | } 24 | } 25 | return false; 26 | } 27 | 28 | bool ImprovedBot::IsShotNeeded(const std::shared_ptr& tank) { 29 | if (time_to_finish_rotation_ <= 0 && time_to_finish_movement_ <= 0) { 30 | int direction = GetDirectionAsInt(); 31 | Coordinates tank_cell = tank->GetCoordinates(); 32 | Coordinates bot_cell = GetCoordinates(); 33 | if (map_->GetField(tank_cell) == CellType::Forest) { 34 | return false; 35 | } 36 | 37 | if (tank_cell.x == bot_cell.x) { 38 | if (IsWallBetweenTankAndBotHorizontally(tank_cell, bot_cell)) { 39 | return false; 40 | } 41 | return MaybeChangeDirection(tank_cell.y, bot_cell.y, direction, 0, 2); 42 | } 43 | 44 | if (tank_cell.y == bot_cell.y) { 45 | if (IsWallBetweenTankAndBotVertically(tank_cell, bot_cell)) { 46 | return false; 47 | } 48 | return MaybeChangeDirection(tank_cell.x, bot_cell.x, direction, 3, 1); 49 | } 50 | } 51 | return false; 52 | } 53 | 54 | bool ImprovedBot::CheckDirection( 55 | int tank_coordinate, int bot_coordinate, int direction) { 56 | number_of_cells_to_move_ = 0; 57 | current_number_of_turns_ = 1; 58 | MaybeChangeRotationDirection(direction, tank_coordinate <= bot_coordinate); 59 | return false; 60 | } 61 | 62 | bool ImprovedBot::MaybeChangeDirection(int tank_coordinate, 63 | int bot_coordinate, 64 | int current_direction, 65 | int first_direction, 66 | int second_direction) { 67 | if (tank_coordinate > bot_coordinate) { 68 | if (current_direction == second_direction) { 69 | return true; 70 | } else if (current_direction == first_direction) { 71 | return ChangeDirection(); 72 | } 73 | } else if (current_direction == first_direction) { 74 | return true; 75 | } else if (current_direction == second_direction) { 76 | return ChangeDirection(); 77 | } 78 | return CheckDirection(tank_coordinate, bot_coordinate, current_direction); 79 | } 80 | 81 | void ImprovedBot::MaybeChangeRotationDirection(int direction, bool condition) { 82 | if ((direction == 0 || direction == 1) ^ condition) { 83 | TurnRotationReverseOff(); 84 | } else { 85 | TurnRotationReverseOn(); 86 | } 87 | } 88 | 89 | bool ImprovedBot::ChangeDirection() { 90 | number_of_cells_to_move_ = 0; 91 | current_number_of_turns_ = 2; 92 | return false; 93 | } 94 | -------------------------------------------------------------------------------- /movable_objects/improved_bot.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVABLE_OBJECTS_IMPROVED_BOT_H_ 2 | #define MOVABLE_OBJECTS_IMPROVED_BOT_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "bot.h" 8 | 9 | class ImprovedBot : public Bot { 10 | public: 11 | ImprovedBot(std::shared_ptr map, const QString& texture_path, 12 | Coordinates initial_cell, TankParameters tank_parameters, 13 | BotParameters bot_parameters, Direction direction); 14 | ~ImprovedBot() override = default; 15 | 16 | protected: 17 | bool CheckDirection( 18 | int tank_coordinate, int bot_coordinate, int direction) override; 19 | bool IsRotationStartNeeded(const std::shared_ptr& tank) override; 20 | bool IsShotNeeded(const std::shared_ptr&) override; 21 | 22 | private: 23 | bool ChangeDirection(); 24 | bool MaybeChangeDirection(int tank_coordinate, 25 | int bot_coordinate, 26 | int current_direction, 27 | int first_direction, 28 | int second_direction); 29 | void MaybeChangeRotationDirection(int direction, bool condition); 30 | }; 31 | 32 | #endif // MOVABLE_OBJECTS_IMPROVED_BOT_H_ 33 | -------------------------------------------------------------------------------- /movable_objects/movable.cpp: -------------------------------------------------------------------------------- 1 | #include "movable.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "rocket.h" 7 | #include "tank.h" 8 | #include "../game_core/constants.h" 9 | #include "../static_objects/portal.h" 10 | 11 | Movable::Movable(std::shared_ptr map, const QString& texture_path, 12 | Coordinates cell, Direction direction, int speed) 13 | : GameObject(std::move(map), texture_path, cell), 14 | previous_cell_(cell), current_speed_(speed), basic_speed_(speed), 15 | rotate_degree_(90 * static_cast(direction)) { 16 | directions_[static_cast(direction)] = 1; 17 | } 18 | 19 | void Movable::StartMovement( 20 | const std::list>& tanks, 21 | std::list, Coordinates>>* objects_copies_, 22 | std::vector>>* objects, 23 | int number_of_cells) { 24 | Coordinates new_cell = { 25 | cell_.x + linear_movement_direction_ * (directions_[1] - directions_[3]), 26 | cell_.y + linear_movement_direction_ * (directions_[2] - directions_[0])}; 27 | Coordinates old_cell = new_cell; 28 | 29 | if (map_->GetField(new_cell) == CellType::Wall) { 30 | cells_to_finish_movement_ = 0; 31 | return; 32 | } 33 | 34 | if (dynamic_cast(this) != nullptr) { 35 | for (auto& object : tanks) { 36 | if (object->GetCoordinates() == new_cell) { 37 | cells_to_finish_movement_ = 0; 38 | object->cells_to_finish_movement_ = 0; 39 | return; 40 | } 41 | } 42 | for (auto& object : *objects_copies_) { 43 | if (object.second == new_cell) { 44 | cells_to_finish_movement_ = 0; 45 | object.first->cells_to_finish_movement_ = 0; 46 | return; 47 | } 48 | } 49 | current_speed_ = std::max( 50 | static_cast(map_->GetField(cell_)) * basic_speed_, 51 | static_cast(map_->GetField(new_cell)) * basic_speed_); 52 | } 53 | 54 | if (auto portal = std::dynamic_pointer_cast( 55 | (*objects)[new_cell.x][new_cell.y]); 56 | portal != nullptr && dynamic_cast(this) == nullptr) { 57 | Coordinates cells = GetNewPortalCells(portal->GetNewCell(), new_cell); 58 | 59 | if (map_->GetField(cells) == CellType::Wall) { 60 | return; 61 | } 62 | 63 | for (auto& object : tanks) { 64 | if (object->GetCoordinates() == cells) { 65 | return; 66 | } 67 | } 68 | objects_copies_->emplace_back( 69 | std::static_pointer_cast(shared_from_this()), 70 | Coordinates{cells.x, cells.y}); 71 | copy_existence_ = true; 72 | 73 | new_cell = cells; 74 | } 75 | 76 | if ((*objects)[new_cell.x][new_cell.y] != nullptr && 77 | std::dynamic_pointer_cast( 78 | (*objects)[new_cell.x][new_cell.y]) == nullptr) { 79 | if (std::dynamic_pointer_cast( 80 | (*objects)[new_cell.x][new_cell.y]) != nullptr) { 81 | current_speed_ *= 2; 82 | } 83 | if (auto tank = dynamic_cast(this); tank != nullptr) { 84 | if (std::dynamic_pointer_cast( 85 | (*objects)[new_cell.x][new_cell.y]) != nullptr) { 86 | tank->IncreaseHealth( 87 | std::min(35, tank->GetMaxHealth() - tank->GetCurrentHealth())); 88 | } else if (std::dynamic_pointer_cast( 89 | (*objects)[new_cell.x][new_cell.y]) != nullptr) { 90 | tank->IncreaseCharge(); 91 | } 92 | } 93 | if (auto rocket = dynamic_cast(this); rocket != nullptr) { 94 | cells_to_finish_movement_ = 0; 95 | if (rocket->CanBreakObstacles()) { 96 | (*objects)[new_cell.x][new_cell.y] = nullptr; 97 | } 98 | return; 99 | } 100 | (*objects)[new_cell.x][new_cell.y] = nullptr; 101 | } 102 | 103 | new_cell = old_cell; 104 | previous_cell_ = cell_; 105 | cell_ = new_cell; 106 | time_to_finish_movement_ = current_speed_; 107 | cells_to_finish_movement_ = number_of_cells - 1; 108 | } 109 | 110 | void Movable::Move(const int milliseconds_passed) { 111 | time_to_finish_movement_ = 112 | std::max(time_to_finish_movement_ - milliseconds_passed, 0); 113 | } 114 | 115 | void Movable::TurnReverseOn() { 116 | linear_movement_direction_ = -1; 117 | } 118 | 119 | void Movable::TurnReverseOff() { 120 | linear_movement_direction_ = 1; 121 | } 122 | 123 | void Movable::StartRotation() { 124 | current_speed_ = 125 | static_cast(map_->GetField(cell_)) * basic_speed_; 126 | if (rotation_direction_ == 1) { 127 | SwitchToNextDirection(); 128 | } else { 129 | SwitchToPreviousDirection(); 130 | } 131 | time_to_finish_rotation_ = current_speed_; 132 | } 133 | 134 | void Movable::Rotate(int milliseconds_passed) { 135 | time_to_finish_rotation_ -= milliseconds_passed; 136 | time_to_finish_rotation_ = std::max(time_to_finish_rotation_, 0); 137 | } 138 | 139 | void Movable::TurnRotationReverseOn() { 140 | rotation_direction_ = -1; 141 | } 142 | 143 | void Movable::TurnRotationReverseOff() { 144 | rotation_direction_ = 1; 145 | } 146 | 147 | void Movable::UpdateCoordinates(Coordinates cell) { 148 | if (cell_ != cell && GetTimeToFinishMovement() == 0) { 149 | cell_ = cell; 150 | copy_existence_ = false; 151 | } 152 | 153 | width_ = map_->GetCellWidth(); 154 | height_ = map_->GetCellHeight(); 155 | 156 | double movement_proportion = 1.0 * time_to_finish_movement_ / current_speed_; 157 | if (copy_existence_ && cell_ == cell) { 158 | opacity_ = movement_proportion; 159 | previous_opacity_ = opacity_; 160 | } else if (copy_existence_) { 161 | opacity_ = 1 - movement_proportion; 162 | } 163 | 164 | previous_upper_left_cell_coordinates_ = upper_left_cell_coordinates_; 165 | upper_left_cell_coordinates_.x = map_->GetUpperLeftCellCoordinates().x 166 | + (width_ * cell.x) - linear_movement_direction_ * movement_proportion 167 | * (directions_[1] * width_ - directions_[3] * width_); 168 | 169 | upper_left_cell_coordinates_.y = map_->GetUpperLeftCellCoordinates().y 170 | + (height_ * cell.y) - linear_movement_direction_ * movement_proportion 171 | * (directions_[2] * height_ - directions_[0] * height_); 172 | 173 | if (map_->GetField(cell_) == CellType::Forest) { 174 | if (movement_proportion <= 0.5) { 175 | opacity_ = constants::kOpacityLevel; 176 | } 177 | } else if (map_->GetField(previous_cell_) == CellType::Forest) { 178 | if (movement_proportion <= 0.5) { 179 | opacity_ = 1; 180 | } 181 | } else if (!copy_existence_) { 182 | opacity_ = 1; 183 | } 184 | 185 | double rotation_proportion = 186 | static_cast(time_to_finish_rotation_) / current_speed_; 187 | rotate_degree_ = GetDirectionAsInt() * 90 - 188 | rotation_direction_ * 90 * rotation_proportion; 189 | rotate_degree_ %= 360; 190 | 191 | RescaleImage(); 192 | } 193 | 194 | void Movable::ReturnToOriginal() { 195 | upper_left_cell_coordinates_ = previous_upper_left_cell_coordinates_; 196 | opacity_ = previous_opacity_; 197 | } 198 | 199 | int Movable::GetTimeToFinishMovement() const { 200 | return time_to_finish_movement_; 201 | } 202 | 203 | int Movable::GetCellsToFinishMovement() const { 204 | return cells_to_finish_movement_; 205 | } 206 | 207 | int Movable::GetTimeToFinishRotation() const { 208 | return time_to_finish_rotation_; 209 | } 210 | 211 | bool Movable::IsMovingOrRotating() const { 212 | return (GetTimeToFinishMovement() > 0 || GetTimeToFinishRotation() > 0 || 213 | GetCellsToFinishMovement() > 0); 214 | } 215 | 216 | int Movable::GetDirectionAsInt() const { 217 | if (directions_[0] == 1) { 218 | return 0; 219 | } 220 | if (directions_[1] == 1) { 221 | return 1; 222 | } 223 | if (directions_[2] == 1) { 224 | return 2; 225 | } 226 | return 3; 227 | } 228 | 229 | Direction Movable::GetDirection() const { 230 | return static_cast(GetDirectionAsInt()); 231 | } 232 | 233 | bool Movable::HaveObjectsCollided(const std::shared_ptr& obj1, 234 | const std::shared_ptr& obj2) { 235 | if (obj1 == obj2 || Movable::CanRocketKillTank(obj1, obj2)) { 236 | return false; 237 | } 238 | 239 | if (obj1->GetUpperLeftCellCoordinates().x >= 240 | obj2->GetUpperLeftCellCoordinates().x + obj2->GetWidth()) { 241 | return false; 242 | } 243 | if (obj1->GetUpperLeftCellCoordinates().x + obj1->GetWidth() <= 244 | obj2->GetUpperLeftCellCoordinates().x) { 245 | return false; 246 | } 247 | if (obj1->GetUpperLeftCellCoordinates().y >= 248 | obj2->GetUpperLeftCellCoordinates().y + obj2->GetHeight()) { 249 | return false; 250 | } 251 | return obj1->GetUpperLeftCellCoordinates().y + obj1->GetHeight() > 252 | obj2->GetUpperLeftCellCoordinates().y; 253 | } 254 | 255 | bool Movable::CanRocketKillTank(const std::shared_ptr& rocket, 256 | const std::shared_ptr& tank) { 257 | auto casted_rocket = std::dynamic_pointer_cast(rocket); 258 | auto casted_tank = std::dynamic_pointer_cast(tank); 259 | if (casted_rocket != nullptr && casted_tank != nullptr) { 260 | return casted_tank == casted_rocket->GetAttachedTank(); 261 | } 262 | return false; 263 | } 264 | 265 | Direction Movable::GetDirectionFromString(const std::string& direction) { 266 | static std::unordered_map map = { 267 | {"up", Direction::Up}, {"down", Direction::Down}, 268 | {"left", Direction::Left}, {"right", Direction::Right}}; 269 | return map[direction]; 270 | } 271 | 272 | void Movable::SwitchToNextDirection() { 273 | int current_direction = GetDirectionAsInt(); 274 | directions_[current_direction] = 0; 275 | directions_[(current_direction + 1) % 4] = 1; 276 | } 277 | 278 | void Movable::SwitchToPreviousDirection() { 279 | int current_direction = GetDirectionAsInt(); 280 | directions_[current_direction] = 0; 281 | directions_[(current_direction + 3) % 4] = 1; 282 | } 283 | 284 | Coordinates Movable::GetNewPortalCells(Coordinates portal_coordinates, 285 | Coordinates new_cells) const { 286 | if (new_cells.y == cell_.y - 1) { 287 | --portal_coordinates.y; 288 | } else if (new_cells.y == cell_.y + 1) { 289 | ++portal_coordinates.y; 290 | } else if (new_cells.x == cell_.x - 1) { 291 | --portal_coordinates.x; 292 | } else { 293 | ++portal_coordinates.x; 294 | } 295 | return portal_coordinates; 296 | } 297 | -------------------------------------------------------------------------------- /movable_objects/movable.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVABLE_OBJECTS_MOVABLE_H_ 2 | #define MOVABLE_OBJECTS_MOVABLE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../game_core/map.h" 15 | #include "game_core/game_object.h" 16 | #include "../static_objects/static_object.h" 17 | 18 | enum class Direction { 19 | Up = 0, 20 | Right = 1, 21 | Down = 2, 22 | Left = 3 23 | }; 24 | 25 | // Forward declaration to use this class in StartMovement function. 26 | class Tank; 27 | 28 | class Movable : public std::enable_shared_from_this, 29 | public GameObject { 30 | public: 31 | Movable(std::shared_ptr map, const QString& texture_path, 32 | Coordinates cell, Direction direction, int speed); 33 | ~Movable() override = default; 34 | 35 | // Defines current move characteristics by initializing several variables, 36 | // including current speed, number of cells to finish movement, time to 37 | // finish movement, copy cell coordinates in case of moving through portal. 38 | // These variables are initialized accordingly to the current state of the 39 | // object, e.g. obstacles or walls in front of him. 40 | virtual void StartMovement( 41 | const std::list>& tanks, 42 | std::list, Coordinates>>* objects_copies_, 43 | std::vector>>* objects, 44 | int number_of_cells); 45 | 46 | // Updates state of current move by updating time to finish movement variable. 47 | virtual void Move(int milliseconds_passed); 48 | 49 | // Changes current move direction to the reverse. 50 | virtual void TurnReverseOn(); 51 | // Changes current move direction from the reverse to normal. 52 | virtual void TurnReverseOff(); 53 | 54 | // Defines current rotation characteristics by initializing several variables, 55 | // including current rotation speed, time to finish rotation. 56 | // These variables are initialized accordingly to the current state of the 57 | // object, e.g. obstacles or walls in front of him. 58 | virtual void StartRotation(); 59 | 60 | // Updates state of current rotation by updating time to finish rotation 61 | // variable. 62 | virtual void Rotate(int milliseconds_passed); 63 | 64 | // Changes current rotation direction to the reverse. 65 | virtual void TurnRotationReverseOn(); 66 | 67 | // Changes current rotation direction from the reverse to normal. 68 | virtual void TurnRotationReverseOff(); 69 | 70 | // Updates object's coordinates on the map according to the cell numbers and 71 | // the state of movement or rotation. 72 | virtual void UpdateCoordinates(Coordinates cell); 73 | 74 | // Changes object's coordinates from current to previous. It's used in 75 | // portals implementation. To draw one tank in two places at the same time 76 | // we firstly draw it in the first place, then change its coordinates to copy 77 | // coordinates and draw it once again in the second place, then return to 78 | // original coordinates. 79 | virtual void ReturnToOriginal(); 80 | 81 | int GetTimeToFinishMovement() const; 82 | int GetCellsToFinishMovement() const; 83 | int GetTimeToFinishRotation() const; 84 | bool IsMovingOrRotating() const; 85 | 86 | int GetDirectionAsInt() const; 87 | Direction GetDirection() const; 88 | 89 | // Checks if given objects have been collided, i.e. they have common points 90 | // on the screen. Compares their coordinates for that. 91 | static bool HaveObjectsCollided(const std::shared_ptr& obj1, 92 | const std::shared_ptr& obj2); 93 | 94 | // Checks if given rocket has been released by the given tank. The result of 95 | // this function is used when we decide if we need to reduce tank's health 96 | // (rockets released by the tank can't reduce its own health). 97 | static bool CanRocketKillTank(const std::shared_ptr& rocket, 98 | const std::shared_ptr& tank); 99 | 100 | // Returns direction object corresponding to the string. 101 | static Direction GetDirectionFromString(const std::string& direction); 102 | 103 | protected: 104 | void SwitchToNextDirection(); 105 | void SwitchToPreviousDirection(); 106 | 107 | Coordinates GetNewPortalCells(Coordinates portal_coordinates, 108 | Coordinates new_cells) const; 109 | 110 | protected: 111 | Coordinates previous_cell_; 112 | Coordinates previous_upper_left_cell_coordinates_; 113 | 114 | // Item at index i is responsible for the i-th direction in Direction enum 115 | // class. 116 | std::vector directions_ = {0, 0, 0, 0}; 117 | 118 | int current_speed_; 119 | const int basic_speed_; 120 | 121 | int time_to_finish_movement_ = 0; 122 | int cells_to_finish_movement_ = 0; 123 | int rotate_degree_; 124 | int time_to_finish_rotation_ = 0; 125 | 126 | // Responsible for the direction of movement along the line 127 | // (vertically or horizontally). 128 | // Equals to 1 when the movable object going forward, and -1 otherwise. 129 | int linear_movement_direction_ = 1; 130 | 131 | // Equals to 1 when the movable object rotates clockwise, and -1 otherwise. 132 | int rotation_direction_ = 1; 133 | 134 | double opacity_ = 1; 135 | double previous_opacity_ = 1; 136 | 137 | bool copy_existence_ = false; 138 | }; 139 | 140 | #endif // MOVABLE_OBJECTS_MOVABLE_H_ 141 | -------------------------------------------------------------------------------- /movable_objects/rocket.cpp: -------------------------------------------------------------------------------- 1 | #include "rocket.h" 2 | 3 | Rocket::Rocket(std::shared_ptr map, std::shared_ptr tank, 4 | RocketParameters parameters) 5 | : Movable(std::move(map), ":/textures/rocket.png", tank->GetCoordinates(), 6 | tank->GetDirection(), parameters.speed), 7 | tank_(std::move(tank)), 8 | power_(parameters.power), 9 | can_break_obstacle_(parameters.can_break_obstacle) {} 10 | 11 | void Rocket::Draw(QPainter* painter) { 12 | painter->save(); 13 | painter->translate( 14 | upper_left_cell_coordinates_.x + width_ / 2, 15 | upper_left_cell_coordinates_.y + height_ / 2); 16 | painter->setOpacity(opacity_); 17 | painter->rotate(rotate_degree_); 18 | painter->drawPixmap(-width_ / 2, 19 | -height_ / 2, 20 | scaled_pixmap_); 21 | painter->restore(); 22 | } 23 | 24 | std::shared_ptr Rocket::GetAttachedTank() const { 25 | return tank_; 26 | } 27 | 28 | int Rocket::GetPower() const { 29 | return power_; 30 | } 31 | 32 | bool Rocket::CanBreakObstacles() const { 33 | return can_break_obstacle_; 34 | } 35 | -------------------------------------------------------------------------------- /movable_objects/rocket.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVABLE_OBJECTS_ROCKET_H_ 2 | #define MOVABLE_OBJECTS_ROCKET_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "tank.h" 9 | 10 | struct RocketParameters { 11 | int power; 12 | int speed; 13 | bool can_break_obstacle; 14 | }; 15 | 16 | class Rocket : public Movable { 17 | public: 18 | Rocket(std::shared_ptr map, std::shared_ptr tank, 19 | RocketParameters parameters); 20 | ~Rocket() override = default; 21 | 22 | void Draw(QPainter* painter) override; 23 | 24 | std::shared_ptr GetAttachedTank() const; 25 | int GetPower() const; 26 | bool CanBreakObstacles() const; 27 | 28 | private: 29 | const std::shared_ptr tank_; 30 | int power_; 31 | bool can_break_obstacle_; 32 | }; 33 | 34 | #endif // MOVABLE_OBJECTS_ROCKET_H_ 35 | -------------------------------------------------------------------------------- /movable_objects/tank.cpp: -------------------------------------------------------------------------------- 1 | #include "tank.h" 2 | 3 | #include 4 | 5 | std::mt19937 Tank::random_generator_ = std::mt19937( 6 | std::chrono::system_clock::now().time_since_epoch().count()); 7 | 8 | Tank::Tank(std::shared_ptr map, const QString& texture_path, 9 | Coordinates init_cell, TankParameters parameters, 10 | Direction direction) 11 | : Movable(std::move(map), texture_path, init_cell, direction, 12 | parameters.speed), 13 | rate_of_fire_(parameters.rate_of_fire), 14 | time_since_last_shot_(0), 15 | current_type_of_charge_(0), 16 | current_charge_({parameters.max_light_charge, 17 | parameters.max_medium_charge, 18 | parameters.max_hard_charge}), 19 | current_health_(parameters.max_health), 20 | max_charge_({parameters.max_light_charge, 21 | parameters.max_medium_charge, 22 | parameters.max_hard_charge}), 23 | max_health_(parameters.max_health) {} 24 | 25 | void Tank::Draw(QPainter* painter) { 26 | painter->save(); 27 | painter->translate( 28 | upper_left_cell_coordinates_.x + width_ / 2, 29 | upper_left_cell_coordinates_.y + height_ / 2); 30 | painter->rotate(rotate_degree_); 31 | painter->setOpacity(opacity_); 32 | painter->drawPixmap(-width_ / 2, -height_ / 2, 33 | scaled_pixmap_); 34 | painter->restore(); 35 | DrawHealth(painter); 36 | } 37 | 38 | void Tank::DrawHealth(QPainter* painter) { 39 | painter->save(); 40 | painter->translate( 41 | upper_left_cell_coordinates_.x + width_ / 2, 42 | upper_left_cell_coordinates_.y + height_ / 4); 43 | if (current_health_ > 0.3 * max_health_ * 0.3) { 44 | painter->setBrush(Qt::blue); 45 | } else { 46 | painter->setBrush(Qt::red); 47 | } 48 | painter->drawRect(-width_ / 2, 49 | 5 * height_ / 8, 50 | current_health_ * width_ / max_health_, 51 | height_ / 8); 52 | painter->setBrush(Qt::white); 53 | painter->drawRect( 54 | -width_ / 2 + current_health_ * width_ / max_health_, 55 | 5 * height_ / 8, 56 | (max_health_ - current_health_) * width_ / max_health_, 57 | height_ / 8); 58 | painter->restore(); 59 | } 60 | 61 | bool Tank::IsAbleToShoot() const { 62 | return (time_since_last_shot_ >= rate_of_fire_) && 63 | ((current_type_of_charge_ == 0 && current_charge_[0] > 0) || 64 | (current_type_of_charge_ == 1 && current_charge_[1] > 0) || 65 | (current_type_of_charge_ == 2 && current_charge_[2] > 0)); 66 | } 67 | 68 | void Tank::IncreaseTimeSinceLastShot(int delta) { 69 | time_since_last_shot_ = 70 | std::min(time_since_last_shot_ + delta, rate_of_fire_); 71 | } 72 | 73 | void Tank::SetZeroTimeFromLastShot() { 74 | time_since_last_shot_ = 0; 75 | } 76 | 77 | int Tank::GetCurrentHealth() const { 78 | return current_health_; 79 | } 80 | 81 | int Tank::GetMaxHealth() const { 82 | return max_health_; 83 | } 84 | 85 | void Tank::DecreaseHealth(int health) { 86 | current_health_ -= health; 87 | } 88 | 89 | void Tank::IncreaseHealth(int health) { 90 | current_health_ += health; 91 | } 92 | 93 | bool Tank::IsDead() const { 94 | return current_health_ <= 0; 95 | } 96 | 97 | int Tank::GetTimeSinceLastShot() const { 98 | return time_since_last_shot_; 99 | } 100 | 101 | int Tank::GetRateOfFire() const { 102 | return rate_of_fire_; 103 | } 104 | 105 | void Tank::ChangeTypeOfCharge(int type) { 106 | current_type_of_charge_ = type; 107 | } 108 | 109 | void Tank::DecreaseCharge(int type, int charge) { 110 | current_charge_[type] -= charge; 111 | } 112 | 113 | void Tank::IncreaseCharge() { 114 | int type = random_generator_() % 3; 115 | current_charge_[type] += 116 | std::min(10 - 2 * type, max_charge_[type] - current_charge_[type]); 117 | } 118 | 119 | int Tank::GetTypeOfCharge() const { 120 | return current_type_of_charge_; 121 | } 122 | 123 | int Tank::GetCurrentCharge(int type) const { 124 | return current_charge_[type]; 125 | } 126 | 127 | ChargeState Tank::GetChargeState() const { 128 | if (current_charge_[current_type_of_charge_] == 0) { 129 | return ChargeState::Empty; 130 | } else if (current_charge_[current_type_of_charge_] <= 131 | max_charge_[current_type_of_charge_] / 2) { 132 | return ChargeState::LessThanHalf; 133 | } 134 | return ChargeState::MoreThanHalf; 135 | } 136 | -------------------------------------------------------------------------------- /movable_objects/tank.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVABLE_OBJECTS_TANK_H_ 2 | #define MOVABLE_OBJECTS_TANK_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../game_core/map.h" 13 | #include "movable.h" 14 | 15 | struct TankParameters { 16 | int speed; 17 | int rate_of_fire; 18 | int max_health; 19 | int max_light_charge; 20 | int max_medium_charge; 21 | int max_hard_charge; 22 | }; 23 | 24 | enum class ChargeState { 25 | Empty = 0, 26 | LessThanHalf = 1, 27 | MoreThanHalf = 2 28 | }; 29 | 30 | class Tank : public Movable { 31 | public: 32 | Tank(std::shared_ptr map, const QString& texture_path, 33 | Coordinates init_cell, TankParameters parameters, Direction direction); 34 | ~Tank() override = default; 35 | 36 | void Draw(QPainter* painter) override; 37 | virtual void DrawHealth(QPainter* painter); 38 | 39 | virtual bool IsAbleToShoot() const; 40 | virtual void IncreaseTimeSinceLastShot(int delta); 41 | virtual void SetZeroTimeFromLastShot(); 42 | 43 | virtual int GetTimeSinceLastShot() const; 44 | virtual int GetRateOfFire() const; 45 | 46 | virtual void DecreaseHealth(int health); 47 | virtual void IncreaseHealth(int health); 48 | 49 | virtual int GetCurrentHealth() const; 50 | virtual int GetMaxHealth() const; 51 | virtual bool IsDead() const; 52 | 53 | void ChangeTypeOfCharge(int type); 54 | void DecreaseCharge(int type, int charge = 1); 55 | void IncreaseCharge(); 56 | 57 | int GetTypeOfCharge() const; 58 | int GetCurrentCharge(int type) const; 59 | ChargeState GetChargeState() const; 60 | 61 | protected: 62 | int rate_of_fire_; 63 | int time_since_last_shot_; 64 | 65 | int current_type_of_charge_; 66 | std::vector current_charge_; 67 | 68 | int current_health_; 69 | 70 | const std::vector max_charge_; 71 | const int max_health_; 72 | 73 | static std::mt19937 random_generator_; 74 | }; 75 | 76 | #endif // MOVABLE_OBJECTS_TANK_H_ 77 | -------------------------------------------------------------------------------- /resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/app_icon.ico -------------------------------------------------------------------------------- /resources/game_objects_data/rockets.json: -------------------------------------------------------------------------------- 1 | { 2 | "rockets": [ 3 | { 4 | "speed": 100, 5 | "power": 7, 6 | "can_break_obstacle": true 7 | }, 8 | { 9 | "speed": 200, 10 | "power": 15, 11 | "can_break_obstacle": true 12 | }, 13 | { 14 | "speed": 300, 15 | "power": 30, 16 | "can_break_obstacle": false 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /resources/game_objects_data/tanks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tanks": [ 3 | { 4 | "speed": 500, 5 | "rate_of_fire": 300, 6 | "max_health": 200, 7 | "max_light_charge" : 30, 8 | "max_medium_charge" : 15, 9 | "max_hard_charge" : 5 10 | }, 11 | { 12 | "speed": 350, 13 | "rate_of_fire": 450, 14 | "max_health": 150, 15 | "max_light_charge" : 35, 16 | "max_medium_charge" : 20, 17 | "max_hard_charge" : 7 18 | }, 19 | { 20 | "speed": 250, 21 | "rate_of_fire": 300, 22 | "max_health": 250, 23 | "max_light_charge" : 40, 24 | "max_medium_charge" : 20, 25 | "max_hard_charge" : 10 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /resources/maps/map1.json: -------------------------------------------------------------------------------- 1 | { 2 | "map": { 3 | "width": 15, 4 | "height": 15, 5 | "map": [ 6 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 7 | [0, 1, 1, 1, 1, 3, 3, 3, 3, 3, 0, 1, 1, 1, 0], 8 | [0, 1, 1, 1, 1, 1, 3, 3, 3, 3, 0, 1, 1, 1, 0], 9 | [0, 0, 0, 0, 0, 1, 1, 3, 3, 3, 0, 1, 1, 1, 0], 10 | [0, 1, 1, 1, 0, 1, 1, 1, 3, 3, 0, 1, 1, 1, 0], 11 | [0, 1, 1, 1, 0, 1, 1, 1, 1, 3, 0, 1, 1, 1, 0], 12 | [0, 1, 1, 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 0, 0], 13 | [0, 1, 1, 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 0, 0], 14 | [0, 1, 1, 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 0, 0], 15 | [0, 1, 1, 1, 0, 1, 1, 1, 1, 2, 0, 1, 1, 1, 0], 16 | [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 0, 1, 1, 1, 0], 17 | [0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 0, 1, 1, 1, 0], 18 | [0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 1, 1, 1, 0], 19 | [0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 1, 1, 1, 0], 20 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 21 | ] 22 | }, 23 | "player_tank": { 24 | "initial_cell_x": 1, 25 | "initial_cell_y": 2, 26 | "initial_direction": "up" 27 | }, 28 | "difficulty": [ 29 | { 30 | "bots": [ 31 | { 32 | "type": "standard", 33 | "initial_cell_x": 2, 34 | "initial_cell_y": 7, 35 | "moving_length": 1, 36 | "number_of_turns": 1, 37 | "side_rotation_frequency": 2, 38 | "initial_direction": "right" 39 | }, 40 | { 41 | "type": "standard", 42 | "initial_cell_x": 8, 43 | "initial_cell_y": 12, 44 | "moving_length": 5, 45 | "number_of_turns": 1, 46 | "side_rotation_frequency": 2, 47 | "initial_direction": "down" 48 | }, 49 | { 50 | "type": "standard", 51 | "initial_cell_x": 13, 52 | "initial_cell_y": 3, 53 | "moving_length": 3, 54 | "number_of_turns": 1, 55 | "side_rotation_frequency": 2, 56 | "initial_direction": "left" 57 | } 58 | ], 59 | "obstacles": [ 60 | [4, 6], 61 | [4, 7], 62 | [5, 8], 63 | [10, 6], 64 | [10, 7], 65 | [9, 8], 66 | [5, 3], 67 | [6, 3], 68 | [1, 11], 69 | [2, 11], 70 | [3, 11], 71 | [6, 10], 72 | [6, 11], 73 | [7, 10], 74 | [8, 10] 75 | ], 76 | "portals": [ 77 | [1, 1, 13, 13] 78 | ] 79 | }, 80 | { 81 | "bots": [ 82 | { 83 | "type": "standard", 84 | "initial_cell_x": 2, 85 | "initial_cell_y": 7, 86 | "moving_length": 1, 87 | "number_of_turns": 1, 88 | "side_rotation_frequency": 2, 89 | "initial_direction": "right" 90 | }, 91 | { 92 | "type": "standard", 93 | "initial_cell_x": 8, 94 | "initial_cell_y": 12, 95 | "moving_length": 5, 96 | "number_of_turns": 1, 97 | "side_rotation_frequency": 2, 98 | "initial_direction": "down" 99 | }, 100 | { 101 | "type": "standard", 102 | "initial_cell_x": 13, 103 | "initial_cell_y": 3, 104 | "moving_length": 3, 105 | "number_of_turns": 1, 106 | "side_rotation_frequency": 2, 107 | "initial_direction": "left" 108 | }, 109 | { 110 | "type": "improved", 111 | "initial_cell_x": 8, 112 | "initial_cell_y": 3, 113 | "moving_length": 4, 114 | "number_of_turns": 1, 115 | "side_rotation_frequency": 1, 116 | "initial_direction": "down" 117 | }, 118 | { 119 | "type": "improved", 120 | "initial_cell_x": 13, 121 | "initial_cell_y": 13, 122 | "moving_length": 10, 123 | "number_of_turns": 1, 124 | "side_rotation_frequency": 4, 125 | "initial_direction": "up" 126 | } 127 | ], 128 | "obstacles": [ 129 | [4, 6], 130 | [4, 7], 131 | [5, 8], 132 | [10, 6], 133 | [10, 7], 134 | [9, 8], 135 | [5, 3], 136 | [6, 3], 137 | [1, 11], 138 | [2, 11], 139 | [3, 11], 140 | [6, 10], 141 | [6, 11], 142 | [7, 10], 143 | [8, 10], 144 | [7, 4], 145 | [8, 4], 146 | [12, 13], 147 | [12, 12], 148 | [13, 12] 149 | ], 150 | "portals": [ 151 | [7, 2, 3, 12] 152 | ] 153 | }, 154 | { 155 | "bots": [ 156 | { 157 | "type": "standard", 158 | "initial_cell_x": 2, 159 | "initial_cell_y": 7, 160 | "moving_length": 1, 161 | "number_of_turns": 1, 162 | "side_rotation_frequency": 2, 163 | "initial_direction": "right" 164 | }, 165 | { 166 | "type": "standard", 167 | "initial_cell_x": 8, 168 | "initial_cell_y": 12, 169 | "moving_length": 5, 170 | "number_of_turns": 1, 171 | "side_rotation_frequency": 3, 172 | "initial_direction": "up" 173 | }, 174 | { 175 | "type": "standard", 176 | "initial_cell_x": 4, 177 | "initial_cell_y": 2, 178 | "moving_length": 8, 179 | "number_of_turns": 1, 180 | "side_rotation_frequency": 2, 181 | "initial_direction": "left" 182 | }, 183 | { 184 | "type": "improved", 185 | "initial_cell_x": 13, 186 | "initial_cell_y": 3, 187 | "moving_length": 3, 188 | "number_of_turns": 1, 189 | "side_rotation_frequency": 2, 190 | "initial_direction": "left" 191 | }, 192 | { 193 | "type": "improved", 194 | "initial_cell_x": 8, 195 | "initial_cell_y": 3, 196 | "moving_length": 10, 197 | "number_of_turns": 1, 198 | "side_rotation_frequency": 4, 199 | "initial_direction": "down" 200 | }, 201 | { 202 | "type": "clever", 203 | "initial_cell_x": 2, 204 | "initial_cell_y": 13, 205 | "moving_length": 0, 206 | "number_of_turns": 1, 207 | "side_rotation_frequency": 10, 208 | "initial_direction": "left" 209 | }, 210 | { 211 | "type": "clever", 212 | "initial_cell_x": 12, 213 | "initial_cell_y": 1, 214 | "moving_length": 5, 215 | "number_of_turns": 1, 216 | "side_rotation_frequency": 10, 217 | "initial_direction": "up" 218 | } 219 | ], 220 | "obstacles": [ 221 | [4, 6], 222 | [4, 7], 223 | [5, 8], 224 | [10, 6], 225 | [10, 7], 226 | [9, 7], 227 | [5, 3], 228 | [6, 3], 229 | [1, 11], 230 | [2, 11], 231 | [3, 11], 232 | [7, 4], 233 | [8, 4], 234 | [12, 13], 235 | [12, 12], 236 | [13, 12], 237 | [12, 4], 238 | [13, 4], 239 | [4, 1], 240 | [5, 1], 241 | [5, 2], 242 | [12, 2], 243 | [13, 2] 244 | ], 245 | "portals": [ 246 | [1, 1, 13, 13], 247 | [7, 2, 3, 12] 248 | ] 249 | } 250 | ] 251 | } -------------------------------------------------------------------------------- /resources/maps/map2.json: -------------------------------------------------------------------------------- 1 | { 2 | "map": { 3 | "width": 15, 4 | "height": 15, 5 | "map": [ 6 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 7 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, 4, 0], 8 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 4, 4, 0], 9 | [0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 0, 1, 1, 1, 0], 10 | [0, 2, 2, 2, 2, 1, 1, 2, 2, 2, 0, 1, 1, 1, 0], 11 | [0, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0], 12 | [0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], 13 | [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], 14 | [0, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], 15 | [0, 3, 3, 3, 3, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0], 16 | [0, 3, 3, 3, 3, 1, 1, 3, 3, 3, 0, 1, 1, 1, 0], 17 | [0, 0, 0, 0, 0, 1, 1, 1, 3, 3, 0, 1, 1, 1, 0], 18 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0, 1, 4, 4, 0], 19 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, 4, 0], 20 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 21 | ] 22 | }, 23 | "player_tank": { 24 | "initial_cell_x": 7, 25 | "initial_cell_y": 5, 26 | "initial_direction": "down" 27 | }, 28 | "difficulty": [ 29 | { 30 | "bots": [ 31 | { 32 | "type": "standard", 33 | "initial_cell_x": 2, 34 | "initial_cell_y": 2, 35 | "moving_length": 2, 36 | "number_of_turns": 1, 37 | "side_rotation_frequency": 1, 38 | "initial_direction": "right" 39 | }, 40 | { 41 | "type": "standard", 42 | "initial_cell_x": 2, 43 | "initial_cell_y": 9, 44 | "moving_length": 3, 45 | "number_of_turns": 1, 46 | "side_rotation_frequency": 2, 47 | "initial_direction": "up" 48 | }, 49 | { 50 | "type": "standard", 51 | "initial_cell_x": 9, 52 | "initial_cell_y": 13, 53 | "moving_length": 10, 54 | "number_of_turns": 1, 55 | "side_rotation_frequency": 2, 56 | "initial_direction": "left" 57 | }, 58 | { 59 | "type": "standard", 60 | "initial_cell_x": 13, 61 | "initial_cell_y": 7, 62 | "moving_length": 1, 63 | "number_of_turns": 1, 64 | "side_rotation_frequency": 1, 65 | "initial_direction": "up" 66 | } 67 | ], 68 | "obstacles": [ 69 | [4, 5], 70 | [4, 6], 71 | [2, 7], 72 | [2, 8], 73 | [6, 5], 74 | [7, 7], 75 | [6, 12], 76 | [10, 6], 77 | [11, 7], 78 | [12, 8] 79 | ], 80 | "portals": [ 81 | [5, 2, 9, 2] 82 | ] 83 | }, 84 | { 85 | "bots": [ 86 | { 87 | "type": "standard", 88 | "initial_cell_x": 2, 89 | "initial_cell_y": 2, 90 | "moving_length": 2, 91 | "number_of_turns": 1, 92 | "side_rotation_frequency": 1, 93 | "initial_direction": "right" 94 | }, 95 | { 96 | "type": "standard", 97 | "initial_cell_x": 2, 98 | "initial_cell_y": 9, 99 | "moving_length": 3, 100 | "number_of_turns": 1, 101 | "side_rotation_frequency": 2, 102 | "initial_direction": "up" 103 | }, 104 | { 105 | "type": "standard", 106 | "initial_cell_x": 9, 107 | "initial_cell_y": 13, 108 | "moving_length": 10, 109 | "number_of_turns": 1, 110 | "side_rotation_frequency": 2, 111 | "initial_direction": "left" 112 | }, 113 | { 114 | "type": "standard", 115 | "initial_cell_x": 13, 116 | "initial_cell_y": 7, 117 | "moving_length": 1, 118 | "number_of_turns": 1, 119 | "side_rotation_frequency": 1, 120 | "initial_direction": "up" 121 | }, 122 | { 123 | "type": "improved", 124 | "initial_cell_x": 2, 125 | "initial_cell_y": 13, 126 | "moving_length": 4, 127 | "number_of_turns": 1, 128 | "side_rotation_frequency": 4, 129 | "initial_direction": "up" 130 | }, 131 | { 132 | "type": "improved", 133 | "initial_cell_x": 12, 134 | "initial_cell_y": 2, 135 | "moving_length": 4, 136 | "number_of_turns": 2, 137 | "side_rotation_frequency": 4, 138 | "initial_direction": "down" 139 | } 140 | ], 141 | "obstacles": [ 142 | [4, 5], 143 | [4, 6], 144 | [2, 7], 145 | [2, 8], 146 | [6, 5], 147 | [7, 7], 148 | [6, 12], 149 | [10, 6], 150 | [11, 7], 151 | [12, 8], 152 | [9, 5], 153 | [9, 6], 154 | [7, 12], 155 | [7, 13], 156 | [5, 12], 157 | [4, 12] 158 | ], 159 | "portals": [ 160 | [4, 2, 10, 2] 161 | ] 162 | }, 163 | { 164 | "bots": [ 165 | { 166 | "type": "standard", 167 | "initial_cell_x": 2, 168 | "initial_cell_y": 2, 169 | "moving_length": 2, 170 | "number_of_turns": 1, 171 | "side_rotation_frequency": 1, 172 | "initial_direction": "right" 173 | }, 174 | { 175 | "type": "standard", 176 | "initial_cell_x": 2, 177 | "initial_cell_y": 9, 178 | "moving_length": 3, 179 | "number_of_turns": 1, 180 | "side_rotation_frequency": 2, 181 | "initial_direction": "up" 182 | }, 183 | { 184 | "type": "standard", 185 | "initial_cell_x": 9, 186 | "initial_cell_y": 13, 187 | "moving_length": 10, 188 | "number_of_turns": 1, 189 | "side_rotation_frequency": 2, 190 | "initial_direction": "left" 191 | }, 192 | { 193 | "type": "improved", 194 | "initial_cell_x": 13, 195 | "initial_cell_y": 7, 196 | "moving_length": 4, 197 | "number_of_turns": 1, 198 | "side_rotation_frequency": 1, 199 | "initial_direction": "up" 200 | }, 201 | { 202 | "type": "improved", 203 | "initial_cell_x": 12, 204 | "initial_cell_y": 2, 205 | "moving_length": 4, 206 | "number_of_turns": 2, 207 | "side_rotation_frequency": 4, 208 | "initial_direction": "down" 209 | }, 210 | { 211 | "type": "clever", 212 | "initial_cell_x": 2, 213 | "initial_cell_y": 13, 214 | "moving_length": 10, 215 | "number_of_turns": 1, 216 | "side_rotation_frequency": 4, 217 | "initial_direction": "left" 218 | } 219 | ], 220 | "obstacles": [ 221 | [4, 5], 222 | [4, 6], 223 | [2, 7], 224 | [2, 8], 225 | [7, 7], 226 | [6, 12], 227 | [10, 6], 228 | [11, 7], 229 | [12, 8], 230 | [9, 5], 231 | [9, 6], 232 | [7, 12], 233 | [7, 13], 234 | [5, 12], 235 | [4, 12], 236 | [8, 5], 237 | [6, 5], 238 | [5, 5], 239 | [1, 3], 240 | [2, 3] 241 | ], 242 | "portals": [ 243 | [4, 1, 13, 13], 244 | [10, 1, 1, 13] 245 | ] 246 | } 247 | ] 248 | } -------------------------------------------------------------------------------- /resources/maps/map3.json: -------------------------------------------------------------------------------- 1 | { 2 | "map": { 3 | "width": 15, 4 | "height": 15, 5 | "map": [ 6 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 7 | [0, 3, 3, 3, 3, 2, 2, 1, 1, 1, 0, 4, 4, 1, 0], 8 | [0, 3, 3, 3, 2, 2, 1, 1, 1, 1, 0, 1, 4, 4, 0], 9 | [0, 3, 3, 2, 2, 1, 1, 1, 1, 1, 0, 1, 1, 4, 0], 10 | [0, 3, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0], 11 | [0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], 12 | [0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], 13 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], 14 | [0, 1, 1, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 0], 15 | [0, 1, 1, 1, 0, 3, 3, 3, 2, 1, 1, 1, 1, 1, 0], 16 | [0, 1, 1, 1, 0, 3, 3, 2, 2, 1, 0, 0, 1, 1, 0], 17 | [0, 4, 1, 1, 0, 3, 2, 2, 1, 1, 0, 1, 1, 1, 0], 18 | [0, 4, 4, 1, 0, 2, 2, 1, 1, 1, 0, 1, 1, 1, 0], 19 | [0, 1, 4, 4, 0, 2, 1, 1, 1, 1, 0, 1, 1, 1, 0], 20 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 21 | ] 22 | }, 23 | "player_tank": { 24 | "initial_cell_x": 6, 25 | "initial_cell_y": 6, 26 | "initial_direction": "left" 27 | }, 28 | "difficulty": [ 29 | { 30 | "bots": [ 31 | { 32 | "type": "standard", 33 | "initial_cell_x": 3, 34 | "initial_cell_y": 12, 35 | "moving_length": 6, 36 | "number_of_turns": 1, 37 | "side_rotation_frequency": 4, 38 | "initial_direction": "down" 39 | }, 40 | { 41 | "type": "standard", 42 | "initial_cell_x": 3, 43 | "initial_cell_y": 3, 44 | "moving_length": 3, 45 | "number_of_turns": 1, 46 | "side_rotation_frequency": 2, 47 | "initial_direction": "right" 48 | }, 49 | { 50 | "type": "standard", 51 | "initial_cell_x": 13, 52 | "initial_cell_y": 13, 53 | "moving_length": 6, 54 | "number_of_turns": 1, 55 | "side_rotation_frequency": 4, 56 | "initial_direction": "up" 57 | } 58 | ], 59 | "obstacles": [ 60 | [1, 8], 61 | [2, 8], 62 | [5, 5], 63 | [5, 6], 64 | [5, 7], 65 | [6, 5], 66 | [6, 7], 67 | [7, 5], 68 | [7, 6], 69 | [7, 7], 70 | [12, 9], 71 | [13, 9] 72 | ], 73 | "portals": [ 74 | [12, 12, 10, 2] 75 | ] 76 | }, 77 | { 78 | "bots": [ 79 | { 80 | "type": "standard", 81 | "initial_cell_x": 3, 82 | "initial_cell_y": 12, 83 | "moving_length": 6, 84 | "number_of_turns": 1, 85 | "side_rotation_frequency": 4, 86 | "initial_direction": "down" 87 | }, 88 | { 89 | "type": "standard", 90 | "initial_cell_x": 3, 91 | "initial_cell_y": 3, 92 | "moving_length": 8, 93 | "number_of_turns": 1, 94 | "side_rotation_frequency": 2, 95 | "initial_direction": "right" 96 | }, 97 | { 98 | "type": "standard", 99 | "initial_cell_x": 9, 100 | "initial_cell_y": 6, 101 | "moving_length": 6, 102 | "number_of_turns": 1, 103 | "side_rotation_frequency": 4, 104 | "initial_direction": "up" 105 | }, 106 | { 107 | "type": "improved", 108 | "initial_cell_x": 13, 109 | "initial_cell_y": 13, 110 | "moving_length": 6, 111 | "number_of_turns": 2, 112 | "side_rotation_frequency": 1, 113 | "initial_direction": "up" 114 | }, 115 | { 116 | "type": "improved", 117 | "initial_cell_x": 13, 118 | "initial_cell_y": 1, 119 | "moving_length": 4, 120 | "number_of_turns": 1, 121 | "side_rotation_frequency": 4, 122 | "initial_direction": "left" 123 | } 124 | ], 125 | "obstacles": [ 126 | [1, 8], 127 | [2, 8], 128 | [5, 5], 129 | [5, 6], 130 | [5, 7], 131 | [6, 5], 132 | [6, 7], 133 | [7, 5], 134 | [7, 6], 135 | [7, 7], 136 | [12, 9], 137 | [13, 9], 138 | [4, 3], 139 | [4, 4], 140 | [3, 7], 141 | [2, 7], 142 | [1, 7], 143 | [12, 3], 144 | [13, 3] 145 | ], 146 | "portals": [ 147 | [12, 12, 10, 2] 148 | ] 149 | }, 150 | { 151 | "bots": [ 152 | { 153 | "type": "standard", 154 | "initial_cell_x": 3, 155 | "initial_cell_y": 12, 156 | "moving_length": 6, 157 | "number_of_turns": 1, 158 | "side_rotation_frequency": 4, 159 | "initial_direction": "down" 160 | }, 161 | { 162 | "type": "standard", 163 | "initial_cell_x": 3, 164 | "initial_cell_y": 3, 165 | "moving_length": 8, 166 | "number_of_turns": 1, 167 | "side_rotation_frequency": 2, 168 | "initial_direction": "right" 169 | }, 170 | { 171 | "type": "standard", 172 | "initial_cell_x": 9, 173 | "initial_cell_y": 6, 174 | "moving_length": 6, 175 | "number_of_turns": 1, 176 | "side_rotation_frequency": 4, 177 | "initial_direction": "up" 178 | }, 179 | { 180 | "type": "improved", 181 | "initial_cell_x": 13, 182 | "initial_cell_y": 13, 183 | "moving_length": 6, 184 | "number_of_turns": 2, 185 | "side_rotation_frequency": 1, 186 | "initial_direction": "up" 187 | }, 188 | { 189 | "type": "improved", 190 | "initial_cell_x": 10, 191 | "initial_cell_y": 1, 192 | "moving_length": 4, 193 | "number_of_turns": 1, 194 | "side_rotation_frequency": 4, 195 | "initial_direction": "left" 196 | }, 197 | { 198 | "type": "clever", 199 | "initial_cell_x": 6, 200 | "initial_cell_y": 10, 201 | "moving_length": 5, 202 | "number_of_turns": 2, 203 | "side_rotation_frequency": 1, 204 | "initial_direction": "down" 205 | }, 206 | { 207 | "type": "clever", 208 | "initial_cell_x": 9, 209 | "initial_cell_y": 13, 210 | "moving_length": 15, 211 | "number_of_turns": 1, 212 | "side_rotation_frequency": 1, 213 | "initial_direction": "right" 214 | } 215 | ], 216 | "obstacles": [ 217 | [1, 8], 218 | [2, 8], 219 | [5, 5], 220 | [5, 6], 221 | [5, 7], 222 | [6, 5], 223 | [6, 7], 224 | [7, 5], 225 | [7, 6], 226 | [7, 7], 227 | [12, 9], 228 | [13, 9], 229 | [4, 3], 230 | [4, 4], 231 | [3, 7], 232 | [2, 7], 233 | [1, 7], 234 | [12, 3], 235 | [13, 3], 236 | [7, 9], 237 | [7, 10], 238 | [7, 11], 239 | [9, 12], 240 | [8, 12], 241 | [8, 13], 242 | [6, 11], 243 | [5, 11] 244 | ], 245 | "portals": [ 246 | [13, 1, 1, 13] 247 | ] 248 | } 249 | ] 250 | } -------------------------------------------------------------------------------- /resources/maps/map4.json: -------------------------------------------------------------------------------- 1 | { 2 | "map": { 3 | "width": 15, 4 | "height": 15, 5 | "map": [ 6 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 7 | [0, 2, 2, 2, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0], 8 | [0, 2, 2, 0, 1, 1, 0, 1, 1, 0, 3, 3, 3, 3, 0], 9 | [0, 2, 1, 0, 1, 4, 0, 0, 1, 0, 2, 3, 3, 3, 0], 10 | [0, 1, 1, 0, 1, 4, 4, 1, 1, 0, 2, 2, 3, 3, 0], 11 | [0, 1, 1, 0, 1, 1, 4, 1, 1, 0, 1, 2, 2, 3, 0], 12 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 0], 13 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0], 14 | [0, 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 0, 1, 0], 15 | [0, 1, 1, 0, 1, 1, 0, 2, 1, 1, 0, 1, 0, 1, 0], 16 | [0, 1, 1, 0, 3, 1, 0, 2, 2, 1, 0, 1, 1, 1, 0], 17 | [0, 1, 1, 0, 3, 3, 0, 0, 2, 2, 0, 1, 1, 1, 0], 18 | [0, 4, 1, 0, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 0], 19 | [0, 4, 4, 0, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 0], 20 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 21 | ] 22 | }, 23 | "player_tank": { 24 | "initial_cell_x": 5, 25 | "initial_cell_y": 5, 26 | "initial_direction": "up" 27 | }, 28 | "difficulty": [ 29 | { 30 | "bots": [ 31 | { 32 | "type": "standard", 33 | "initial_cell_x": 4, 34 | "initial_cell_y": 4, 35 | "moving_length": 5, 36 | "number_of_turns": 1, 37 | "side_rotation_frequency": 10, 38 | "initial_direction": "down" 39 | }, 40 | { 41 | "type": "standard", 42 | "initial_cell_x": 12, 43 | "initial_cell_y": 1, 44 | "moving_length": 2, 45 | "number_of_turns": 1, 46 | "side_rotation_frequency": 2, 47 | "initial_direction": "up" 48 | }, 49 | { 50 | "type": "standard", 51 | "initial_cell_x": 2, 52 | "initial_cell_y": 12, 53 | "moving_length": 20, 54 | "number_of_turns": 2, 55 | "side_rotation_frequency": 2, 56 | "initial_direction": "up" 57 | }, 58 | { 59 | "type": "standard", 60 | "initial_cell_x": 13, 61 | "initial_cell_y": 9, 62 | "moving_length": 0, 63 | "number_of_turns": 1, 64 | "side_rotation_frequency": 4, 65 | "initial_direction": "left" 66 | } 67 | ], 68 | "obstacles": [ 69 | [6, 3], 70 | [5, 4], 71 | [5, 6], 72 | [4, 5], 73 | [4, 6], 74 | [1, 10], 75 | [1, 11], 76 | [10, 5] 77 | ], 78 | "portals": [ 79 | [2, 7, 12, 12] 80 | ] 81 | }, 82 | { 83 | "bots": [ 84 | { 85 | "type": "standard", 86 | "initial_cell_x": 4, 87 | "initial_cell_y": 4, 88 | "moving_length": 5, 89 | "number_of_turns": 1, 90 | "side_rotation_frequency": 10, 91 | "initial_direction": "down" 92 | }, 93 | { 94 | "type": "standard", 95 | "initial_cell_x": 12, 96 | "initial_cell_y": 1, 97 | "moving_length": 2, 98 | "number_of_turns": 1, 99 | "side_rotation_frequency": 2, 100 | "initial_direction": "up" 101 | }, 102 | { 103 | "type": "standard", 104 | "initial_cell_x": 2, 105 | "initial_cell_y": 12, 106 | "moving_length": 20, 107 | "number_of_turns": 2, 108 | "side_rotation_frequency": 2, 109 | "initial_direction": "up" 110 | }, 111 | { 112 | "type": "standard", 113 | "initial_cell_x": 13, 114 | "initial_cell_y": 9, 115 | "moving_length": 0, 116 | "number_of_turns": 1, 117 | "side_rotation_frequency": 4, 118 | "initial_direction": "left" 119 | }, 120 | { 121 | "type": "standard", 122 | "initial_cell_x": 7, 123 | "initial_cell_y": 13, 124 | "moving_length": 3, 125 | "number_of_turns": 1, 126 | "side_rotation_frequency": 1, 127 | "initial_direction": "right" 128 | }, 129 | { 130 | "type": "improved", 131 | "initial_cell_x": 1, 132 | "initial_cell_y": 2, 133 | "moving_length": 20, 134 | "number_of_turns": 1, 135 | "side_rotation_frequency": 2, 136 | "initial_direction": "down" 137 | }, 138 | { 139 | "type": "improved", 140 | "initial_cell_x": 9, 141 | "initial_cell_y": 5, 142 | "moving_length": 5, 143 | "number_of_turns": 3, 144 | "side_rotation_frequency": 1, 145 | "initial_direction": "up" 146 | }, 147 | { 148 | "type": "improved", 149 | "initial_cell_x": 11, 150 | "initial_cell_y": 13, 151 | "moving_length": 0, 152 | "number_of_turns": 2, 153 | "side_rotation_frequency": 3, 154 | "initial_direction": "up" 155 | } 156 | ], 157 | "obstacles": [ 158 | [6, 3], 159 | [5, 4], 160 | [5, 6], 161 | [4, 5], 162 | [4, 6], 163 | [1, 10], 164 | [1, 11], 165 | [10, 5], 166 | [1, 3], 167 | [2, 4], 168 | [8, 8], 169 | [9, 8], 170 | [12, 11], 171 | [11, 11], 172 | [6, 11], 173 | [7, 11] 174 | ], 175 | "portals": [ 176 | [2, 7, 12, 12] 177 | ] 178 | }, 179 | { 180 | "bots": [ 181 | { 182 | "type": "standard", 183 | "initial_cell_x": 4, 184 | "initial_cell_y": 4, 185 | "moving_length": 5, 186 | "number_of_turns": 1, 187 | "side_rotation_frequency": 10, 188 | "initial_direction": "down" 189 | }, 190 | { 191 | "type": "standard", 192 | "initial_cell_x": 12, 193 | "initial_cell_y": 1, 194 | "moving_length": 2, 195 | "number_of_turns": 1, 196 | "side_rotation_frequency": 2, 197 | "initial_direction": "up" 198 | }, 199 | { 200 | "type": "standard", 201 | "initial_cell_x": 2, 202 | "initial_cell_y": 12, 203 | "moving_length": 20, 204 | "number_of_turns": 2, 205 | "side_rotation_frequency": 2, 206 | "initial_direction": "up" 207 | }, 208 | { 209 | "type": "standard", 210 | "initial_cell_x": 13, 211 | "initial_cell_y": 9, 212 | "moving_length": 0, 213 | "number_of_turns": 1, 214 | "side_rotation_frequency": 4, 215 | "initial_direction": "left" 216 | }, 217 | { 218 | "type": "standard", 219 | "initial_cell_x": 7, 220 | "initial_cell_y": 13, 221 | "moving_length": 3, 222 | "number_of_turns": 1, 223 | "side_rotation_frequency": 1, 224 | "initial_direction": "right" 225 | }, 226 | { 227 | "type": "improved", 228 | "initial_cell_x": 1, 229 | "initial_cell_y": 2, 230 | "moving_length": 20, 231 | "number_of_turns": 1, 232 | "side_rotation_frequency": 2, 233 | "initial_direction": "down" 234 | }, 235 | { 236 | "type": "improved", 237 | "initial_cell_x": 9, 238 | "initial_cell_y": 5, 239 | "moving_length": 5, 240 | "number_of_turns": 3, 241 | "side_rotation_frequency": 1, 242 | "initial_direction": "up" 243 | }, 244 | { 245 | "type": "improved", 246 | "initial_cell_x": 11, 247 | "initial_cell_y": 13, 248 | "moving_length": 0, 249 | "number_of_turns": 2, 250 | "side_rotation_frequency": 3, 251 | "initial_direction": "up" 252 | }, 253 | { 254 | "type": "clever", 255 | "initial_cell_x": 13, 256 | "initial_cell_y": 4, 257 | "moving_length": 10, 258 | "number_of_turns": 1, 259 | "side_rotation_frequency": 4, 260 | "initial_direction": "left" 261 | }, 262 | { 263 | "type": "clever", 264 | "initial_cell_x": 13, 265 | "initial_cell_y": 13, 266 | "moving_length": 4, 267 | "number_of_turns": 1, 268 | "side_rotation_frequency": 1, 269 | "initial_direction": "up" 270 | } 271 | ], 272 | "obstacles": [ 273 | [6, 3], 274 | [5, 4], 275 | [5, 6], 276 | [4, 5], 277 | [4, 6], 278 | [1, 10], 279 | [1, 11], 280 | [10, 5], 281 | [1, 3], 282 | [2, 4], 283 | [8, 8], 284 | [9, 8], 285 | [12, 11], 286 | [11, 11], 287 | [6, 11], 288 | [7, 11], 289 | [13, 2], 290 | [12, 2], 291 | [12, 4], 292 | [10, 12], 293 | [10, 13] 294 | ], 295 | "portals": [ 296 | [2, 7, 12, 13], 297 | [12, 5, 3, 12] 298 | ] 299 | } 300 | ] 301 | } -------------------------------------------------------------------------------- /resources/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | maps/map1.json 4 | maps/map2.json 5 | maps/map3.json 6 | maps/map4.json 7 | game_objects_data/tanks.json 8 | game_objects_data/rockets.json 9 | textures/0.png 10 | textures/1.png 11 | textures/2.png 12 | textures/3.png 13 | textures/4.png 14 | textures/tank.png 15 | textures/bot.png 16 | textures/improved_bot.png 17 | textures/clever_bot.png 18 | textures/log.png 19 | textures/rocket.png 20 | textures/boom.png 21 | textures/medicalkit.png 22 | textures/charge.png 23 | textures/portal.png 24 | translations/tanks_be_BY.qm 25 | translations/tanks_en_US.qm 26 | translations/tanks_ru_RU.qm 27 | rules/rules.html 28 | rules/style.css 29 | rules/new_game.png 30 | rules/pause.png 31 | rules/settings.png 32 | rules/about.png 33 | rules/timer.png 34 | sounds/backgroundmusic1.mp3 35 | sounds/backgroundmusic2.mp3 36 | sounds/backgroundmusic3.mp3 37 | sounds/backgroundmusic4.mp3 38 | sounds/boom.mp3 39 | 40 | 41 | -------------------------------------------------------------------------------- /resources/rules/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/rules/about.png -------------------------------------------------------------------------------- /resources/rules/new_game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/rules/new_game.png -------------------------------------------------------------------------------- /resources/rules/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/rules/pause.png -------------------------------------------------------------------------------- /resources/rules/rules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Tanks Wiki 7 | 8 | 9 | 10 | 11 | 12 |

13 | Tanks 14 | Wiki 15 |

16 |

17 | In this section you can get to know about the rules of the 18 | game, learn about management features and clarify information 19 | about objects on the map. 20 |

21 |

Version 0.8.6

22 |

23 | This project was developed by 24 | Alesia Taraikovich , 25 | Aliaksandra Harmaza , 26 | Andrew Nevero .
27 | BSU FAMCS 2019-2021 28 |

29 | 30 |

© Tanks

31 |
32 | 50 |
51 | 52 |
53 | 54 | 55 | 58 | 59 | 60 | 63 |
56 |

Keys and buttons

57 |
61 |

Сontrol keys

62 |
64 |

Ability to control player's tank. 65 | 67 | 68 | 71 | 74 | 75 | 76 | 79 | 82 | 83 | 84 | 87 | 90 | 91 | 92 | 95 | 98 | 99 | 100 | 101 | 102 | 103 | 106 |
69 |

W

70 |
72 |

Drive forward

73 |
77 |

S

78 |
80 |

Drive back

81 |
85 |

A

86 |
88 |

Turn left

89 |
93 |

D

94 |
96 |

Turn right

97 |
104 |

1, 2, 3 keys

105 |
107 | 108 |

Ability to choose type of rocket for 109 | player's 110 | tank. 111 | 113 | 114 | 116 | 119 | 122 | 127 | 128 | 129 | 132 | 135 | 138 | 141 | 142 | 143 | 146 | 149 | 152 | 155 | 156 | 157 | 160 | 163 | 166 | 169 | 170 | 171 | 172 | 173 | 174 | 177 | 180 | 181 | 182 | 186 | 191 | 192 | 193 | 197 | 202 | 203 | 204 | 208 | 232 | 233 | 234 | 238 | 244 | 245 | 246 | 250 | 257 | 258 | 259 | 262 | 263 |
115 | 117 |

Damage

118 |
120 |

Speed

121 |
123 |

Is able to
124 | destroy
125 | objects

126 |
130 |

1

131 |
133 |

low

134 |
136 |

high

137 |
139 |

yes

140 |
144 |

2

145 |
147 |

medium

148 |
150 |

medium

151 |
153 |

yes

154 |
158 |

3

159 |
161 |

high

162 |
164 |

low

165 |
167 |

no

168 |
175 |

Esc

176 |
178 |

Ability to pause the game for a while.

179 |
183 | 185 | 187 |

New game
188 | Click this button to start the game. 189 |

190 |
194 | 196 | 198 |

Pause
199 | Сlick this button to suspend the game. 200 |

201 |
205 | 207 | 209 |

Settings 210 |
    211 |
  • Virtual keys - ability to control 212 | tank using buttons on the screen. 213 |
  • 214 |
  • 215 | Experimental virtual keys layout - 216 | enables new virtual keys layout on 217 | Android. 218 |
  • 219 |
  • Activate charge line - ability to 220 | see recharge time.
  • 221 |
  • 222 | Performance - ability to change image 223 | refresh rate. 224 |
  • 225 |
  • Language - ability to switch the 226 | language of the app. Restarting app 227 | needed to apply language changes. 228 |
  • 229 |
230 |

231 |
235 | 237 | 239 |

About
240 | The rules of the game and information about 241 | authors. 242 |

243 |
247 | 249 | 251 |

Timer
252 | Time that has passed since the beginning of 253 | the game. Starts blinking red after 8 254 | minutes. You have 10 minutes per round. 255 |

256 |
260 |

Start page

261 |
264 | 265 |
266 | 267 |
268 | 269 | 270 | 273 | 274 | 275 | 279 | 284 | 285 | 286 | 290 | 296 | 297 | 298 | 302 | 307 | 308 | 309 | 313 | 318 | 319 | 320 | 324 | 332 | 333 | 334 | 337 | 338 |
271 |

Textures

272 |
276 | 278 | 280 |

Wall
281 | Main type of coating. A continuous block 282 | through which it is impossible to pass.

283 |
287 | 289 | 291 |

Ground
292 | Main type of coating. 293 | Tank and robots have the fastest speed there. 294 |

295 |
299 | 301 | 303 |

Quicksand
304 | One of the types of coating. Slows objects 305 | down.

306 |
310 | 312 | 314 |

Water
315 | One of the types of coating. Slows objects 316 | down more than quicksand.

317 |
321 | 323 | 325 | 326 |

Forest
327 | One of the types of coating. Slows objects 328 | down very much. Hides player's 329 | tank from robots, even if they 330 | collide in the forest.

331 |
335 |

Start page

336 |
339 |
340 |
341 | 342 |
343 | 344 | 345 | 348 | 349 | 350 | 354 |
346 |

Tanks and robots

347 |
351 | 353 | 355 |

Yellow tank
356 | Player's tank, which is controlled by you. 357 | This is the most powerful tank on the map: it 358 | has a lot of health and a possibility to 359 | change types of rockets. 360 | 362 | 363 | 366 | 369 | 372 | 373 | 374 | 377 | 380 | 383 | 384 | 385 | 388 | 391 | 394 | 395 | 396 | 399 | 402 | 405 | 406 | 407 | 408 | 409 | 410 | 414 | 421 | 422 | 423 | 427 | 434 | 435 | 436 | 440 | 450 | 451 | 452 | 455 | 456 |
364 |

Type

365 |
367 |

Health

368 |
370 |

Speed

371 |
375 |

1

376 |
378 |

200

379 |
381 |

2 cps

382 |
386 |

2

387 |
389 |

150

390 |
392 |

2.85 cps

393 |
397 |

3

398 |
400 |

250

401 |
403 |

4 cps

404 |
411 | 413 | 415 |

Pink tank or simple bot
416 | The first and the simplest modification of 417 | a robot. Shoots player's tank only if 418 | sees it in front of itself. 419 |

420 |
424 | 426 | 428 |

Green tank or improved bot
429 | The second modification of a robot. It 430 | turns and shoots player tank if it stands on 431 | the same line. 432 |

433 |
437 | 439 | 441 |

Blue tank or clever bot
442 | The third modification of a robot. 443 | Looks for the player's tank on the map, 444 | builds the shortest way and follows it. When 445 | the player's tank is in the forest 446 | behaves like 447 | simple robot. 448 |

449 |
453 |

Start page

454 |
457 | 458 |
459 | 460 |
461 | 462 | 463 | 466 | 467 | 468 | 472 | 482 | 483 | 484 | 488 | 497 | 498 | 499 | 503 | 512 | 513 | 514 | 518 | 527 | 528 | 529 | 533 | 542 | 543 | 544 | 548 | 557 | 558 | 559 | 562 | 563 |
464 |

Obstacles and bonuses

465 |
469 | 471 | 473 | 474 |

Rocket
475 | Tanks can launch rockets, interacting 476 | with other objects. Rockets reduce tank's 477 | health and destroy logs, 478 | medical kits, charges. 480 |

481 |
485 | 487 | 489 | 490 |

Boom
491 | When robots die, they explode and release an 492 | explosion, which reduces objects's health 493 | and destroys logs, medical 494 | kits, charges . 495 |

496 |
500 | 502 | 504 | 505 |

Medical kit
506 | Periodically appears on the map and 507 | disappears after a while. Increases tanks' 508 | health, but no more than initial health. 509 | Destroyed when meets up a rocket. 510 |

511 |
515 | 517 | 519 | 520 |

Charge
521 | Periodically appears on the map and 522 | disappears after a while. Adds rockets to 523 | random type of 524 | charge . 525 |

526 |
530 | 532 | 534 | 535 |

Log
536 | Obstacle which you can break down or destroy 537 | by explosion or rocket. If the tank 539 | runs into an obstacle, it slows down. 540 |

541 |
545 | 547 | 549 |

Portal
550 | Portals on the map are paired. Player's tank 551 | and robots (but not rockets) 552 | can teleport from one cell, connected with 553 | one of two related portals, to another cell, 554 | where the second portal is situated. 555 |

556 |
560 |

Start page

561 |
564 |
565 | 566 |

© Tanks

567 |
568 | 569 | 570 | 571 | -------------------------------------------------------------------------------- /resources/rules/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/rules/screenshot1.png -------------------------------------------------------------------------------- /resources/rules/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/rules/screenshot2.png -------------------------------------------------------------------------------- /resources/rules/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/rules/settings.png -------------------------------------------------------------------------------- /resources/rules/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #FFE5CC; 3 | font-family: roboto, open sans, comic sans, bitstream charter; 4 | margin: 0px; 5 | } 6 | 7 | h1 { 8 | color: #663300; 9 | text-align: center; 10 | } 11 | 12 | h2 { 13 | color: #FFCCCC; 14 | font-weight: 100; 15 | text-align: center; 16 | margin: 0px; 17 | } 18 | 19 | h3 { 20 | color: #000000; 21 | font-weight: 100; 22 | text-align: center; 23 | margin-bottom: -4px; 24 | } 25 | 26 | h4 { 27 | color: #442200; 28 | text-align: right; 29 | margin-right: 20px; 30 | margin-top: 4px; 31 | margin-bottom: 0px; 32 | } 33 | 34 | p { 35 | color: #000000; 36 | font-weight: 100; 37 | text-align: justify; 38 | margin: 0px; 39 | } 40 | 41 | a { 42 | color: #663300; 43 | font-weight: 400; 44 | text-align: center; 45 | text-decoration: none; 46 | margin-bottom: -4px; 47 | } 48 | 49 | .menu { 50 | margin: 0px; 51 | } 52 | 53 | .menu tr { 54 | border-color: #000000; 55 | border: 0; 56 | } 57 | 58 | .menu h1 { 59 | color: #663300; 60 | font-weight: 400; 61 | text-align: center; 62 | margin: 0px; 63 | } 64 | 65 | .menu h2 { 66 | text-align: left; 67 | font-weight: medium; 68 | margin: 0px; 69 | } 70 | 71 | .menu a { 72 | color: #7C5043; 73 | font-weight: 100; 74 | text-decoration: none; 75 | margin: 0px; 76 | } 77 | 78 | .menu ul { 79 | margin: 0px; 80 | } 81 | 82 | .info { 83 | color: #000000; 84 | text-align: justify; 85 | border-style: solid; 86 | border-color: #000000; 87 | border-width: 2px; 88 | border: 2; 89 | } 90 | 91 | .info h2 { 92 | color: #663300; 93 | font-weight: 100; 94 | text-align: center; 95 | margin: 0px; 96 | } 97 | 98 | .info h3 { 99 | color: #442200; 100 | font-weight: 100; 101 | text-align: left; 102 | margin: 0px; 103 | } 104 | 105 | .info a { 106 | color: #774400; 107 | font-weight: 100; 108 | text-align: left; 109 | text-decoration: none; 110 | margin: 0px; 111 | } 112 | 113 | .info ul { 114 | margin: 0px; 115 | text-align: left; 116 | } 117 | -------------------------------------------------------------------------------- /resources/rules/timer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/rules/timer.png -------------------------------------------------------------------------------- /resources/sounds/backgroundmusic1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/sounds/backgroundmusic1.mp3 -------------------------------------------------------------------------------- /resources/sounds/backgroundmusic2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/sounds/backgroundmusic2.mp3 -------------------------------------------------------------------------------- /resources/sounds/backgroundmusic3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/sounds/backgroundmusic3.mp3 -------------------------------------------------------------------------------- /resources/sounds/backgroundmusic4.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/sounds/backgroundmusic4.mp3 -------------------------------------------------------------------------------- /resources/sounds/boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/sounds/boom.mp3 -------------------------------------------------------------------------------- /resources/textures/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/0.png -------------------------------------------------------------------------------- /resources/textures/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/1.png -------------------------------------------------------------------------------- /resources/textures/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/2.png -------------------------------------------------------------------------------- /resources/textures/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/3.png -------------------------------------------------------------------------------- /resources/textures/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/4.png -------------------------------------------------------------------------------- /resources/textures/boom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/boom.png -------------------------------------------------------------------------------- /resources/textures/bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/bot.png -------------------------------------------------------------------------------- /resources/textures/charge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/charge.png -------------------------------------------------------------------------------- /resources/textures/clever_bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/clever_bot.png -------------------------------------------------------------------------------- /resources/textures/improved_bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/improved_bot.png -------------------------------------------------------------------------------- /resources/textures/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/log.png -------------------------------------------------------------------------------- /resources/textures/medicalkit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/medicalkit.png -------------------------------------------------------------------------------- /resources/textures/portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/portal.png -------------------------------------------------------------------------------- /resources/textures/rocket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/rocket.png -------------------------------------------------------------------------------- /resources/textures/tank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/textures/tank.png -------------------------------------------------------------------------------- /resources/translations/tanks_be_BY.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/translations/tanks_be_BY.qm -------------------------------------------------------------------------------- /resources/translations/tanks_be_BY.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainWindow 6 | 7 | 8 | New game 9 | Новая гульня 10 | 11 | 12 | 13 | 14 | 15 | Pause 16 | Паўза 17 | 18 | 19 | 20 | Settings 21 | Налады 22 | 23 | 24 | 25 | About 26 | Інфармацыя 27 | 28 | 29 | 30 | High speed, low charge, can destroy obstacles 31 | Высокая хуткасць, нізкая шкода, можа знішчаць перашкоды 32 | 33 | 34 | 35 | You have %1 minutes per round 36 | У вас ёсць %1 хвілін на раўнд 37 | 38 | 39 | 40 | Low speed, high charge, can't destroy obstacles 41 | Нізкая хуткасць, высокая шкода, не можа знішчаць перашкоды 42 | 43 | 44 | 45 | Medium speed, medium charge, can destroy obstacles 46 | Сярэдняя хуткасць, сярэдняя шкода, можа знішчаць перашкоды 47 | 48 | 49 | 50 | Health 51 | Здароўе 52 | 53 | 54 | 55 | Continue 56 | Працягнуць 57 | 58 | 59 | 60 | You win! 61 | You can start a new game with help of appropriate button on the left. 62 | Вы перамаглі! Вы можаце пачаць новую гульню з дапамогаю адпаведнай кнопкі злева. 63 | 64 | 65 | 66 | You died! 67 | You can start a new game with help of appropriate button on the left. 68 | Вы прайгралі! Вы можаце пачаць новую гульню з дапамогаю адпаведнай кнопкі злева. 69 | 70 | 71 | 72 | NewGameDialog 73 | 74 | 75 | Choose map, tank and difficulty 76 | Выберыце карту, танк і цяжкасць 77 | 78 | 79 | 80 | 81 | Map 82 | Карта 83 | 84 | 85 | 86 | 87 | Tank 88 | Танк 89 | 90 | 91 | 92 | Difficulty 93 | Цяжкасць 94 | 95 | 96 | 97 | Easy 98 | Проста 99 | 100 | 101 | 102 | Normal 103 | Нармальна 104 | 105 | 106 | 107 | Hard 108 | Складана 109 | 110 | 111 | 112 | SettingsDialog 113 | 114 | 115 | Virtual keys 116 | Віртуальныя клавішы 117 | 118 | 119 | 120 | Mobile virtual keys layout 121 | Мабільная раскладка клавіш 122 | 123 | 124 | 125 | Charge line 126 | Паласа перазарадкі 127 | 128 | 129 | 130 | Music 131 | Музыка 132 | 133 | 134 | 135 | Performance 136 | Прадукцыйнасць 137 | 138 | 139 | 140 | Language 141 | Мова 142 | 143 | 144 | 145 | %1 frames per second 146 | %1 кадраў у секунду 147 | 148 | 149 | 150 | The app needs to be restarted to change the language. 151 | Прыкладанне трэба перазапусціць, каб змяніць мову. 152 | 153 | 154 | 155 | Close application? 156 | Зачыніць прыкладанне? 157 | 158 | 159 | 160 | Restart application? 161 | Перазапусціць прыкладанне? 162 | 163 | 164 | 165 | en_US 166 | Англійская 167 | 168 | 169 | 170 | ru_RU 171 | Руская 172 | 173 | 174 | 175 | be_BY 176 | Беларуская 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /resources/translations/tanks_en_US.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/translations/tanks_en_US.qm -------------------------------------------------------------------------------- /resources/translations/tanks_en_US.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainWindow 6 | 7 | 8 | New game 9 | New game 10 | 11 | 12 | 13 | 14 | 15 | Pause 16 | Pause 17 | 18 | 19 | 20 | Settings 21 | Settings 22 | 23 | 24 | 25 | About 26 | About 27 | 28 | 29 | 30 | High speed, low charge, can destroy obstacles 31 | High speed, low charge, can destroy obstacles 32 | 33 | 34 | 35 | You have %1 minutes per round 36 | You have %1 minutes per round 37 | 38 | 39 | 40 | Low speed, high charge, can't destroy obstacles 41 | Low speed, high charge, can't destroy obstacles 42 | 43 | 44 | 45 | Medium speed, medium charge, can destroy obstacles 46 | Medium speed, medium charge, can destroy obstacles 47 | 48 | 49 | 50 | Health 51 | Health 52 | 53 | 54 | 55 | Continue 56 | Continue 57 | 58 | 59 | 60 | You win! 61 | You can start a new game with help of appropriate button on the left. 62 | You win! You can start a new game with help of appropriate button on the left. 63 | 64 | 65 | 66 | You died! 67 | You can start a new game with help of appropriate button on the left. 68 | You died! You can start a new game with help of appropriate button on the left. 69 | 70 | 71 | 72 | NewGameDialog 73 | 74 | 75 | Choose map, tank and difficulty 76 | Choose map, tank and difficulty 77 | 78 | 79 | 80 | 81 | Map 82 | Map 83 | 84 | 85 | 86 | 87 | Tank 88 | Tank 89 | 90 | 91 | 92 | Difficulty 93 | Difficulty 94 | 95 | 96 | 97 | Easy 98 | Easy 99 | 100 | 101 | 102 | Normal 103 | Normal 104 | 105 | 106 | 107 | Hard 108 | Hard 109 | 110 | 111 | 112 | SettingsDialog 113 | 114 | 115 | Virtual keys 116 | Virtual keys 117 | 118 | 119 | 120 | Mobile virtual keys layout 121 | Mobile virtual keys layout 122 | 123 | 124 | 125 | Charge line 126 | Charge line 127 | 128 | 129 | 130 | Music 131 | Music 132 | 133 | 134 | 135 | Performance 136 | Performance 137 | 138 | 139 | 140 | Language 141 | Language 142 | 143 | 144 | 145 | %1 frames per second 146 | %1 frames per second 147 | 148 | 149 | 150 | The app needs to be restarted to change the language. 151 | The app needs to be restarted to change the language. 152 | 153 | 154 | 155 | Close application? 156 | Close application? 157 | 158 | 159 | 160 | Restart application? 161 | Restart application? 162 | 163 | 164 | 165 | en_US 166 | English 167 | 168 | 169 | 170 | ru_RU 171 | Russian 172 | 173 | 174 | 175 | be_BY 176 | Belarusian 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /resources/translations/tanks_ru_RU.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anevero/tanks/eb2a9e0783d5ee4fb70769535034a474e7f59370/resources/translations/tanks_ru_RU.qm -------------------------------------------------------------------------------- /resources/translations/tanks_ru_RU.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainWindow 6 | 7 | 8 | New game 9 | Новая игра 10 | 11 | 12 | 13 | 14 | 15 | Pause 16 | Пауза 17 | 18 | 19 | 20 | Settings 21 | Настройки 22 | 23 | 24 | 25 | About 26 | Информация 27 | 28 | 29 | 30 | High speed, low charge, can destroy obstacles 31 | Высокая скорость, низкий урон, может уничтожать препятствия 32 | 33 | 34 | 35 | You have %1 minutes per round 36 | У вас есть %1 минут на раунд 37 | 38 | 39 | 40 | Low speed, high charge, can't destroy obstacles 41 | Низкая скорость, высокий урон, не может уничтожать препятствия 42 | 43 | 44 | 45 | Medium speed, medium charge, can destroy obstacles 46 | Средняя скорость, средний урон, может уничтожать препятствия 47 | 48 | 49 | 50 | Health 51 | Здоровье 52 | 53 | 54 | 55 | Continue 56 | Продолжить 57 | 58 | 59 | 60 | You win! 61 | You can start a new game with help of appropriate button on the left. 62 | Вы победили! Вы можете начать новую игру с помощью соответствующей кнопки слева. 63 | 64 | 65 | 66 | You died! 67 | You can start a new game with help of appropriate button on the left. 68 | Вы проиграли! Вы можете начать новую игру с помощью соответствующей кнопки слева. 69 | 70 | 71 | 72 | NewGameDialog 73 | 74 | 75 | Choose map, tank and difficulty 76 | Выберите карту, танк и сложность 77 | 78 | 79 | 80 | 81 | Map 82 | Карта 83 | 84 | 85 | 86 | 87 | Tank 88 | Танк 89 | 90 | 91 | 92 | Difficulty 93 | Сложность 94 | 95 | 96 | 97 | Easy 98 | Легко 99 | 100 | 101 | 102 | Normal 103 | Нормально 104 | 105 | 106 | 107 | Hard 108 | Тяжело 109 | 110 | 111 | 112 | SettingsDialog 113 | 114 | 115 | Virtual keys 116 | Виртуальные клавиши 117 | 118 | 119 | 120 | Mobile virtual keys layout 121 | Мобильная раскладка клавиш 122 | 123 | 124 | 125 | Charge line 126 | Полоса перезарядки 127 | 128 | 129 | 130 | Music 131 | Музыка 132 | 133 | 134 | 135 | Performance 136 | Производительность 137 | 138 | 139 | 140 | Language 141 | Язык 142 | 143 | 144 | 145 | %1 frames per second 146 | %1 кадров в секунду 147 | 148 | 149 | 150 | The app needs to be restarted to change the language. 151 | Приложение нужно перезапустить, чтобы изменить язык. 152 | 153 | 154 | 155 | Close application? 156 | Закрыть приложение? 157 | 158 | 159 | 160 | Restart application? 161 | Перезапустить приложение? 162 | 163 | 164 | 165 | en_US 166 | Английский 167 | 168 | 169 | 170 | ru_RU 171 | Русский 172 | 173 | 174 | 175 | be_BY 176 | Белорусский 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /static_objects/portal.cpp: -------------------------------------------------------------------------------- 1 | #include "portal.h" 2 | 3 | Portal::Portal(std::shared_ptr map, 4 | Coordinates cell, Coordinates new_cell) 5 | : StaticObject(std::move(map), ":/textures/portal.png", cell), 6 | new_cell_(new_cell) {} 7 | 8 | Coordinates Portal::GetNewCell() const { 9 | return new_cell_; 10 | } 11 | -------------------------------------------------------------------------------- /static_objects/portal.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_OBJECTS_PORTAL_H_ 2 | #define STATIC_OBJECTS_PORTAL_H_ 3 | 4 | #include 5 | #include 6 | #include "static_object.h" 7 | #include "../game_core/map.h" 8 | 9 | class Portal : public StaticObject { 10 | public: 11 | Portal(std::shared_ptr map, Coordinates cell, 12 | Coordinates new_cell); 13 | 14 | Coordinates GetNewCell() const; 15 | 16 | private: 17 | Coordinates new_cell_; 18 | }; 19 | 20 | #endif // STATIC_OBJECTS_PORTAL_H_ 21 | -------------------------------------------------------------------------------- /static_objects/static_object.cpp: -------------------------------------------------------------------------------- 1 | #include "static_object.h" 2 | 3 | #include "../game_core/constants.h" 4 | 5 | StaticObject::StaticObject( 6 | std::shared_ptr map, const QString& texture_path, 7 | Coordinates coordinates) 8 | : GameObject(std::move(map), texture_path, coordinates) {} 9 | 10 | void StaticObject::Draw(QPainter* painter) { 11 | RescaleImage(); 12 | painter->save(); 13 | painter->translate( 14 | upper_left_cell_coordinates_.x + width_ / 2, 15 | upper_left_cell_coordinates_.y + height_ / 2); 16 | if (map_->GetField(cell_) == CellType::Forest) { 17 | painter->setOpacity(constants::kOpacityLevel); 18 | } 19 | painter->drawPixmap(-width_ / 2, -height_ / 2, scaled_pixmap_); 20 | painter->restore(); 21 | } 22 | 23 | void StaticObject::UpdateCoordinates() { 24 | width_ = map_->GetWidth() / map_->GetNumberOfCellsHorizontally(); 25 | height_ = map_->GetHeight() / map_->GetNumberOfCellsVertically(); 26 | upper_left_cell_coordinates_.x = 27 | map_->GetUpperLeftCellCoordinates().x + cell_.x * width_; 28 | upper_left_cell_coordinates_.y = 29 | map_->GetUpperLeftCellCoordinates().y + cell_.y * height_; 30 | 31 | RescaleImage(); 32 | } 33 | 34 | MedicalKit::MedicalKit(std::shared_ptr map, Coordinates coordinates) 35 | : StaticObject(std::move(map), ":/textures/medicalkit.png", coordinates) {} 36 | 37 | Obstacle::Obstacle(std::shared_ptr map, Coordinates coordinates) 38 | : StaticObject(std::move(map), ":/textures/log.png", coordinates) {} 39 | 40 | Charge::Charge(std::shared_ptr map, Coordinates coordinates) 41 | : StaticObject(std::move(map), ":/textures/charge.png", coordinates) {} 42 | -------------------------------------------------------------------------------- /static_objects/static_object.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_OBJECTS_STATIC_OBJECT_H_ 2 | #define STATIC_OBJECTS_STATIC_OBJECT_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "../game_core/map.h" 8 | #include "game_core/game_object.h" 9 | 10 | class StaticObject : public GameObject { 11 | public: 12 | StaticObject(std::shared_ptr map, const QString& texture_path, 13 | Coordinates coordinates); 14 | ~StaticObject() override = default; 15 | 16 | void Draw(QPainter* painter) override; 17 | 18 | void UpdateCoordinates(); 19 | }; 20 | 21 | class Obstacle : public StaticObject { 22 | public: 23 | Obstacle(std::shared_ptr map, Coordinates coordinates); 24 | ~Obstacle() override = default; 25 | }; 26 | 27 | class MedicalKit : public StaticObject { 28 | public: 29 | MedicalKit(std::shared_ptr map, Coordinates coordinates); 30 | ~MedicalKit() override = default; 31 | }; 32 | 33 | class Charge : public StaticObject { 34 | public: 35 | Charge(std::shared_ptr map, Coordinates coordinates); 36 | ~Charge() override = default; 37 | }; 38 | 39 | #endif // STATIC_OBJECTS_STATIC_OBJECT_H_ 40 | -------------------------------------------------------------------------------- /tanks.pro: -------------------------------------------------------------------------------- 1 | greaterThan(QT_MAJOR_VERSION, 4): 2 | QT += widgets core gui multimedia 3 | 4 | android { 5 | QT += androidextras 6 | } 7 | 8 | DEFINES += QT_DEPRECATED_WARNINGS 9 | 10 | TARGET = Tanks 11 | TEMPLATE = app 12 | CONFIG += c++17 13 | QMAKE_CXXFLAGS += -std=c++17 14 | QMAKE_CXXFLAGS_RELEASE -= -O2 15 | QMAKE_CXXFLAGS_RELEASE += -O3 16 | 17 | ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android 18 | DISTFILES += \ 19 | android/AndroidManifest.xml \ 20 | android/build.gradle \ 21 | android/gradle.properties \ 22 | android/gradle/wrapper/gradle-wrapper.jar \ 23 | android/gradle/wrapper/gradle-wrapper.properties \ 24 | android/gradlew \ 25 | android/gradlew.bat \ 26 | android/res/values/libs.xml 27 | 28 | SOURCES += \ 29 | main.cpp \ 30 | game_core/about_dialog.cpp \ 31 | game_core/game_object.cpp \ 32 | game_core/mainwindow.cpp \ 33 | game_core/map.cpp \ 34 | game_core/new_game_dialog.cpp \ 35 | game_core/settings_dialog.cpp \ 36 | static_objects/portal.cpp \ 37 | static_objects/static_object.cpp \ 38 | movable_objects/boom.cpp \ 39 | movable_objects/bot.cpp \ 40 | movable_objects/clever_bot.cpp \ 41 | movable_objects/improved_bot.cpp \ 42 | movable_objects/movable.cpp \ 43 | movable_objects/rocket.cpp \ 44 | movable_objects/tank.cpp 45 | 46 | HEADERS += \ 47 | game_core/about_dialog.h \ 48 | game_core/constants.h \ 49 | game_core/game_object.h \ 50 | game_core/mainwindow.h \ 51 | game_core/map.h \ 52 | game_core/new_game_dialog.h \ 53 | game_core/settings_dialog.h \ 54 | static_objects/portal.h \ 55 | static_objects/static_object.h \ 56 | movable_objects/boom.h \ 57 | movable_objects/bot.h \ 58 | movable_objects/clever_bot.h \ 59 | movable_objects/improved_bot.h \ 60 | movable_objects/movable.h \ 61 | movable_objects/rocket.h \ 62 | movable_objects/tank.h 63 | 64 | RESOURCES += resources/resources.qrc 65 | CONFIG += resources_big 66 | 67 | TRANSLATIONS += \ 68 | resources/translations/tanks_be_BY.ts \ 69 | resources/translations/tanks_en_US.ts \ 70 | resources/translations/tanks_ru_RU.ts 71 | 72 | RC_ICONS = resources/app_icon.ico 73 | 74 | VERSION = 0.8.6.0 75 | --------------------------------------------------------------------------------