Release 0.1.9-alpha: Merge branch 'dev'

master
krzys-h 2016-11-02 21:42:22 +01:00
commit b0e34bbe6a
112 changed files with 3244 additions and 893 deletions

3
.gitignore vendored
View File

@ -35,3 +35,6 @@ Makefile
# Ignore QtCreator temp files
CMakeLists.txt.user
CMakeLists.txt.user.*
# Ignore Visual Studio Code files
/.vscode

View File

@ -16,9 +16,9 @@ set(COLOBOT_VERSION_MINOR 1)
set(COLOBOT_VERSION_REVISION 8)
# Used on official releases
set(COLOBOT_VERSION_RELEASE_CODENAME "-alpha")
#set(COLOBOT_VERSION_RELEASE_CODENAME "-alpha")
# Used on unreleased, development builds
#set(COLOBOT_VERSION_UNRELEASED "+alpha")
set(COLOBOT_VERSION_UNRELEASED "+alpha")
# Append git characteristics to version
if(DEFINED COLOBOT_VERSION_UNRELEASED)
@ -132,7 +132,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
message(STATUS "Detected GCC version 4.7+")
set(NORMAL_CXX_FLAGS "-std=gnu++11 -Wall -Wold-style-cast")
set(NORMAL_CXX_FLAGS "-std=gnu++11 -Wall -Wold-style-cast -pedantic-errors")
set(NORMAL_CXX_FLAGS "${NORMAL_CXX_FLAGS} -fno-delete-null-pointer-checks") # Temporary hack, see #828
set(RELEASE_CXX_FLAGS "-O2")
set(DEBUG_CXX_FLAGS "-g -O0")
set(TEST_CXX_FLAGS "-pthread")
@ -144,7 +145,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
message(STATUS "Detected Clang version 3.1+")
set(NORMAL_CXX_FLAGS "-std=c++11 -Wall -Wold-style-cast")
set(NORMAL_CXX_FLAGS "-std=c++11 -Wall -Wold-style-cast -pedantic-errors")
set(RELEASE_CXX_FLAGS "-O2")
set(DEBUG_CXX_FLAGS "-g -O0")
set(TEST_CXX_FLAGS "-pthread")
@ -152,11 +153,15 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
message(STATUS "Detected MSVC compiler")
set(NORMAL_CXX_FLAGS "/wd\"4244\" /wd\"4309\" /wd\"4800\" /wd\"4996\" /wd\"4351\"") # disable some useless warnings
set(RELEASE_CXX_FLAGS "")
set(DEBUG_CXX_FLAGS "")
set(NORMAL_CXX_FLAGS "/wd\"4244\" /wd\"4309\" /wd\"4800\" /wd\"4996\" /wd\"4351\" /EHsc") # disable some useless warnings
set(RELEASE_CXX_FLAGS "/MD")
set(DEBUG_CXX_FLAGS "/MDd /ZI")
set(TEST_CXX_FLAGS "")
add_definitions(-DNOEXCEPT= -DHAS_MSVC_EXCEPTION_BUG)
# Needed for Debug information (it's set to "No" by default for some reason)
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG")
else()
message(FATAL_ERROR "Your C++ compiler doesn't seem to be supported.")
endif()

View File

@ -3,9 +3,11 @@
So you want to contribute to Colobot: Gold Edition? That's awesome! Before you start, read this page, it contains a lot of useful information on how to do so.
## General information
Before you start, read more about technical details of the project. They can be found in our [Game Design Document](http://compiled.colobot.info/jenkins/job/colobot-gold-gdd/lastSuccessfulBuild/artifact/Colobot_Gold_Edition-Game_Design_Document.pdf) ([live editor](http://krzysh.pl:3000/project/545e90d99e8bceed2284797e)).
Before you start, read more about technical details of the project. They can be found in:
You may also find some useful information on the our (outdated) [dev wiki](http://colobot.info/wiki/dev).
* [Developer README](README-dev.md)
* [Doxygen documentation](http://compiled.colobot.info/job/colobot/job/colobot/job/dev/Doxygen/)
* [Game Design Document](http://compiled.colobot.info/job/colobot/job/colobot-misc/job/master/lastSuccessfulBuild/artifact/Colobot_Gold_Edition-Game_Design_Document.pdf)
## Before you start coding
* If you want to fix a bug, please first check the related [issue on GitHub's bug tracker](https://github.com/colobot/colobot/issues). If there isn't one, make it.
@ -13,14 +15,69 @@ You may also find some useful information on the our (outdated) [dev wiki](http:
* Before you start, check *"Assignee"* field in the issue and read the comments to see if nobody else is working on the same issue. If somebody is assigned to it, but there was no activity for a long time, you can take it over. Also, please post a comment on the issue that you want to help us, so other people don't waste time working at that issue in the same time.
## Coding style
See the [related page on dev wiki](http://colobot.info/wiki/dev/Coding_rules) for general guidelines, or [this file](https://github.com/colobot/colobot-lint/blob/master/RULES.md) for detailed description.
When writing code, please adhere to the following rules:
See [colobot-lint repository](https://github.com/colobot/colobot-lint) for automated tool that checks the coding style.
* Indent with spaces, 1 indentation level = 4 spaces. Unix line endings. And don't leave whitespace at the end of lines. Thank you.
* Put braces in new lines.
Like that:
```c++
if (a == b)
{
// ...
}
else
{
// ...
}
```
NOT like that:
```c++
if (a == b) {
// ...
} else {
// ...
}
```
You may omit braces if there is only one line in block:
```c++
if (a == b)
doStuff();
```
* Name functions beginning with upper case, e.g. `FooBar()`
* Name classes beginning with C, e.g. `class CSomeClass`
* Name accessors like so: `SetValue()` and `GetValue()`
* Name structs and enums beginning with uppercase letter, e.g. `struct SomeStruct`
* Enum values should begin with a prefix and underscore and should be all uppercase, e.g. `SOME_ENUM_VALUE`
* Use constant values instead of #define's, e.g. `const int MAX_SPACE = 1000;` instead of `#define MAX_SPACE 1000` (names should be all uppercase as in example)
* Don't use C-style casts, e.g. `(type)(foo)`. Use new C++-style casts, e.g. `static_cast<type>(foo)`.
* Don't use global variables - use static class members whenever possible.
* Provide full namespace qualifier wherever possible, e.g. Math::MultiplyMatrices to avoid confusion.
* Document the new code in Doxygen. Even a short description is better than nothing at all. Also, if you are changing/rewriting old code, please do the same.
* Put comments in your code but not explaining its structure or what it does (Doxygen is for that), but **why** it does so.
* Whenever possible, please write unit tests for your code. Tests should go into `test/` subdirectory in each of the code directories.
* You can use STL classes where needed.
* Throwing exceptions is allowed, with the exception of CBot code (which has no automated memory management yet, so memory leaks could possibly occur)
Also, when writing `#include`s:
* first - in `.cpp` modules - associated `.h` header, e.g. `#include "app/app.h"` in `app.cpp`
* then - local includes, e.g. `#include "common/logger.h"` (listed in alphabetical order for clarity)
* and last - global includes, e.g. `#include <SDL/SDL.h>`, `#include <vector>`
We also have an automated tool for checking the code style. See [colobot-lint repository](https://github.com/colobot/colobot-lint) for details.
If your pull request breaks the coding style, you will have to fix it before it gets merged.
## Commiting rules
Please adhere to the following rules:
* Commits should have meaningful descriptions.
* Commits should not break the build nor tests.
* Changes in one commit should not be too extensive, unless necessary.
* Merges to *master* branch must be discussed beforehand and should include fully finished features if possible.
## Submitting pull requests
After you finish working on your issue and want your code to be merged into the main repository, you should submit a **pull request**. Go to [this page](https://github.com/colobot/colobot/pulls) and select "New pull request". All pull request should ALWAYS be submitted to the *dev* branch. After your PR gets reviewed by our development team, it will be merged to *dev* branch, and on the next release - to the *master* branch.
After you finish working on your issue and want your code to be merged into the main repository, you should submit a **pull request**. Go to [this page](https://github.com/colobot/colobot/pulls) and select "New pull request". All pull requests should ALWAYS be submitted to the *dev* branch. After your PR gets reviewed by our development team, it will be merged to *dev* branch, and on the next release - to the *master* branch.
If you need more help, see [GitHub's help page on Pull Requests](https://help.github.com/articles/using-pull-requests/).

View File

@ -859,7 +859,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH =
IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/docimg"
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program

161
INSTALL-MSYS2.md Normal file
View File

@ -0,0 +1,161 @@
This guide is written to help you with a process of compilation (the newest version of) Colobot: Gold Edition in a Windows environment. [MSYS2](http://sourceforge.net/projects/msys2/) will be used.
Why MSYS2?
----------
You might ask, why you would use this method instead of the other one? Firstly, let's answer a question "why not just use MSYS?".
The biggest problem with the compilation is **3rd party dependencies**. Installing compiler and tools like Git or Cmake is really nothing compared to trying to install endless number of libraries. Why is it that hard, you ask? Well, we, as the developers, would willingly help with this process for example by providing a package with all the needed stuff. Actually, there was a package once, but IT develops fast and new versions of software are released, including not only compiler, but also the libraries. This means that our package might work for compiler v.XX.1, but for v.XX.2 it might not. One of the programmers may use a new feature and Colobot may not compile with lib v.YY. Let's be honest -- it's too hard to provide new packages every time a new version of *something* comes up, at least for us, developers. You can check for yourself if current package works, it probably not.
Here comes MSYS2. It provides everything that MSYS has and much more. It is basically a whole Linux-style development environment for Windows. Most importantly, it gives you a package manager, which makes not only installation of all the needed stuff easy, but also can take care of updating it with little human effort.
Also, with the broken package, if you don't want to manually compile and install all the libraries, MSYS2 might be the only way to go.
Guide
-----
Compiling in Windows is harder than in most Linux distributions, but with MSYS2 it isn't impossible.
### Setting Up an Environment
Steps in this section needs to be done only once. After this, you can basically develop Colobot the same way you would do it normally (well, there might be a problem with integrating Windows-only IDEs).
#### Installation of MSYS2
Read this page really carefully: <http://sourceforge.net/p/msys2/wiki/MSYS2%20installation/> . By following it, you should install and configure MSYS2 without major problems.
#### TIP
When you face any problems during this guide or anywhere in the future, the first thing you should do is to close all MSYS2 processes and run `autorebase.bat` script (it's in the main MSYS2 folder), especially after installation and updating. If you don't do it, odd problems might appear, like CMake not detecting GCC.
#### Running
Now you need to install `MinGW-w64 toolchain`. Open `MSYS2 Shell` and enter the following command:
```sh
pacman -S mingw-w64-i686-toolchain
```
**Warning:** Commands shown in this guide are for 32-bit operating system. If you have 64-bit OS, you must replace all `i686` occurrences with `x86_64`.
MSYS2 creates a new environment (with all the "system" variables set properly) during this installation. You have done that from default `MSYS2 Shell`. To work with GOLD, you need to switch. There are two ways of "opening" an environment you can work in:
1. Open MSYS2 Shell and enter
```sh
export PATH=/mingw32/bin:$PATH
```
or
```sh
export PATH=/mingw64/bin:$PATH
```
2. Open `MinGW-w64 Win32 Shell` (or `MinGW-w64 Win64 Shell`)
You must do one of these two steps every time you want to compile GOLD.
#### TIP
You can add "Open MSYS2 Shell here" to the context menu using registry. Save the following code as `.reg` file and run it.
```
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\Background\shell\open_msys2]
@="Open MSYS2 here"
[HKEY_CLASSES_ROOT\Directory\Background\shell\open_msys2\command]
@="c:\\msys32\\usr\\bin\\mintty.exe /bin/sh -lc 'cd \"$(cygpath \"%V\")\"; export PATH=\"/mingw32/bin:$PATH\"; exec bash'"
[HKEY_CLASSES_ROOT\Folder\shell\open_msys2]
@="Open MSYS2 here"
[HKEY_CLASSES_ROOT\Folder\shell\open_msys2\command]
@="c:\\msys32\\usr\\bin\\mintty.exe /bin/sh -lc 'cd \"$(cygpath \"%V\")\"; export PATH=\"/mingw32/bin:$PATH\"; exec bash'"
```
Remember to modify the paths before you use it so they fit in your installation.
#### Installation of Tools and Dependencies
To compile Colobot you need:
- cmake
- git
- make
- gcc
Install them:
```sh
pacman -S msys2-devel git make mingw-w64-i686-cmake mingw-w64-i686-gcc
```
It's a good time to configure git or even ssh if you plan to push to the remote repository.
When you are done, you can finally get the greatest benefit of using MSYS2. You are going to install required 3rd party libraries. This is how you do it:
1. Find names of all required dependencies. They are listed in the [main INSTALL.md file](INSTALL.md#compiling-on-linux)
2. Find each library using
```sh
pacman -Ss dependency-name
```
3. Install each library using
```sh
pacman -S package-name
```
Easy, isn't it?
If you are lazy, you can just use this one-line command, although there is no guarantee it will work or install everything (might be out of date):
```sh
pacman -S mingw-w64-i686-boost mingw-w64-i686-glew mingw-w64-i686-libpng gettext mingw-w64-i686-gettext mingw-w64-i686-libpng mingw-w64-i686-libsndfile mingw-w64-i686-libvorbis mingw-w64-i686-libogg mingw-w64-i686-openal mingw-w64-i686-SDL2 mingw-w64-i686-SDL2_image mingw-w64-i686-SDL2_ttf
```
You should now have everything set up and working. You can close all instances of MSYS2 and autorebase to ensure everything installed properly.
### Compilation of GOLD
You could say that you have a small Linux inside of your Windows right now. To compile GOLD just follow the instructions for Linux that are available in the repository (https://github.com/colobot/colobot/blob/dev/INSTALL.md\#compiling-on-linux).
**Warning:** You must add `-G"MSYS Makefiles"` argument to `cmake`. For example, when you want to build a developer version:
```sh
cmake -DCMAKE_BUILD_TYPE=Debug -DDEV_BUILD=1 -G"MSYS Makefiles" ..
```
### Dlls
Your executable should run fine if you run it from the shell (like `./colobot` in a folder with a compiled binary). However, it will rather not run if you run it "from Windows", like by double clicking the shortcut. You will get error telling you that some dlls are missing. It's normal on Windows, so don't panic. Linker do dynamic linking by default, so binary must be distributed with the libraries stored in binary dll files. You can provide them in a few ways.
#### PATH
Add `C:\msys32\mingw32\bin` to your environment variable `PATH`.
RMB on Computer -&gt; Click Properties -&gt; Advanced system settings -&gt; Environment Variables -&gt; Edit Path
Be careful though. If you have any other installation of MinGW or other tools like FPC, git and so on, this might result in conflicts (for example two `gcc.exe` files) and the effects are unpredictable.
You can use `PATH` also in other way: just copy all the dlls from `C:\msys32\mingw32\bin` to a place that's already in the `PATH`. This might result in a mess though, because you will mix files for GOLD with something else.
#### Copy
It's the way how it's done in most applications distributed for Windows. Just copy all the needed dlls to a folder with the game. All of them are in `C:\msys32\mingw32\bin`.
You can simply try to run a game and each time it gives you an error copy a missing dll to folder with an executable (like `C:\Program Files\colobot`). Or, you can use this smart command (thanks @krzys-h!):
```sh
ldd colobot.exe | grep -v /c/WINDOWS/ | cut -d ' ' -f 3 | while read -r line; do cp $line /c/path/to/the/game; done
```
#### Static Linking
You can try to compile GOLD with static linking. Nobody have done that (at least in Windows) yet, but it's possible in theory. If you did it, please, let us know!
The End
-------
Hope, this guide helped you with using Windows as a development environment especially for Colobot.
This method was first used and described by @MrSimbax. Script for gaining all the needed dlls was written by @krzys-h.

View File

@ -40,7 +40,7 @@ statically linked Win32 binaries. More information is available in
#### Compiling with MSYS2/MinGW-w64
See [this wiki page](http://colobot.info/wiki/dev/Compiling_GOLD_on_Windows_with_MSYS2) for details.
See the [INSTALL-MSYS2.md](INSTALL-MSYS2.md) file for details.
#### Compiling with MSVC
@ -125,7 +125,7 @@ So if you provided prefix "/some/prefix", you can run:
### Compiling on MacOS X
As of 0.1.2-alpha, we have added MacOS X support. See [INSTALL-MacOSX.md](https://github.com/colobot/colobot/blob/master/INSTALL-MacOSX.md)
As of 0.1.2-alpha, we have added MacOS X support. See [INSTALL-MacOSX.md](INSTALL-MacOSX.md)
file for details.

99
README-dev.md Normal file
View File

@ -0,0 +1,99 @@
# README for developers
This file contains a ton of information useful for development of the game
## Repository setup
All the repositories related to Colobot can be found on our GitHub organization page: https://github.com/colobot
### Main code repository
This is the repository you are currently in.
This repository contains all the source files of Colobot, along with some 3rd party libraries, testing framework and build files for CMake.
### Data repository
The data repository is available at: https://github.com/colobot/colobot-data
This repository contains the data files necessary to run the game. These are level files, textures, models, sounds, music, help files, translations, etc. It contains many binary files, and so, it may grow up to considerable size. If that becomes a problem, we may remove some old revisions of binary files to free up space.
### Branch setup
Current setup is as follows:
* branch **master** - will always contain the best-playable version of the project so that new users could download and compile the project without problems. This branch is not intended for actual development but an integration branch for more "public" releases. The changes should be pulled from general development branch - *dev*. This branch will also have tags at certain commits to denote releases.
* branch **dev** - development branch. This branch is used for general development. Changes committed here should be either pull requests implementing whole features, or incremental commits that do not change many files.
Other **dev-*** branches may be created as needed, for work on major rewriting, or focusing on a set of features.
## 3rd party libraries
3rd party libraries are bundled in `lib/`. They are provided for ease of use since the standard packages are rare in OS distributions.
In case of GTest and GMock, CMake tries to detect if they are available in the system. If so, the system-provided versions will be used.
## CMake build system
The build system is as follows:
* `CMakeLists.txt` - definition of project, list of required packages, build type setup, general compiler options and reference to src subdirectory,
* `src/CMakeLists.txt` - defines the main colobot target,
* `src/CBot/CMakeLists.txt` - defines the CBot library target,
* `src/tools/CMakeLists.txt` - defines tool program targets,
* `data/CMakeLists.txt` - separate file in data submodule, includes routines for creating translations, manpage, icons, etc. and installing them with program.
Test build system is discussed below.
CMake sets some `#defines` which are passed to code in generated headers `common/config.h` and `common/version.h`.
There are only a few include directories specified:
* the main source directory `src/`,
* CMake binary directory (because of generated `common/config.h`),
* `lib/` directory with 3rd party libraries.
Because of the above, include paths in source should always feature the full path from src, e.g. `#include "object/auto/auto.h"`, even if the file lies in the same directory. This will make it easier to move files around and change the `#include` lines with automated replace scripts (some of which are available in *tools/*).
Note that the recommended way of building the project is to use separate build directory, where CMake will generate all targets. In this way, you can keep a clean source directory. The following shell commands illustrate this usage:
```sh
cd <colobot_repository>
mkdir build
cmake ../
make
bin/colobot -datadir ../data
```
## Tests organization
Tests are kept in `test/` directory which includes:
* `test/cbot` - CBOT interpreter for test purposes,
* `test/unit` - automated unit tests.
Each test directory contains own `CMakeLists.txt` specifying targets. Note however that the only targets added as automated tests in CMake are in `test/unit` directory. The other targets are used as development support tools, not automated tests.
Tests can be enabled or disabled using CMake option TESTS (OFF by default). To run the automated tests (you must be in the build directory):
```
./colobot_ut
# or:
make test
# or:
ctest -V .
```
For unit testing, we use Google Test framework (http://code.google.com/p/googletest/) and for mocking objects and function calls, GMock library (http://code.google.com/p/gmock/).
Note that currently, there are very few well-tested parts of code. It is quite impossible to write unit tests for all the code we have but we should test as much code as possible, if not at unit level, then at module level. Hence the test environments and code snippets that enable us to test parts of code without actually running the full-fledged game.
## CI and automated builds
Our team is not large, but since compiling the whole project is lengthy and requires some knowledge, we use some automated services to facilitate the development.
Currently we use Jenkins server hosted at http://compiled.colobot.info/ to run tests and provide compiled game binaries.
Testers are encouraged to use these packages to test the game and report any problems.
## Code documentation
[Doxygen](http://www.stack.nl/~dimitri/doxygen/) is used as the documentation tool.
The documentation is extracted from comment blocks in code by running `make doc`. The resulting HTML files are available in ''doc/'' inside the build directory. Generated documentation files '''will not''' be included in the repository. The idea is that anyone can generate the documentation at any time, in his local copy. Our Jenkins also builds documentation after every commit, see http://compiled.colobot.info/job/colobot/job/colobot/job/dev/Doxygen/
Diagram generation (class inheritance, include dependencies, etc.) is disabled for now to speed up the generation process but you can of course enable it in your local copy.
Currently, only a few classes and structs are documented properly. If you can, please expand the documentation wherever possible.
## Coding rules
Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md#coding-style) file.

2
data

@ -1 +1 @@
Subproject commit faebc8f4bc20121328b993dd388a2a14617c1fbd
Subproject commit 27d3674c6809b4c466e7cc81a7942a94c5280698

BIN
docimg/2d_coord.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

1
docimg/README.txt Normal file
View File

@ -0,0 +1 @@
Images used in Doxygen documentation

View File

@ -1730,6 +1730,9 @@ msgstr ""
msgid "Expression expected after ="
msgstr ""
msgid "Ambiguous call to overloaded function"
msgstr ""
msgid "Dividing by zero"
msgstr ""

View File

@ -86,6 +86,9 @@ msgstr "Trägt schon etwas"
msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)"
msgstr ""
msgid "Ambiguous call to overloaded function"
msgstr ""
msgid "Analysis already performed"
msgstr "Analyse schon durchgeführt"

View File

@ -81,6 +81,9 @@ msgstr "Porte déjà quelque chose"
msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)"
msgstr ""
msgid "Ambiguous call to overloaded function"
msgstr ""
msgid "Analysis already performed"
msgstr "Analyse déjà effectuée"

View File

@ -84,6 +84,9 @@ msgstr "Nie można nieść więcej przedmiotów"
msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)"
msgstr "Alternatywny tryb kamery\\Poruszaj na boki zamiast obracać (w kamerze swobodnej)"
msgid "Ambiguous call to overloaded function"
msgstr "Niejednoznaczne wywołanie przeciążonej funkcji"
msgid "Analysis already performed"
msgstr "Analiza została już wykonana"

127
po/ru.po
View File

@ -37,7 +37,7 @@ msgid "..power cell"
msgstr "Батарею"
msgid "1) First click on the key you want to redefine."
msgstr "1) Сначала нажми на клавишу, которую вы хотите переопределить."
msgstr "1) Сначала нажмите на клавишу, которую вы хотите переопределить."
msgid "2) Then press the key you want to use instead."
msgstr "2) После этого нажмите на клавишу, которую вы хотите использовать."
@ -61,7 +61,7 @@ msgid "Abort\\Abort the current mission"
msgstr "Выход\\Прервать текущую миссию"
msgid "Access beyond array limit"
msgstr "Доступ к массиву за предел"
msgstr "Доступ к элементу за пределами массива"
msgid "Access to solution\\Shows the solution (detailed instructions for missions)"
msgstr "Доступ к решению\\Показывает решение (подробные инструкции для миссий)"
@ -70,7 +70,7 @@ msgid "Access to solutions\\Show program \"4: Solution\" in the exercises"
msgstr "Доступ к решению\\Показывает решение \"4: Решение\" в упражнениях"
msgid "Add new program"
msgstr ""
msgstr "Добавить новую программу"
msgid "Alien Queen"
msgstr "Королева чужих"
@ -82,6 +82,9 @@ msgid "Already carrying something"
msgstr "Уже что-то несу"
msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)"
msgstr "Альтернативный режим камеры\\Движение в стороны вместо поворачивания (в режиме свободной камеры)"
msgid "Ambiguous call to overloaded function"
msgstr ""
msgid "Analysis already performed"
@ -94,7 +97,7 @@ msgid "Analyzes only organic matter"
msgstr "Анализирую только органические вещества"
msgid "Anisotropy level\\Anisotropy level"
msgstr ""
msgstr "Уровень анизотр. фильтр.\\Уровень анизотропной фильтрации"
msgid "Ant"
msgstr "Муравей"
@ -121,13 +124,13 @@ msgid "Automatic indent\\When program editing"
msgstr "Автоматический отступ\\При редактировании программы"
msgid "Autosave interval\\How often your game will autosave"
msgstr ""
msgstr "Интервал автосохр.\\Как часно ваша игра будет сохраняться автоматически"
msgid "Autosave slots\\How many autosave slots you'll have"
msgstr ""
msgstr "Кол-во автосохр.\\Как много мест для автосохранений у вас есть"
msgid "Autosave\\Enables autosave"
msgstr ""
msgstr "Автосохранение\\Включает автоматическое сохранение"
msgid "Back"
msgstr "Назад"
@ -152,7 +155,7 @@ msgid "Black box"
msgstr "Черный ящик"
msgid "Blood\\Display blood when the astronaut is hit"
msgstr ""
msgstr "Кровь\\Показывать кровь когда астронавт ранен"
msgid "Blue"
msgstr "Синий"
@ -179,7 +182,7 @@ msgid "Build a derrick"
msgstr "Построить буровую вышку"
msgid "Build a destroyer"
msgstr ""
msgstr "Построить уничтожитель"
msgid "Build a exchange post"
msgstr "Построить пост по обмену сообщениями"
@ -289,9 +292,8 @@ msgstr "Камера (\\key camera;)"
msgid "Camera back\\Moves the camera backward"
msgstr "Отдалить камеру\\Перемещение камеры назад"
#, fuzzy
msgid "Camera border scrolling\\Scrolling when the mouse touches right or left border"
msgstr "Прокрутка\\Прокрутка, когда указатель мыши касается граней экрана"
msgstr "Прокрутка\\Прокрутка, когда указатель мыши касается правой или левой грани экрана"
msgid "Camera closer\\Moves the camera forward"
msgstr "Приблизать камеру\\Перемещение камеры вперед"
@ -313,10 +315,10 @@ msgid "Camera up\\Turns the camera up"
msgstr "Камера (\\key camera;)"
msgid "Can not produce not researched object"
msgstr ""
msgstr "Невозможно изготовить неизученный объект"
msgid "Can not produce this object in this mission"
msgstr ""
msgstr "Невозможно изготовить этот объект в этой миссии"
msgid "Can't open file"
msgstr "Невозможно открыть файл"
@ -346,7 +348,7 @@ msgid "Chapters:"
msgstr "Разделы:"
msgid "Cheat console\\Show cheat console"
msgstr ""
msgstr "Консоль чит-кодов\\Показать консоль для чит-кодов"
msgid "Checkpoint"
msgstr "Контрольная точка"
@ -355,7 +357,7 @@ msgid "Climb\\Increases the power of the jet"
msgstr "Взлет и подъем\\Увеличивает мощность реактивного двигателя"
msgid "Clone program"
msgstr ""
msgstr "Клонировать программу"
#, fuzzy
msgid "Clone selected program"
@ -371,13 +373,13 @@ msgid "Code battles"
msgstr ""
msgid "Code battles\\Program your robot to be the best of them all!"
msgstr ""
msgstr "Code battles\\Запрограммируйте собственного робота чтобы быть лучшим среди них!"
msgid "Colobot rules!"
msgstr "Правила игры!"
msgid "Colobot: Gold Edition"
msgstr ""
msgstr "Colobot: Gold Edition"
msgid "Command line"
msgstr "Командная строка"
@ -413,7 +415,7 @@ msgid "Custom levels:"
msgstr "Пользовательские уровни:"
msgid "Custom levels\\Levels from mods created by the users"
msgstr ""
msgstr "Пользоват. уровни\\Уровни из модов, созданных пользователями"
msgid "Customize your appearance"
msgstr "Настроить свой внешний вид"
@ -441,7 +443,7 @@ msgid "Descend\\Reduces the power of the jet"
msgstr "Снижение и посадка\\Понижение мощности реактивного двигателя"
msgid "Destroy"
msgstr ""
msgstr "Уничтожить"
msgid "Destroy the building"
msgstr "Уничтожить здание"
@ -475,13 +477,13 @@ msgid "Dust\\Dust and dirt on bots and buildings"
msgstr "Пыль\\Пыль и грязь на ботах и зданиях"
msgid "Dynamic lighting\\Mobile light sources"
msgstr "Динамическое освещение\\Подвижные источники света"
msgstr "Динамич. освещение\\Подвижные источники света"
msgid "Dynamic shadows ++\\Dynamic shadows + self shadowing"
msgstr ""
msgstr "Динамич. тени ++\\Динамические тени + самозатенение"
msgid "Dynamic shadows\\Beautiful shadows!"
msgstr ""
msgstr "Динамические тени\\Прекрасные тени!"
msgid "Edit the selected program"
msgstr "Изменить выбранную программу"
@ -517,13 +519,13 @@ msgid "Exercises\\Programming exercises"
msgstr "Упражнения\\Упражнения по программированию"
msgid "Explode (\\key action;)"
msgstr ""
msgstr "Взорвать (\\key action;)"
msgid "Explosive"
msgstr "Взрывчатка"
msgid "Expression expected after ="
msgstr ""
msgstr "Выражение ожидалось после ="
msgid "Extend shield (\\key action;)"
msgstr "Поднять щит (\\key action;)"
@ -620,7 +622,7 @@ msgid "Gantry crane"
msgstr "Козловой кран"
msgid "Generating"
msgstr ""
msgstr "Выработка"
msgid "Gold Edition development by:"
msgstr "\"Золотое издание\" было разработано:"
@ -647,7 +649,7 @@ msgid "Ground not flat enough"
msgstr "Земля недостаточно плоская"
msgid "Hair color:"
msgstr "Волосы:"
msgstr "Цвет волос:"
msgid "Head\\Face and hair"
msgstr "Голова\\Лицо и волосы"
@ -692,7 +694,7 @@ msgid "Inappropriate cell type"
msgstr "Батарея не подходит"
msgid "Inappropriate object"
msgstr ""
msgstr "Неподходящий объект"
msgid "Incorrect index type"
msgstr "Неверный тип индекса"
@ -731,10 +733,10 @@ msgid "Instructions\\Shows the instructions for the current mission"
msgstr "Инструкции\\Показывает инструкции по текущей миссии"
msgid "Internal error - tell the developers"
msgstr ""
msgstr "Внутренняя ошибка - сообщите разработчикам"
msgid "Invert\\Invert values on this axis"
msgstr ""
msgstr "Инвертир.\\Инвертировать значения по этой оси"
msgid "Jet temperature"
msgstr "Температура реактивного двигателя"
@ -794,21 +796,19 @@ msgid "Load\\Loads the selected mission"
msgstr "Загрузить\\Загрузить выбранную миссию"
msgid "Loading basic level settings"
msgstr ""
msgstr "Загрузка основных настроек уровня"
#, fuzzy
msgid "Loading finished!"
msgstr "Программа выполнена"
msgstr "Загрузка завершена!"
msgid "Loading music"
msgstr ""
msgstr "Загрузка музыки"
#, fuzzy
msgid "Loading objects"
msgstr "Список объектов"
msgstr "Загрузка объектов"
msgid "Loading terrain"
msgstr ""
msgstr "Загрузка местности"
msgid "Lowest\\Minimum graphic quality (highest frame rate)"
msgstr "Низкое\\Минимальное качество графики (быстро)"
@ -882,7 +882,7 @@ msgid "Next object\\Selects the next object"
msgstr "Следующий объект\\Выбор следующего объекта"
msgid "No"
msgstr ""
msgstr "Нет"
msgid "No energy in the subsoil"
msgstr "Под землей нет запасов энергии"
@ -906,7 +906,7 @@ msgid "No more energy"
msgstr "Нет энергии"
msgid "No ore in the subsoil"
msgstr ""
msgstr "Под поверхностью нет руды"
msgid "No power cell"
msgstr "Нет батареи"
@ -921,13 +921,13 @@ msgid "No titanium ore to convert"
msgstr "Нет титановых руд для преобразования"
msgid "No titanium to transform"
msgstr ""
msgstr "Нет титана для преобразования"
msgid "No uranium to transform"
msgstr "Нет урана для преобразования"
msgid "No userlevels installed!"
msgstr ""
msgstr "Не установленны пользовательские уровни!"
msgid "Normal size"
msgstr "Нормальный размер"
@ -945,7 +945,7 @@ msgid "Not enough energy yet"
msgstr "Не хватает энергии"
msgid "Not found anything to destroy"
msgstr ""
msgstr "Ничего не найдено для уничтожения"
msgid "Nothing to analyze"
msgstr "Нечего анализировать"
@ -1032,16 +1032,16 @@ msgid "Paste (Ctrl+V)"
msgstr "Вставить (Ctrl+V)"
msgid "Pause blur\\Blur the background on the pause screen"
msgstr ""
msgstr "Размытие при паузе\\Размытие фона во время паузы"
msgid "Pause in background\\Pause the game when the window is unfocused"
msgstr ""
msgstr "Пауза в фоне\\Приостанавливает игру когда окно не в фокусе"
msgid "Pause/continue"
msgstr "Пауза/продолжить"
msgid "Pause\\Pause the game without opening menu"
msgstr ""
msgstr "Пауза\\Приостанавливает игру без открытия меню"
msgid "Phazer shooter"
msgstr "Фазовый стрелок"
@ -1125,7 +1125,7 @@ msgid "Private\\Private folder"
msgstr "Личное\\Личная папка"
msgid "Processing level file"
msgstr ""
msgstr "Обработка файла уровня"
#, fuzzy
msgid "Program cloned"
@ -1165,7 +1165,7 @@ msgid "Quake at explosions\\The screen shakes at explosions"
msgstr "Землетряс. при взрывах\\Тряска экрана при взрывах"
msgid "Quit\\Quit Colobot: Gold Edition"
msgstr ""
msgstr "Выход\\Выйти из Colobot: Gold Edition"
msgid "Quit\\Quit the current mission or exercise"
msgstr "Выход\\Выход из текущей миссии"
@ -1192,7 +1192,7 @@ msgid "Red flag"
msgstr "Красный флаг"
msgid "Reflections on the buttons \\Shiny buttons"
msgstr "Отражения кнопок \\Блестящие кнопки"
msgstr "Отражения на кнопках \\Блестящие кнопки"
msgid "Remains of Apollo mission"
msgstr "Остатки миссии Аполлон"
@ -1239,7 +1239,7 @@ msgid "Restoring CBot execution state"
msgstr ""
msgid "Restoring saved objects"
msgstr ""
msgstr "Восстановить сохранённые объекты"
msgid "Return to start"
msgstr "Вернуться в начало"
@ -1308,7 +1308,7 @@ msgid "Semicolon terminator missing"
msgstr "Отсутствует точка с запятой"
msgid "Shadow resolution\\Higher means better range and quality, but slower"
msgstr ""
msgstr "Разрешение тени\\Больше означает более лучшие дистанция и качество, но более медленные"
msgid "Shield level"
msgstr "Уровень брони"
@ -1457,13 +1457,13 @@ msgid "Target bot"
msgstr "Целевой бот"
msgid "Terrain relief"
msgstr ""
msgstr "Рельеф местности"
msgid "Texture filtering\\Texture filtering"
msgstr ""
msgstr "Фильтрация текстур\\Фильтрация текстур"
msgid "Textures"
msgstr ""
msgstr "Текстуры"
msgid "The expression must return a boolean value"
msgstr "Выражение должно возвращать логическое значение"
@ -1484,7 +1484,7 @@ msgid "This class does not exist"
msgstr "Этот класс не существует"
msgid "This is example code that cannot be run directly"
msgstr ""
msgstr "Это пример кода который не может быть непосредственно запущен"
msgid "This is not a member of this class"
msgstr "Это не член этого класса"
@ -1493,13 +1493,13 @@ msgid "This label does not exist"
msgstr "Эта метка не существует"
msgid "This menu is for userlevels from mods, but you didn't install any"
msgstr ""
msgstr "Это меню для пользовательских уровней из модов, но вы ни одного не уставили"
msgid "This object is not a member of a class"
msgstr "Этот объект не член класса"
msgid "This program is read-only, clone it to edit"
msgstr ""
msgstr "Эта программа только для чтения, для редактирования клонируйте её"
msgid "Thump (\\key action;)"
msgstr "Удар (\\key action;)"
@ -1529,13 +1529,13 @@ msgid "Too close to a building"
msgstr "Слишком близко к зданию"
msgid "Too close to an existing flag"
msgstr ""
msgstr "Слишком близко к существующему флагу"
msgid "Too close to space ship"
msgstr "Слишком близко к кораблю"
msgid "Too many flags of this color (maximum 5)"
msgstr ""
msgstr "Слишком много флагов этого цвета (максимум 5)"
msgid "Too many parameters"
msgstr "Слишком много параметров"
@ -1577,7 +1577,7 @@ msgid "Type declaration missing"
msgstr "Не задан тип"
msgid "Unable to control enemy objects"
msgstr ""
msgstr "Невозможно контролировать вражеские объекты"
msgid "Undo (Ctrl+Z)"
msgstr "Отмена (Ctrl+Z)"
@ -1679,7 +1679,7 @@ msgid "Yellow flag"
msgstr "Желтый флаг"
msgid "Yes"
msgstr ""
msgstr "Да"
msgid "You can fly with the keys (\\key gup;) and (\\key gdown;)"
msgstr "Вы можете лететь с помощью клавиш (\\key gup;) и (\\key gdown;)"
@ -1761,7 +1761,7 @@ msgid "\\Red flags"
msgstr "\\Красный флаг"
msgid "\\Return to Colobot: Gold Edition"
msgstr ""
msgstr "Вернуться к Colobot: Gold Edition"
msgid "\\SatCom on standby"
msgstr "\\SatCom ждет"
@ -1794,7 +1794,7 @@ msgid "\\Use the orange pencil"
msgstr "\\Использовать оранжевое перо"
msgid "\\Use the purple pencil"
msgstr ""
msgstr "Использовать фиолетовое перо"
msgid "\\Use the red pencil"
msgstr "\\Использовать красное перо"
@ -1833,7 +1833,7 @@ msgstr "epsitec.com"
#~ msgstr "Здание слишком близко"
#~ msgid "COLOBOT"
#~ msgstr "КОЛОБОТ"
#~ msgstr "COLOBOT"
#~ msgid "Camera awayest"
#~ msgstr "Отдалить камеру"
@ -1871,9 +1871,8 @@ msgstr "epsitec.com"
#~ msgid "Developed by :"
#~ msgstr "Разработка :"
#, fuzzy
#~ msgid "Do you want to quit Colobot: Gold Edition?"
#~ msgstr "Вы хотите закрыть COLOBOT?"
#~ msgstr "Вы хотите закрыть Colobot: Gold Edition?"
#~ msgid "Exit film\\Film at the exit of exercises"
#~ msgstr "Ролик при выходе\\Ролик во время выхода из упражнения"

View File

@ -92,7 +92,7 @@ void CBotClass::ClearPublic()
////////////////////////////////////////////////////////////////////////////////
void CBotClass::Purge()
{
if ( this == nullptr ) return;
assert ( this != nullptr );
delete m_pVar;
m_pVar = nullptr;
@ -104,7 +104,7 @@ void CBotClass::Purge()
m_nbVar = m_parent == nullptr ? 0 : m_parent->m_nbVar;
m_next->Purge();
if (m_next != nullptr) m_next->Purge();
m_next = nullptr; // no longer belongs to this chain
}
@ -205,7 +205,7 @@ std::string CBotClass::GetName()
////////////////////////////////////////////////////////////////////////////////
CBotClass* CBotClass::GetParent()
{
if ( this == nullptr ) return nullptr;
assert ( this != nullptr );
return m_parent;
}
@ -333,7 +333,7 @@ CBotTypResult CBotClass::CompileMethode(const std::string& name,
r = m_pMethod->CompileCall(name, ppParams, nIdent);
if ( r.Eq(CBotErrUndefCall) && m_parent != nullptr )
return m_parent->m_pMethod->CompileCall(name, ppParams, nIdent);
return m_parent->CompileMethode(name, pThis, ppParams, pStack, nIdent);
return r;
}
@ -354,9 +354,7 @@ bool CBotClass::ExecuteMethode(long& nIdent,
if (m_parent != nullptr)
{
ret = m_parent->m_pCalls->DoCall(name, pThis, ppParams, pResult, pStack, pToken);
if (ret >= 0) return ret;
ret = m_parent->m_pMethod->DoCall(nIdent, name, pThis, ppParams, pStack, pToken, m_parent);
ret = m_parent->ExecuteMethode(nIdent, name, pThis, ppParams, pResult, pStack, pToken);
}
return ret;
}
@ -368,7 +366,14 @@ void CBotClass::RestoreMethode(long& nIdent,
CBotVar** ppParams,
CBotStack*& pStack)
{
m_pMethod->RestoreCall(nIdent, name, pThis, ppParams, pStack, this);
CBotClass* pClass = this;
while (pClass != nullptr)
{
bool ok = pClass->m_pMethod->RestoreCall(nIdent, name, pThis, ppParams, pStack, pClass);
if (ok) return;
pClass = pClass->m_parent;
}
assert(false);
}
////////////////////////////////////////////////////////////////////////////////
@ -504,16 +509,25 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack)
classe->Purge(); // empty the old definitions // TODO: Doesn't this remove all classes of the current program?
classe->m_IsDef = false; // current definition
classe->m_pOpenblk = p;
if ( !IsOfType( p, ID_OPBLK) )
{
pStack->SetError(CBotErrOpenBlock, p);
return nullptr;
}
while ( pStack->IsOk() && !IsOfType( p, ID_CLBLK ) )
int level = 1;
do // skip over the definition
{
classe->CompileDefItem(p, pStack, false);
int type = p->GetType();
p = p->GetNext();
if (type == ID_OPBLK) level++;
if (type == ID_CLBLK) level--;
}
while (level > 0 && p != nullptr);
if (level > 0) pStack->SetError(CBotErrCloseBlock, classe->m_pOpenblk);
if (pStack->IsOk()) return classe;
}
@ -521,6 +535,26 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack)
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
void CBotClass::DefineClasses(CBotClass* pClass, CBotCStack* pStack)
{
while (pClass != nullptr)
{
CBotClass* pParent = pClass->m_parent;
pClass->m_nbVar = (pParent == nullptr) ? 0 : pParent->m_nbVar;
CBotToken* p = pClass->m_pOpenblk->GetNext();
while (pStack->IsOk() && !IsOfType(p, ID_CLBLK))
{
pClass->CompileDefItem(p, pStack, false);
}
if (!pStack->IsOk()) return;
pClass = pClass->GetNext();
}
}
////////////////////////////////////////////////////////////////////////////////
bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
{
@ -605,7 +639,6 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
{
// return a method precompiled in pass 1
CBotFunction* pf = m_pMethod;
CBotFunction* prev = nullptr;
CBotToken* ppp = p;
CBotCStack* pStk = pStack->TokenStack(nullptr, true);
CBotDefParam* params = CBotDefParam::Compile(p, pStk );
@ -614,7 +647,6 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
while ( pf != nullptr ) // search by name and parameters
{
if (pf->GetName() == pp && pf->CheckParam( params )) break;
prev = pf;
pf = pf->Next();
}
@ -650,6 +682,7 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
initType = CBotVar::InitType::DEF;
pcopy->SetInit(initType);
pcopy->SetUniqNum(pv->GetUniqNum());
pcopy->SetPrivate(pv->GetPrivate());
pile->AddVar(pcopy);
pv = pv->GetNext();
}
@ -659,18 +692,12 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
// compiles a method
p = pBase;
CBotFunction* f =
CBotFunction::Compile(p, pile, nullptr/*, false*/);
CBotFunction::Compile(p, pile, pf/*, false*/);
if ( f != nullptr )
{
f->m_pProg = pStack->GetProgram();
f->m_bSynchro = bSynchro;
// replaces the element in the chain
f->m_next = pf->m_next;
pf->m_next = nullptr;
delete pf;
if (prev == nullptr) m_pMethod = f;
else prev->m_next = f;
}
pStack->Return(nullptr, pile);
}

View File

@ -291,6 +291,14 @@ public:
static CBotClass* Compile1(CBotToken* &p,
CBotCStack* pStack);
/*!
* \brief DefineClasses Calls CompileDefItem for each class in a list
* of classes, defining fields and pre-compiling methods.
* \param pClass List of classes
* \param pStack
*/
static void DefineClasses(CBotClass* pClass, CBotCStack* pStack);
/*!
* \brief CompileDefItem
* \param p
@ -385,6 +393,8 @@ private:
CBotFunction* m_pMethod;
void (*m_rUpdate)(CBotVar* thisVar, void* user);
CBotToken* m_pOpenblk;
//! How many times the program currently holding the lock called Lock()
int m_lockCurrentCount = 0;
//! Programs waiting for lock. m_lockProg[0] is the program currently holding the lock, if any

View File

@ -132,6 +132,11 @@ bool CBotDefParam::Execute(CBotVar** ppVars, CBotStack* &pj)
(static_cast<CBotVarClass*>(newvar))->Copy(ppVars[i], false);
break;
case CBotTypPointer:
{
newvar->SetPointer(ppVars[i]->GetPointer());
newvar->SetType(p->m_type); // keep pointer type
}
break;
case CBotTypArrayPointer:
{
newvar->SetPointer(ppVars[i]->GetPointer());

View File

@ -236,6 +236,7 @@ enum CBotError : int
CBotErrPrivate = 5041, //!< protected item
CBotErrNoPublic = 5042, //!< missing word "public"
CBotErrNoExpression = 5043, //!< expression expected after =
CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function
// Runtime errors
CBotErrZeroDiv = 6000, //!< division by zero

View File

@ -19,6 +19,7 @@
#include "CBot/CBotInstr/CBotDefClass.h"
#include "CBot/CBotInstr/CBotExprRetVar.h"
#include "CBot/CBotInstr/CBotInstrUtils.h"
#include "CBot/CBotInstr/CBotLeftExprVar.h"
@ -44,6 +45,7 @@ CBotDefClass::CBotDefClass()
m_expr = nullptr;
m_hasParams = false;
m_nMethodeIdent = 0;
m_exprRetVar = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
@ -150,6 +152,16 @@ CBotInstr* CBotDefClass::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* p
goto error;
}
pStk->SetCopyVar(var);
// chained method ?
if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStk, true)))
{
inst->m_exprRetVar->SetToken(vartoken);
delete pStk->TokenStack();
}
pStk->SetVar(nullptr);
if ( !pStk->IsOk() ) goto error;
}
if (IsOfType(p, ID_ASS)) // with a assignment?
@ -174,7 +186,8 @@ CBotInstr* CBotDefClass::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* p
CBotClass* result = pStk->GetClass();
if ( !pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_POINTER).Eq(CBotTypNullPointer) &&
( !pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_POINTER).Eq(CBotTypPointer) ||
( result != nullptr && !pClass->IsChildOf(result) ))) // type compatible ?
( result != nullptr && !(pClass->IsChildOf(result) ||
result->IsChildOf(pClass))))) // type compatible ?
{
pStk->SetError(CBotErrBadType1, p->GetStart());
goto error;
@ -231,6 +244,19 @@ bool CBotDefClass::Execute(CBotStack* &pj)
CBotStack* pile = pj->AddStack(this);//essential for SetState()
// if ( pile == EOX ) return true;
if (m_exprRetVar != nullptr) // Class c().method();
{
if (pile->IfStep()) return false;
if (pile->GetState() == 4)
{
CBotStack* pile3 = pile->AddStack();
if (!m_exprRetVar->Execute(pile3)) return false;
pile3->SetVar(nullptr);
pile->Return(pile3); // release pile3 stack
pile->SetState(5);
}
}
CBotToken* pt = &m_token;
CBotClass* pClass = CBotClass::Find(pt);
@ -268,9 +294,10 @@ bool CBotDefClass::Execute(CBotStack* &pj)
// evaluates the expression for the assignment
if (!m_expr->Execute(pile)) return false;
CBotVar* pv = pile->GetVar();
if ( bIntrincic )
{
CBotVar* pv = pile->GetVar();
if ( pv == nullptr || pv->GetPointer() == nullptr )
{
pile->SetError(CBotErrNull, &m_token);
@ -280,9 +307,20 @@ bool CBotDefClass::Execute(CBotStack* &pj)
}
else
{
if ( !(pv == nullptr || pv->GetPointer() == nullptr) )
{
if ( !pv->GetClass()->IsChildOf(pClass))
{
pile->SetError(CBotErrBadType1, &m_token);
return pj->Return(pile);
}
}
CBotVarClass* pInstance;
pInstance = (static_cast<CBotVarPointer*>(pile->GetVar()))->GetPointer(); // value for the assignment
pInstance = pv->GetPointer(); // value for the assignment
CBotTypResult type = pThis->GetTypResult();
pThis->SetPointer(pInstance);
pThis->SetType(type); // keep pointer type
}
pThis->SetInit(CBotVar::InitType::DEF);
}
@ -350,6 +388,14 @@ bool CBotDefClass::Execute(CBotStack* &pj)
pile->SetState(3); // finished this part
}
if (m_exprRetVar != nullptr && pile->GetState() == 3) // Class c().method();
{
CBotStack* pile3 = pile->AddStack();
pile3->SetCopyVar(pThis);
pile->SetState(4);
return false; // go back to the top ^^^
}
if ( pile->IfStep() ) return false;
if ( m_next2b != nullptr &&
@ -374,6 +420,16 @@ void CBotDefClass::RestoreState(CBotStack* &pj, bool bMain)
pThis->SetUniqNum((static_cast<CBotLeftExprVar*>(m_var))->m_nIdent); // its attribute a unique number
}
if (m_exprRetVar != nullptr) // Class c().method();
{
if (pile->GetState() == 4)
{
CBotStack* pile3 = pile->RestoreStack();
m_exprRetVar->RestoreState(pile3, bMain);
return;
}
}
CBotToken* pt = &m_token;
CBotClass* pClass = CBotClass::Find(pt);
bool bIntrincic = pClass->IsIntrinsic();

View File

@ -85,6 +85,9 @@ private:
//! Constructor method unique identifier
long m_nMethodeIdent;
//! Instruction to chain method calls after constructor
CBotInstr* m_exprRetVar;
};
} // namespace CBot

View File

@ -0,0 +1,209 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include <sstream>
#include "CBot/CBotInstr/CBotExprRetVar.h"
#include "CBot/CBotInstr/CBotExpression.h"
#include "CBot/CBotInstr/CBotInstrMethode.h"
#include "CBot/CBotInstr/CBotIndexExpr.h"
#include "CBot/CBotInstr/CBotFieldExpr.h"
#include "CBot/CBotStack.h"
namespace CBot
{
////////////////////////////////////////////////////////////////////////////////
CBotExprRetVar::CBotExprRetVar()
{
}
////////////////////////////////////////////////////////////////////////////////
CBotExprRetVar::~CBotExprRetVar()
{
}
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bMethodsOnly)
{
if (p->GetType() == ID_DOT)
{
CBotVar* var = pStack->GetVar();
if (var == nullptr) return nullptr;
CBotCStack* pStk = pStack->TokenStack();
CBotInstr* inst = new CBotExprRetVar();
while (true)
{
pStk->SetStartError(p->GetStart());
if (var->GetType() == CBotTypArrayPointer)
{
if (bMethodsOnly) goto err;
if (IsOfType( p, ID_OPBRK ))
{
CBotIndexExpr* i = new CBotIndexExpr();
i->m_expr = CBotExpression::Compile(p, pStk);
inst->AddNext3(i);
var = var->GetItem(0,true);
if (i->m_expr == nullptr || pStk->GetType() != CBotTypInt)
{
pStk->SetError(CBotErrBadIndex, p->GetStart());
goto err;
}
if (!pStk->IsOk() || !IsOfType( p, ID_CLBRK ))
{
pStk->SetError(CBotErrCloseIndex, p->GetStart());
goto err;
}
continue;
}
}
if (var->GetType(CBotVar::GetTypeMode::CLASS_AS_POINTER) == CBotTypPointer)
{
if (IsOfType(p, ID_DOT))
{
CBotToken* pp = p;
if (p->GetType() == TokenTypVar)
{
if (p->GetNext()->GetType() == ID_OPENPAR)
{
CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var, bMethodsOnly);
if (!pStk->IsOk()) goto err;
inst->AddNext3(i);
return pStack->Return(inst, pStk);
}
else if (bMethodsOnly)
{
p = p->GetPrev();
goto err;
}
else
{
CBotFieldExpr* i = new CBotFieldExpr();
i->SetToken(pp);
inst->AddNext3(i);
CBotVar* preVar = var;
var = var->GetItem(p->GetString());
if (var != nullptr)
{
i->SetUniqNum(var->GetUniqNum());
if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var))
{
pStk->SetError(CBotErrPrivate, pp);
goto err;
}
}
}
if (var != nullptr)
{
p = p->GetNext();
continue;
}
pStk->SetError(CBotErrUndefItem, p);
goto err;
}
pStk->SetError(CBotErrUndefClass, p);
goto err;
}
}
break;
}
pStk->SetCopyVar(var);
if (pStk->IsOk()) return pStack->Return(inst, pStk);
pStk->SetError(CBotErrUndefVar, p);
err:
delete inst;
return pStack->Return(nullptr, pStk);
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
bool CBotExprRetVar::Execute(CBotStack* &pj)
{
CBotStack* pile = pj->AddStack();
CBotStack* pile1 = pile;
CBotVar* pVar;
if (pile1->GetState() == 0)
{
pVar = pj->GetVar();
pVar->Update(pj->GetUserPtr());
if (pVar->GetType(CBotVar::GetTypeMode::CLASS_AS_POINTER) == CBotTypNullPointer)
{
pile1->SetError(CBotErrNull, &m_token);
return pj->Return(pile1);
}
if ( !m_next3->ExecuteVar(pVar, pile, &m_token, true, false) )
return false;
if (pVar)
pile1->SetCopyVar(pVar);
else
return pj->Return(pile1);
pile1->IncState();
}
pVar = pile1->GetVar();
if (pVar == nullptr)
{
return pj->Return(pile1);
}
if (pVar->IsUndefined())
{
pile1->SetError(CBotErrNotInit, &m_token);
return pj->Return(pile1);
}
return pj->Return(pile1);
}
////////////////////////////////////////////////////////////////////////////////
void CBotExprRetVar::RestoreState(CBotStack* &pj, bool bMain)
{
if (!bMain) return;
CBotStack* pile = pj->RestoreStack();
if ( pile == nullptr ) return;
if (pile->GetState() == 0)
m_next3->RestoreStateVar(pile, bMain);
}
std::string CBotExprRetVar::GetDebugData()
{
std::stringstream ss;
ss << m_token.GetString() << "func(...).something" << std::endl;
return ss.str();
}
} // namespace CBot

View File

@ -0,0 +1,63 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "CBot/CBotInstr/CBotInstr.h"
namespace CBot
{
/**
* \brief Access a member/element of the variable on the stack
*
*
*
*/
class CBotExprRetVar : public CBotInstr
{
public:
CBotExprRetVar();
~CBotExprRetVar();
static CBotInstr* Compile(CBotToken*& p, CBotCStack* pStack, bool bMethodsOnly = false);
/*!
* \brief Execute
* \param pj
* \return
*/
bool Execute(CBotStack* &pj) override;
/*!
* \brief RestoreState
* \param pj
* \param bMain
*/
void RestoreState(CBotStack* &pj, bool bMain) override;
protected:
virtual const std::string GetDebugName() override { return "CBotExprRetVar"; }
virtual std::string GetDebugData() override;
private:
};
} // namespace CBot

View File

@ -67,8 +67,7 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot
if (ident > 0 && ident < 9000)
{
if ( var->IsPrivate(privat) &&
!pStk->GetProgram()->m_bCompileClass)
if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, privat))
{
pStk->SetError(CBotErrPrivate, p);
goto err;
@ -133,12 +132,12 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot
CBotFieldExpr* i = new CBotFieldExpr(); // new element
i->SetToken(pp); // keeps the name of the token
inst->AddNext3(i); // add after
CBotVar* preVar = var;
var = var->GetItem(p->GetString()); // get item correspondent
if (var != nullptr)
{
i->SetUniqNum(var->GetUniqNum());
if ( var->IsPrivate() &&
!pStk->GetProgram()->m_bCompileClass)
if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, privat))
{
pStk->SetError(CBotErrPrivate, pp);
goto err;

View File

@ -134,4 +134,57 @@ std::string CBotFieldExpr::GetDebugData()
return ss.str();
}
////////////////////////////////////////////////////////////////////////////////
bool CBotFieldExpr::CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar,
CBotVar::ProtectionLevel privat)
{
CBotVar::ProtectionLevel varPriv = pVar->GetPrivate();
if (privat == CBotVar::ProtectionLevel::ReadOnly && varPriv == privat)
return true;
if (varPriv == CBotVar::ProtectionLevel::Public) return false;
std::string prevName = (pPrev == nullptr) ? "" : pPrev->GetName();
// implicit 'this.'var, this.var, or super.var
if (pPrev == nullptr || prevName == "this" || prevName == "super") // member of the current class
{
if (varPriv == CBotVar::ProtectionLevel::Private) // var is private ?
{
CBotToken token("this");
CBotVar* pThis = pStack->FindVar(token);
CBotClass* pClass = pThis->GetClass(); // the current class
CBotVar* pVarList = pClass->GetVar();
int ident = pVar->GetUniqNum();
// check if var is inherited from a parent class
if (pVarList == nullptr || ident < pVarList->GetUniqNum())
return true;
}
}
else // any other context
{
if (pVar->IsPrivate()) // var is protected or private ?
{
CBotToken token("this");
CBotVar* pThis = pStack->FindVar(token);
if (pThis == nullptr) return true; // inside a function ?
if (pThis->GetType() != CBotTypPointer) return true;
CBotClass* pClass = pThis->GetClass(); // the current class
if (!pClass->IsChildOf(pPrev->GetClass())) // var is member of some other class ?
return true;
if (varPriv == CBotVar::ProtectionLevel::Private && // private member of a parent class
pClass != pPrev->GetClass()) return true;
}
}
return false;
}
} // namespace CBot

View File

@ -65,6 +65,21 @@ public:
*/
void RestoreStateVar(CBotStack* &pj, bool bMain) override;
/*!
* \brief Check if access to a variable is allowed or not depending on public/private/protected setting
*
* If this function returns true, the caller is responsible for failing the compilation with ::CBotErrPrivate error.
* This function doesn't set the error flag itself.
*
* \param pStack Current compilation stack frame
* \param pPrev Class instance which variable to check is part of, or nullptr if not part of a class
* \param pVar Variable to check
* \param privat CBotVar::ProtectionLevel::ReadOnly if requesting read-only access, anything else otherwise
* \return true if pVar is inaccessible in the current context, false if access should be allowed
*/
static bool CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar,
CBotVar::ProtectionLevel privat = CBotVar::ProtectionLevel::Protected);
protected:
virtual const std::string GetDebugName() override { return "CBotFieldExpr"; }
virtual std::string GetDebugData() override;

View File

@ -224,10 +224,6 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct
func->m_closeblk = (p != nullptr && p->GetPrev() != nullptr) ? *(p->GetPrev()) : CBotToken();
if ( pStk->IsOk() )
{
if ( func->m_bPublic ) // public function, return known for all
{
CBotFunction::AddPublic(func);
}
return pStack->ReturnFunc(func, pStk);
}
}
@ -466,8 +462,7 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n
if ( name.empty() ) return nullptr;
int delta = 99999; // seeks the lowest signature
CBotFunction* pFunc = nullptr; // the best function found
std::map<CBotFunction*, int> funcMap;
if ( this != nullptr )
{
@ -482,44 +477,48 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n
CBotVar* pw = ppVars[i++]; // provided list parameter
while ( pv != nullptr && pw != nullptr)
{
if (!TypesCompatibles(pv->GetTypResult(), pw->GetTypResult()))
CBotTypResult paramType = pv->GetTypResult();
CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
if (!TypesCompatibles(paramType, argType))
{
if ( pFunc == nullptr ) TypeOrError = CBotErrBadParam;
if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam);
break;
}
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer))
{
CBotClass* c1 = paramType.GetClass();
CBotClass* c2 = argType.GetClass();
while (c2 != c1 && c2 != nullptr) // implicit cast
{
alpha += 10;
c2 = c2->GetParent();
}
}
else
{
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
}
pv = pv->GetNext();
pw = ppVars[i++];
}
if ( pw != nullptr )
{
if ( pFunc != nullptr ) continue;
if ( !funcMap.empty() ) continue;
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
continue; // too many parameters
}
if ( pv != nullptr )
{
if ( pFunc != nullptr ) continue;
if ( !funcMap.empty() ) continue;
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
continue; // not enough parameters
}
if (alpha == 0) // perfect signature
{
nIdent = pt->m_nFuncIdent;
TypeOrError = pt->m_retTyp;
return pt;
}
if ( alpha < delta ) // a better signature?
{
pFunc = pt;
delta = alpha;
}
funcMap.insert( std::pair<CBotFunction*, int>(pt, alpha) );
}
}
}
@ -537,50 +536,72 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n
CBotVar* pw = ppVars[i++]; // list of provided parameters
while ( pv != nullptr && pw != nullptr)
{
if (!TypesCompatibles(pv->GetTypResult(), pw->GetTypResult()))
CBotTypResult paramType = pv->GetTypResult();
CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
if (!TypesCompatibles(paramType, argType))
{
if ( pFunc == nullptr ) TypeOrError = CBotErrBadParam;
if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam);
break;
}
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer))
{
CBotClass* c1 = paramType.GetClass();
CBotClass* c2 = argType.GetClass();
while (c2 != c1 && c2 != nullptr) // implicit cast
{
alpha += 10;
c2 = c2->GetParent();
}
}
else
{
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
}
pv = pv->GetNext();
pw = ppVars[i++];
}
if ( pw != nullptr )
{
if ( pFunc != nullptr ) continue;
if ( !funcMap.empty() ) continue; // previous useable function
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
continue; // to many parameters
}
if ( pv != nullptr )
{
if ( pFunc != nullptr ) continue;
if ( !funcMap.empty() ) continue; // previous useable function
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
continue; // not enough parameters
}
if (alpha == 0) // perfect signature
{
nIdent = pt->m_nFuncIdent;
TypeOrError = pt->m_retTyp;
return pt;
}
if ( alpha < delta ) // a better signature?
{
pFunc = pt;
delta = alpha;
}
funcMap.insert( std::pair<CBotFunction*, int>(pt, alpha) );
}
}
}
if ( pFunc != nullptr )
if ( !funcMap.empty() )
{
auto it = funcMap.begin();
CBotFunction* pFunc = it->first; // the best function found
signed int delta = it->second; // seeks the lowest signature
for (++it ; it != funcMap.end() ; it++)
{
if (it->second < delta) // a better signature?
{
TypeOrError.SetType(CBotNoErr);
pFunc = it->first;
delta = it->second;
continue;
}
if (it->second == delta) TypeOrError.SetType(CBotErrAmbiguousCall);
}
if (TypeOrError.Eq(CBotErrAmbiguousCall)) return nullptr;
nIdent = pFunc->m_nFuncIdent;
TypeOrError = pFunc->m_retTyp;
return pFunc;
@ -801,7 +822,7 @@ int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar* pThis,
}
////////////////////////////////////////////////////////////////////////////////
void CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* pThis, CBotVar** ppVars,
bool CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* pThis, CBotVar** ppVars,
CBotStack* pStack, CBotClass* pClass)
{
CBotTypResult type;
@ -810,14 +831,20 @@ void CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* p
if ( pt != nullptr )
{
CBotStack* pStk = pStack->RestoreStack(pt);
if ( pStk == nullptr ) return;
if ( pStk == nullptr ) return true;
pStk->SetProgram(pt->m_pProg); // it may have changed module
CBotVar* pthis = pStk->FindVar("this");
pthis->SetUniqNum(-2);
if (pClass->GetParent() != nullptr)
{
CBotVar* psuper = pStk->FindVar("super");
if (psuper != nullptr) psuper->SetUniqNum(-3);
}
CBotStack* pStk3 = pStk->RestoreStack(nullptr); // to set parameters passed
if ( pStk3 == nullptr ) return;
if ( pStk3 == nullptr ) return true;
pt->m_param->RestoreState(pStk3, true); // parameters
@ -831,7 +858,9 @@ void CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* p
// finally calls the found function
pt->m_block->RestoreState(pStk3, true); // interrupt !
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -179,8 +179,9 @@ public:
* \param ppVars
* \param pStack
* \param pClass
* \return Returns true if the method call was restored.
*/
void RestoreCall(long& nIdent,
bool RestoreCall(long& nIdent,
const std::string& name,
CBotVar* pThis,
CBotVar** ppVars,

View File

@ -69,6 +69,7 @@ private:
CBotInstr* m_expr;
friend class CBotLeftExpr;
friend class CBotExprVar;
friend class CBotExprRetVar;
};
} // namespace CBot

View File

@ -18,7 +18,8 @@
*/
#include "CBot/CBotInstr/CBotInstrCall.h"
#include "CBot/CBotInstr/CBotExpression.h"
#include "CBot/CBotInstr/CBotExprRetVar.h"
#include "CBot/CBotInstr/CBotInstrUtils.h"
#include "CBot/CBotStack.h"
@ -47,62 +48,26 @@ CBotInstrCall::~CBotInstrCall()
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack)
{
CBotVar* ppVars[1000];
int i = 0;
CBotToken* pp = p;
p = p->GetNext();
pStack->SetStartError(p->GetStart());
CBotCStack* pile = pStack;
if ( IsOfType(p, ID_OPENPAR) )
if (p->GetType() == ID_OPENPAR)
{
int start, end;
CBotVar* ppVars[1000];
CBotInstrCall* inst = new CBotInstrCall();
inst->SetToken(pp);
// compile la list of parameters
if (!IsOfType(p, ID_CLOSEPAR)) while (true)
inst->m_parameters = CompileParams(p, pStack, ppVars);
if ( !pStack->IsOk() )
{
start = p->GetStart();
pile = pile->TokenStack(); // keeps the results on the stack
CBotInstr* param = CBotExpression::Compile(p, pile);
end = p->GetStart();
if (inst->m_parameters == nullptr ) inst->m_parameters = param;
else inst->m_parameters->AddNext(param); // constructs the list
if ( !pile->IsOk() )
{
delete inst;
return pStack->Return(nullptr, pile);
}
if ( param != nullptr )
{
if ( pile->GetTypResult().Eq(99) )
{
delete pStack->TokenStack();
pStack->SetError(CBotErrVoid, p->GetStart());
delete inst;
return nullptr;
}
ppVars[i] = pile->GetVar();
ppVars[i]->GetToken()->SetPos(start, end);
i++;
if (IsOfType(p, ID_COMMA)) continue; // skips the comma
if (IsOfType(p, ID_CLOSEPAR)) break;
}
pStack->SetError(CBotErrClosePar, p->GetStart());
delete pStack->TokenStack();
delete inst;
return nullptr;
}
ppVars[i] = nullptr;
// the routine is known?
// CBotClass* pClass = nullptr;
@ -124,6 +89,17 @@ CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack)
}
else pStack->SetVar(nullptr); // routine returns void
if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack)))
{
inst->m_exprRetVar->SetToken(&inst->m_token);
delete pStack->TokenStack();
}
if ( !pStack->IsOk() )
{
delete inst;
return nullptr;
}
return inst;
}
p = pp;
@ -138,6 +114,17 @@ bool CBotInstrCall::Execute(CBotStack* &pj)
CBotStack* pile = pj->AddStack(this);
if ( pile->StackOver() ) return pj->Return( pile );
CBotStack* pile3 = nullptr;
if (m_exprRetVar != nullptr) // func().member
{
pile3 = pile->AddStack2();
if (pile3->GetState() == 1) // function call is done?
{
if (!m_exprRetVar->Execute(pile3)) return false;
return pj->Return(pile3);
}
}
// CBotStack* pile1 = pile;
int i = 0;
@ -165,6 +152,14 @@ bool CBotInstrCall::Execute(CBotStack* &pj)
if ( !pile2->ExecuteCall(m_nFuncIdent, GetToken(), ppVars, m_typRes)) return false; // interrupt
if (m_exprRetVar != nullptr) // func().member
{
pile3->SetCopyVar( pile2->GetVar() ); // copy the result
pile2->SetVar(nullptr);
pile3->SetState(1); // set call is done
return false; // go back to the top ^^^
}
return pj->Return(pile2); // release the entire stack
}
@ -176,6 +171,16 @@ void CBotInstrCall::RestoreState(CBotStack* &pj, bool bMain)
CBotStack* pile = pj->RestoreStack(this);
if ( pile == nullptr ) return;
if (m_exprRetVar != nullptr) // func().member
{
CBotStack* pile3 = pile->AddStack2();
if (pile3->GetState() == 1) // function call is done?
{
m_exprRetVar->RestoreState(pile3, bMain);
return;
}
}
// CBotStack* pile1 = pile;
int i = 0;

View File

@ -69,6 +69,10 @@ private:
CBotTypResult m_typRes;
//! Id of a function.
long m_nFuncIdent;
//! Instruction to return a member of the returned object.
CBotInstr* m_exprRetVar;
friend class CBotDebug;
};

View File

@ -20,6 +20,7 @@
#include <sstream>
#include "CBot/CBotInstr/CBotInstrMethode.h"
#include "CBot/CBotInstr/CBotExprRetVar.h"
#include "CBot/CBotInstr/CBotInstrUtils.h"
#include "CBot/CBotStack.h"
@ -45,7 +46,7 @@ CBotInstrMethode::~CBotInstrMethode()
}
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* var)
CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* var, bool bMethodChain)
{
CBotInstrMethode* inst = new CBotInstrMethode();
inst->SetToken(p); // corresponding token
@ -63,6 +64,7 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar*
if (pStack->IsOk())
{
inst->m_thisIdent = var->GetUniqNum();
CBotClass* pClass = var->GetClass(); // pointer to the class
inst->m_className = pClass->GetName(); // name of the class
CBotTypResult r = pClass->CompileMethode(inst->m_methodName, var, ppVars,
@ -86,7 +88,17 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar*
}
pStack->SetVar(pResult);
}
return inst;
else pStack->SetVar(nullptr);
pp = p;
if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack, bMethodChain)))
{
inst->m_exprRetVar->SetToken(pp);
delete pStack->TokenStack();
}
if ( pStack->IsOk() )
return inst;
}
delete inst;
return nullptr;
@ -106,6 +118,18 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre
return pj->Return(pile1);
}
CBotStack* pile3 = nullptr;
if (m_exprRetVar != nullptr) // .func().member
{
pile3 = pile1->AddStack2();
if (pile3->GetState() == 1)
{
if (!m_exprRetVar->Execute(pile3)) return false;
pVar = nullptr;
return pj->Return(pile3);
}
}
if (pile1->IfStep()) return false;
CBotStack* pile2 = pile1->AddStack(); // for the next parameters
@ -144,8 +168,14 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre
}
ppVars[i] = nullptr;
CBotClass* pClass = CBotClass::Find(m_className);
CBotVar* pThis = pile1->GetVar();
CBotClass* pClass;
if (m_thisIdent == -3) // super.method()
pClass = CBotClass::Find(m_className);
else
pClass = pThis->GetClass();
CBotVar* pResult = nullptr;
if (m_typRes.GetType() > 0) pResult = CBotVar::Create("", m_typRes);
if (m_typRes.Eq(CBotTypClass))
@ -159,6 +189,15 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre
pResult, pile2, GetToken())) return false;
if (pRes != pResult) delete pRes;
if (m_exprRetVar != nullptr) // .func().member
{
pile3->SetCopyVar( pile2->GetVar() );
pile2->SetVar(nullptr);
pile3->SetState(1); // set call is done
pVar = nullptr;
return false; // go back to the top ^^^
}
pVar = nullptr; // does not return value for this
return pj->Return(pile2); // release the entire stack
}
@ -172,6 +211,16 @@ void CBotInstrMethode::RestoreStateVar(CBotStack* &pile, bool bMain)
CBotStack* pile1 = pile->RestoreStack(this); // place for the copy of This
if (pile1 == nullptr) return;
if (m_exprRetVar != nullptr) // .func().member
{
CBotStack* pile3 = pile1->AddStack2();
if (pile3->GetState() == 1) // function call is done?
{
m_exprRetVar->RestoreState(pile3, bMain);
return;
}
}
CBotStack* pile2 = pile1->RestoreStack(); // and for the parameters coming
if (pile2 == nullptr) return;
@ -204,7 +253,13 @@ void CBotInstrMethode::RestoreStateVar(CBotStack* &pile, bool bMain)
}
ppVars[i] = nullptr;
CBotClass* pClass = CBotClass::Find(m_className);
CBotClass* pClass;
if (m_thisIdent == -3) // super.method()
pClass = CBotClass::Find(m_className);
else
pClass = pThis->GetClass();
// CBotVar* pResult = nullptr;
// CBotVar* pRes = pResult;
@ -253,8 +308,14 @@ bool CBotInstrMethode::Execute(CBotStack* &pj)
}
ppVars[i] = nullptr;
CBotClass* pClass = CBotClass::Find(m_className);
CBotVar* pThis = pile1->GetVar();
CBotClass* pClass;
if (m_thisIdent == -3) // super.method()
pClass = CBotClass::Find(m_className);
else
pClass = pThis->GetClass();
CBotVar* pResult = nullptr;
if (m_typRes.GetType()>0) pResult = CBotVar::Create("", m_typRes);
if (m_typRes.Eq(CBotTypClass))

View File

@ -38,9 +38,10 @@ public:
* \param p
* \param pStack
* \param pVar
* \param bMethodChain If true, allows chaining methods only
* \return
*/
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* pVar);
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* pVar, bool bMethodChain = false);
/*!
* \brief Execute
@ -83,6 +84,12 @@ private:
long m_MethodeIdent;
//! Name of the class.
std::string m_className;
//! Variable ID
long m_thisIdent;
//! Instruction to return a member of the returned object.
CBotInstr* m_exprRetVar;
};
} // namespace CBot

View File

@ -149,12 +149,20 @@ bool TypesCompatibles(const CBotTypResult& type1, const CBotTypResult& type2)
if (max >= CBotTypBoolean)
{
if (t1 == CBotTypPointer && t2 == CBotTypNullPointer) return true;
if (t2 != t1) return false;
if (max == CBotTypPointer)
{
CBotClass* c1 = type1.GetClass();
CBotClass* c2 = type2.GetClass();
return c2->IsChildOf(c1);
}
if (max == CBotTypArrayPointer)
return TypesCompatibles(type1.GetTypElem(), type2.GetTypElem());
if (max == CBotTypClass || max == CBotTypPointer)
if (max == CBotTypClass)
return type1.GetClass() == type2.GetClass() ;
return true ;

View File

@ -64,8 +64,7 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack)
inst->m_nIdent = var->GetUniqNum();
if (inst->m_nIdent > 0 && inst->m_nIdent < 9000)
{
if ( var->IsPrivate(CBotVar::ProtectionLevel::ReadOnly) &&
!pStk->GetProgram()->m_bCompileClass)
if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, CBotVar::ProtectionLevel::ReadOnly))
{
pStk->SetError(CBotErrPrivate, p);
goto err;
@ -125,11 +124,12 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack)
if (p->GetType() == TokenTypVar) // must be a name
{
CBotVar* preVar = var;
var = var->GetItem(p->GetString()); // get item correspondent
if (var != nullptr)
{
if ( var->IsPrivate(CBotVar::ProtectionLevel::ReadOnly) &&
!pStk->GetProgram()->m_bCompileClass)
if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var,
CBotVar::ProtectionLevel::ReadOnly))
{
pStk->SetError(CBotErrPrivate, pp);
goto err;
@ -182,15 +182,18 @@ bool CBotLeftExpr::Execute(CBotStack* &pj, CBotStack* array)
if (t2.Eq(CBotTypPointer))
{
CBotClass* c1 = t1.GetClass();
CBotClass* c2 = t2.GetClass();
CBotClass* c2 = var2->GetClass();
if ( !c2->IsChildOf(c1))
{
CBotToken* pt = &m_token;
pile->SetError(CBotErrBadType1, pt);
return pj->Return(pile); // operation performed
}
var1->SetVal(var2); // set pointer
var1->SetType(t1); // keep pointer type
}
var1->SetVal(var2); // do assignment
else
var1->SetVal(var2); // do assignment
}
pile->SetCopyVar(var1); // replace the stack with the copy of the variable
// (for name)

View File

@ -115,7 +115,7 @@ CBotInstr* CBotListArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResu
goto error;
}
CBotTypResult valType = pStk->GetTypResult();
CBotTypResult valType = pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
if (!TypeCompatible(valType, type, ID_ASS) )
{
@ -133,7 +133,7 @@ CBotInstr* CBotListArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResu
goto error;
}
CBotTypResult valType = pStk->GetTypResult();
CBotTypResult valType = pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
if (!TypeCompatible(valType, type, ID_ASS) )
{
@ -185,9 +185,12 @@ bool CBotListArray::Execute(CBotStack* &pj, CBotVar* pVar)
pj->SetError(CBotErrOutArray, p->GetToken());
return false;
}
CBotTypResult type = pVar2->GetTypResult();
if (!p->Execute(pile1, pVar2)) return false; // evaluate expression
if (type.Eq(CBotTypPointer)) pVar2->SetType(type); // keep pointer type
pile1->IncState();
}

View File

@ -24,6 +24,7 @@
#include "CBot/CBotCStack.h"
#include "CBot/CBotClass.h"
#include "CBot/CBotInstr/CBotExprRetVar.h"
#include "CBot/CBotInstr/CBotInstrUtils.h"
#include "CBot/CBotVar/CBotVar.h"
@ -105,7 +106,17 @@ CBotInstr* CBotNew::Compile(CBotToken* &p, CBotCStack* pStack)
// makes pointer to the object on the stack
pStk->SetVar(pVar);
return pStack->Return(inst, pStk);
pp = p;
// chained method ?
if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStk, true)))
{
inst->m_exprRetVar->SetToken(pp);
delete pStk->TokenStack();
}
if (pStack->IsOk())
return pStack->Return(inst, pStk);
}
error:
delete inst;
@ -117,6 +128,16 @@ bool CBotNew::Execute(CBotStack* &pj)
{
CBotStack* pile = pj->AddStack(this); //main stack
if (m_exprRetVar != nullptr) // new Class().method()
{
if (pile->GetState() == 2)
{
CBotStack* pile3 = pile->AddStack();
if (!m_exprRetVar->Execute(pile3)) return false;
return pj->Return(pile3);
}
}
if (pile->IfStep()) return false;
CBotStack* pile1 = pj->AddStack2(); //secondary stack
@ -186,6 +207,16 @@ bool CBotNew::Execute(CBotStack* &pj)
pThis->ConstructorSet(); // indicates that the constructor has been called
}
if (m_exprRetVar != nullptr) // new Class().method()
{
pile->AddStack()->Delete(); // release pile2 stack
CBotStack* pile3 = pile->AddStack(); // add new stack
pile3->SetCopyVar(pThis); // copy the pointer (from pile1)
pile1->Delete(); // release secondary stack(pile1)
pile->SetState(2);
return false; // go back to the top ^^^
}
return pj->Return(pile1); // passes below
}
@ -197,6 +228,16 @@ void CBotNew::RestoreState(CBotStack* &pj, bool bMain)
CBotStack* pile = pj->RestoreStack(this); //primary stack
if (pile == nullptr) return;
if (m_exprRetVar != nullptr) // new Class().method()
{
if (pile->GetState() == 2)
{
CBotStack* pile3 = pile->RestoreStack();
m_exprRetVar->RestoreState(pile3, bMain);
return;
}
}
CBotStack* pile1 = pj->AddStack2(); //secondary stack
CBotToken* pt = &m_vartoken;

View File

@ -66,6 +66,9 @@ private:
long m_nMethodeIdent;
CBotToken m_vartoken;
//! Instruction to chain method calls after constructor
CBotInstr* m_exprRetVar;
};
} // namespace CBot

View File

@ -47,13 +47,13 @@ CBotProgram::CBotProgram(CBotVar* thisVar)
CBotProgram::~CBotProgram()
{
// delete m_classes;
m_classes->Purge();
if (m_classes != nullptr) m_classes->Purge();
m_classes = nullptr;
CBotClass::FreeLock(this);
delete m_functions;
m_stack->Delete();
if (m_stack != nullptr) m_stack->Delete();
}
bool CBotProgram::Compile(const std::string& program, std::vector<std::string>& functions, void* pUser)
@ -62,7 +62,7 @@ bool CBotProgram::Compile(const std::string& program, std::vector<std::string>&
Stop();
// delete m_classes;
m_classes->Purge(); // purge the old definitions of classes
if (m_classes != nullptr) m_classes->Purge(); // purge the old definitions of classes
// but without destroying the object
m_classes = nullptr;
delete m_functions; m_functions = nullptr;
@ -99,6 +99,10 @@ bool CBotProgram::Compile(const std::string& program, std::vector<std::string>&
else m_functions->AddNext(next);
}
}
// Define fields and pre-compile methods for each class in this program
if (pStack->IsOk()) CBotClass::DefineClasses(m_classes, pStack.get());
if ( !pStack->IsOk() )
{
m_error = pStack->GetError(m_errorStart, m_errorEnd);
@ -120,14 +124,13 @@ bool CBotProgram::Compile(const std::string& program, std::vector<std::string>&
if ( p->GetType() == ID_CLASS ||
( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS ))
{
m_bCompileClass = true;
CBotClass::Compile(p, pStack.get()); // completes the definition of the class
}
else
{
m_bCompileClass = false;
CBotFunction::Compile(p, pStack.get(), next);
if (next->IsExtern()) functions.push_back(next->GetName()/* + next->GetParams()*/);
if (next->IsPublic()) CBotFunction::AddPublic(next);
next->m_pProg = this; // keeps pointers to the module
next = next->Next();
}
@ -224,8 +227,11 @@ bool CBotProgram::Run(void* pUser, int timer)
void CBotProgram::Stop()
{
m_stack->Delete();
m_stack = nullptr;
if (m_stack != nullptr)
{
m_stack->Delete();
m_stack = nullptr;
}
m_entryPoint = nullptr;
CBotClass::FreeLock(this);
}
@ -362,11 +368,15 @@ bool CBotProgram::RestoreState(FILE* pf)
if (!ReadString( pf, s )) return false;
Start(s); // point de reprise
m_stack->Delete();
m_stack = nullptr;
if (m_stack != nullptr)
{
m_stack->Delete();
m_stack = nullptr;
}
// retrieves the stack from the memory
// uses a nullptr pointer (m_stack) but it's ok like that
// TODO: no it's not okay like that! but it looks like it doesn't get optimized out at least ~krzys_h
if (!m_stack->RestoreState(pf, m_stack)) return false;
m_stack->SetProgram(this); // bases for routines

View File

@ -332,13 +332,6 @@ public:
*/
CBotFunction* GetFunctions();
/**
* \brief true while compiling class
*
* TODO: refactor this
*/
bool m_bCompileClass;
/**
* \brief Returns static list of all registered external calls
*/

View File

@ -19,6 +19,8 @@
#include "CBot/CBotStack.h"
#include "CBot/CBotClass.h"
#include "CBot/CBotInstr/CBotFunction.h"
#include "CBot/CBotVar/CBotVarPointer.h"
@ -80,10 +82,10 @@ CBotStack* CBotStack::AllocateStack()
////////////////////////////////////////////////////////////////////////////////
void CBotStack::Delete()
{
if ( this == nullptr ) return;
assert ( this != nullptr );
m_next->Delete();
m_next2->Delete();
if (m_next != nullptr) m_next->Delete();
if (m_next2 != nullptr) m_next2->Delete();
if (m_prev != nullptr)
{
@ -190,8 +192,8 @@ bool CBotStack::Return(CBotStack* pfils)
m_var = pfils->m_var; // result transmitted
pfils->m_var = nullptr; // not to destroy the variable
m_next->Delete();m_next = nullptr; // releases the stack above
m_next2->Delete();m_next2 = nullptr; // also the second stack (catch)
if (m_next != nullptr) m_next->Delete();m_next = nullptr; // releases the stack above
if (m_next2 != nullptr) m_next2->Delete();m_next2 = nullptr; // also the second stack (catch)
return IsOk(); // interrupted if error
}
@ -279,7 +281,7 @@ bool CBotStack::IfContinue(int state, const std::string& name)
m_state = state; // where again?
m_error = CBotNoErr;
m_labelBreak.clear();
m_next->Delete(); // purge above stack
if (m_next != nullptr) m_next->Delete(); // purge above stack
return true;
}
@ -476,7 +478,7 @@ bool CBotStack::Execute()
if (!instr->Run(nullptr, pile)) return false; // resume interrupted execution
pile->m_next->Delete();
if (pile->m_next != nullptr) pile->m_next->Delete();
pile->m_callFinished = true;
return true;
@ -760,7 +762,17 @@ bool CBotVar::Save0State(FILE* pf)
if (!WriteWord(pf, 100+static_cast<int>(m_mPrivate)))return false; // private variable?
if (!WriteWord(pf, m_bStatic))return false; // static variable?
if (!WriteWord(pf, m_type.GetType()))return false; // saves the type (always non-zero)
if (!WriteWord(pf, static_cast<unsigned short>(m_binit))) return false; // variable defined?
if (m_type.Eq(CBotTypPointer) && GetPointer() != nullptr)
{
if (GetPointer()->m_bConstructor) // constructor was called?
{
if (!WriteWord(pf, (2000 + static_cast<unsigned short>(m_binit)) )) return false;
return WriteString(pf, m_token->GetString()); // and variable name
}
}
if (!WriteWord(pf, static_cast<unsigned short>(m_binit))) return false; // variable defined?
return WriteString(pf, m_token->GetString()); // and variable name
}
@ -800,6 +812,13 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar)
if ( w == CBotTypClass ) w = CBotTypIntrinsic; // necessarily intrinsic
if (!ReadWord(pf, wi)) return false; // init ?
bool bConstructor = false;
if (w == CBotTypPointer && wi >= 2000)
{
wi -= 2000;
bConstructor = true;
}
CBotVar::InitType initType = static_cast<CBotVar::InitType>(wi);
if (!ReadString(pf, name)) return false; // variable name
@ -849,10 +868,11 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar)
if (isClass && p == nullptr) // set id for each item in this instance
{
CBotVar* pVars = pNew->GetItemList();
long itemId = 1;
while (pVars != nullptr)
CBotVar* pv = pNew->GetClass()->GetVar();
while (pVars != nullptr && pv != nullptr)
{
pVars->m_ident = itemId++;
pVars->m_ident = pv->m_ident;
pv = pv->GetNext();
pVars = pVars->GetNext();
}
}
@ -868,9 +888,10 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar)
case CBotTypPointer:
case CBotTypNullPointer:
if (!ReadString(pf, s)) return false;
if (!ReadString(pf, s)) return false; // name of the class
{
pNew = CBotVar::Create(token, CBotTypResult(w, s));// creates a variable
CBotTypResult ptrType(w, s);
pNew = CBotVar::Create(token, ptrType);// creates a variable
// CBotVarClass* p = nullptr;
long id;
ReadLong(pf, id);
@ -881,6 +902,9 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar)
if ( !CBotVar::RestoreState( pf, pInstance ) ) return false;
(static_cast<CBotVarPointer*>(pNew))->SetPointer( pInstance ); // and point over
if (bConstructor) pNew->ConstructorSet(); // constructor was called
if (ptrType.Eq(CBotTypPointer)) pNew->SetType(ptrType); // keep pointer type
// if ( p != nullptr ) (static_cast<CBotVarPointer*>(pNew))->SetPointer( p ); // rather this one
}

View File

@ -106,7 +106,7 @@ public:
/**
* \brief Returns ::CBotType or ::CBotError stored in this object
* \param mode Mode, see ::GetTypeMode enum
* \param mode Mode, see GetTypeMode enum
*/
int GetType(GetTypeMode mode = GetTypeMode::NORMAL) const;

View File

@ -69,11 +69,10 @@ CBotVarClass::CBotVarClass(const CBotToken& name, const CBotTypResult& type)
m_instances.insert(this);
CBotClass* pClass = type.GetClass();
CBotClass* pClass2 = pClass->GetParent();
if ( pClass2 != nullptr )
if ( pClass != nullptr && pClass->GetParent() != nullptr )
{
// also creates an instance of the parent class
m_pParent = new CBotVarClass(name, CBotTypResult(type.GetType(),pClass2) ); //, nIdent);
m_pParent = new CBotVarClass(name, CBotTypResult(type.GetType(), pClass->GetParent()) ); //, nIdent);
}
SetClass( pClass );

View File

@ -174,9 +174,9 @@ CBotClass* CBotVarPointer::GetClass()
////////////////////////////////////////////////////////////////////////////////
bool CBotVarPointer::Save1State(FILE* pf)
{
if ( m_pClass )
if ( m_type.GetClass() != nullptr )
{
if (!WriteString(pf, m_pClass->GetName())) return false; // name of the class
if (!WriteString(pf, m_type.GetClass()->GetName())) return false; // name of the class
}
else
{

View File

@ -54,6 +54,8 @@ set(SOURCES
CBotInstr/CBotExprLitNum.h
CBotInstr/CBotExprLitString.cpp
CBotInstr/CBotExprLitString.h
CBotInstr/CBotExprRetVar.cpp
CBotInstr/CBotExprRetVar.h
CBotInstr/CBotExprUnaire.cpp
CBotInstr/CBotExprUnaire.h
CBotInstr/CBotExprVar.cpp

View File

@ -51,7 +51,7 @@ bool FileClassOpenFile(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exc
{
// recover mode
mode = pVar->GetValString();
if ( mode != "r" && mode != "w" ) { Exception = CBotErrBadParam; return false; }
if ( mode != "r" && mode != "w" && mode != "a" ) { Exception = CBotErrBadParam; return false; }
// no third parameter
if ( pVar->GetNext() != nullptr ) { Exception = CBotErrOverParam; return false; }
@ -70,7 +70,13 @@ bool FileClassOpenFile(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exc
{
// opens the requested file
assert(g_fileHandler != nullptr);
std::unique_ptr<CBotFile> file = g_fileHandler->OpenFile(filename, mode == "r" ? CBotFileAccessHandler::OpenMode::Read : CBotFileAccessHandler::OpenMode::Write);
CBotFileAccessHandler::OpenMode openMode;
if ( mode == "r" ) openMode = CBotFileAccessHandler::OpenMode::Read;
else if ( mode == "w" ) openMode = CBotFileAccessHandler::OpenMode::Write;
else if ( mode == "a" ) openMode = CBotFileAccessHandler::OpenMode::Append;
std::unique_ptr<CBotFile> file = g_fileHandler->OpenFile(filename, openMode);
if (!file->Opened()) { Exception = CBotErrFileOpen; return false; }

View File

@ -44,7 +44,7 @@ class CBotFileAccessHandler
public:
virtual ~CBotFileAccessHandler() {}
enum class OpenMode : char { Read = 'r', Write = 'w' };
enum class OpenMode : char { Read = 'r', Write = 'w', Append = 'a' };
virtual std::unique_ptr<CBotFile> OpenFile(const std::string& filename, OpenMode mode) = 0;
virtual bool DeleteFile(const std::string& filename) = 0;
};

View File

@ -89,8 +89,6 @@ endif()
# Source files
set(BASE_SOURCES
${OPENAL_SRC}
app/${SYSTEM_CPP_MODULE}
app/${SYSTEM_H_MODULE}
app/app.cpp
app/app.h
app/controller.cpp
@ -103,10 +101,6 @@ set(BASE_SOURCES
app/pausemanager.h
app/signal_handlers.cpp
app/signal_handlers.h
app/system.cpp
app/system.h
app/system_other.cpp
app/system_other.h
common/config_file.cpp
common/config_file.h
common/error.h
@ -123,6 +117,8 @@ set(BASE_SOURCES
common/logger.cpp
common/logger.h
common/make_unique.h
common/profiler.cpp
common/profiler.h
common/regex_utils.cpp
common/regex_utils.h
common/resources/inputstream.cpp
@ -143,6 +139,12 @@ set(BASE_SOURCES
common/resources/sndfile_wrapper.h
common/restext.cpp
common/restext.h
common/system/system.cpp
common/system/system.h
common/system/system_other.cpp
common/system/system_other.h
common/system/${SYSTEM_CPP_MODULE}
common/system/${SYSTEM_H_MODULE}
common/settings.cpp
common/settings.h
common/singleton.h
@ -151,6 +153,8 @@ set(BASE_SOURCES
common/thread/resource_owning_thread.h
common/thread/sdl_cond_wrapper.h
common/thread/sdl_mutex_wrapper.h
common/thread/thread.h
common/thread/worker_thread.h
graphics/core/color.cpp
graphics/core/color.h
graphics/core/device.h

View File

@ -22,18 +22,22 @@
#include "app/controller.h"
#include "app/input.h"
#include "app/pathman.h"
#include "app/system.h"
#include "common/config_file.h"
#include "common/image.h"
#include "common/key.h"
#include "common/logger.h"
#include "common/make_unique.h"
#include "common/profiler.h"
#include "common/stringutils.h"
#include "common/version.h"
#include "common/resources/resourcemanager.h"
#include "common/system/system.h"
#include "common/thread/thread.h"
#include "graphics/core/nulldevice.h"
#include "graphics/opengl/glutil.h"
@ -112,9 +116,7 @@ CApplication::CApplication(CSystemUtils* systemUtils)
m_private(MakeUnique<ApplicationPrivate>()),
m_configFile(MakeUnique<CConfigFile>()),
m_input(MakeUnique<CInput>()),
m_pathManager(MakeUnique<CPathManager>(systemUtils)),
m_performanceCounters(),
m_performanceCountersData()
m_pathManager(MakeUnique<CPathManager>(systemUtils))
{
m_exitCode = 0;
m_active = false;
@ -144,11 +146,6 @@ CApplication::CApplication(CSystemUtils* systemUtils)
m_manualFrameLast = m_systemUtils->CreateTimeStamp();
m_manualFrameTime = m_systemUtils->CreateTimeStamp();
for (int i = 0; i < PCNT_MAX; ++i)
{
m_performanceCounters[i][0] = m_systemUtils->CreateTimeStamp();
m_performanceCounters[i][1] = m_systemUtils->CreateTimeStamp();
}
m_joystickEnabled = false;
@ -173,12 +170,6 @@ CApplication::~CApplication()
m_systemUtils->DestroyTimeStamp(m_manualFrameLast);
m_systemUtils->DestroyTimeStamp(m_manualFrameTime);
for (int i = 0; i < PCNT_MAX; ++i)
{
m_systemUtils->DestroyTimeStamp(m_performanceCounters[i][0]);
m_systemUtils->DestroyTimeStamp(m_performanceCounters[i][1]);
}
m_joystickEnabled = false;
m_controller.reset();
@ -514,8 +505,6 @@ bool CApplication::Create()
#endif
m_sound->Create();
m_sound->CacheAll();
m_sound->CacheCommonMusic();
GetLogger()->Info("CApplication created successfully\n");
@ -685,6 +674,20 @@ bool CApplication::Create()
// Create the robot application.
m_controller = MakeUnique<CController>();
CThread musicLoadThread([this]() {
GetLogger()->Debug("Cache sounds...\n");
SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp();
m_systemUtils->GetCurrentTimeStamp(musicLoadStart);
m_sound->CacheAll();
SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp();
m_systemUtils->GetCurrentTimeStamp(musicLoadEnd);
float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC);
GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime);
}, "Sound loading thread");
musicLoadThread.Start();
if (m_runSceneCategory == LevelCategory::Max)
m_controller->StartApp();
else
@ -994,12 +997,10 @@ int CApplication::Run()
while (true)
{
ResetPerformanceCounters();
if (m_active)
{
StartPerformanceCounter(PCNT_ALL);
StartPerformanceCounter(PCNT_EVENT_PROCESSING);
CProfiler::StartPerformanceCounter(PCNT_ALL);
CProfiler::StartPerformanceCounter(PCNT_EVENT_PROCESSING);
}
// To be sure no old event remains
@ -1088,9 +1089,9 @@ int CApplication::Run()
m_controller->ProcessEvent(event);
}
StopPerformanceCounter(PCNT_EVENT_PROCESSING);
CProfiler::StopPerformanceCounter(PCNT_EVENT_PROCESSING);
StartPerformanceCounter(PCNT_UPDATE_ALL);
CProfiler::StartPerformanceCounter(PCNT_UPDATE_ALL);
// Prepare and process step simulation event
Event event = CreateUpdateEvent();
@ -1100,16 +1101,16 @@ int CApplication::Run()
m_sound->FrameMove(m_relTime);
StartPerformanceCounter(PCNT_UPDATE_GAME);
CProfiler::StartPerformanceCounter(PCNT_UPDATE_GAME);
m_controller->ProcessEvent(event);
StopPerformanceCounter(PCNT_UPDATE_GAME);
CProfiler::StopPerformanceCounter(PCNT_UPDATE_GAME);
StartPerformanceCounter(PCNT_UPDATE_ENGINE);
CProfiler::StartPerformanceCounter(PCNT_UPDATE_ENGINE);
m_engine->FrameUpdate();
StopPerformanceCounter(PCNT_UPDATE_ENGINE);
CProfiler::StopPerformanceCounter(PCNT_UPDATE_ENGINE);
}
StopPerformanceCounter(PCNT_UPDATE_ALL);
CProfiler::StopPerformanceCounter(PCNT_UPDATE_ALL);
/* Update mouse position explicitly right before rendering
* because mouse events are usually way behind */
@ -1117,9 +1118,7 @@ int CApplication::Run()
Render();
StopPerformanceCounter(PCNT_ALL);
UpdatePerformanceCountersData();
CProfiler::StopPerformanceCounter(PCNT_ALL);
}
}
@ -1404,14 +1403,14 @@ Event CApplication::CreateVirtualEvent(const Event& sourceEvent)
/** Renders the frame and swaps buffers as necessary */
void CApplication::Render()
{
StartPerformanceCounter(PCNT_RENDER_ALL);
CProfiler::StartPerformanceCounter(PCNT_RENDER_ALL);
m_engine->Render();
StopPerformanceCounter(PCNT_RENDER_ALL);
CProfiler::StopPerformanceCounter(PCNT_RENDER_ALL);
StartPerformanceCounter(PCNT_SWAP_BUFFERS);
CProfiler::StartPerformanceCounter(PCNT_SWAP_BUFFERS);
if (m_deviceConfig.doubleBuf)
SDL_GL_SwapWindow(m_private->window);
StopPerformanceCounter(PCNT_SWAP_BUFFERS);
CProfiler::StopPerformanceCounter(PCNT_SWAP_BUFFERS);
}
void CApplication::RenderIfNeeded(int updateRate)
@ -1848,45 +1847,6 @@ void CApplication::SetLanguage(Language language)
GetLogger()->Debug("SetLanguage: Test gettext translation: '%s'\n", gettext("Colobot rules!"));
}
void CApplication::StartPerformanceCounter(PerformanceCounter counter)
{
m_systemUtils->GetCurrentTimeStamp(m_performanceCounters[counter][0]);
}
void CApplication::StopPerformanceCounter(PerformanceCounter counter)
{
m_systemUtils->GetCurrentTimeStamp(m_performanceCounters[counter][1]);
}
float CApplication::GetPerformanceCounterData(PerformanceCounter counter) const
{
return m_performanceCountersData[counter];
}
void CApplication::ResetPerformanceCounters()
{
for (int i = 0; i < PCNT_MAX; ++i)
{
StartPerformanceCounter(static_cast<PerformanceCounter>(i));
StopPerformanceCounter(static_cast<PerformanceCounter>(i));
}
}
void CApplication::UpdatePerformanceCountersData()
{
long long sum = m_systemUtils->TimeStampExactDiff(m_performanceCounters[PCNT_ALL][0],
m_performanceCounters[PCNT_ALL][1]);
for (int i = 0; i < PCNT_MAX; ++i)
{
long long diff = m_systemUtils->TimeStampExactDiff(m_performanceCounters[i][0],
m_performanceCounters[i][1]);
m_performanceCountersData[static_cast<PerformanceCounter>(i)] =
static_cast<float>(diff) / static_cast<float>(sum);
}
}
bool CApplication::GetSceneTestMode()
{
return m_sceneTest;

View File

@ -94,34 +94,6 @@ enum MouseMode
MOUSE_NONE, //! < no cursor visible
};
/**
* \enum PerformanceCounter
* \brief Type of counter testing performance
*/
enum PerformanceCounter
{
PCNT_EVENT_PROCESSING, //! < event processing (except update events)
PCNT_UPDATE_ALL, //! < the whole frame update process
PCNT_UPDATE_ENGINE, //! < frame update in CEngine
PCNT_UPDATE_PARTICLE, //! < frame update in CParticle
PCNT_UPDATE_GAME, //! < frame update in CRobotMain
PCNT_RENDER_ALL, //! < the whole rendering process
PCNT_RENDER_PARTICLE, //! < rendering the particles in 3D
PCNT_RENDER_WATER, //! < rendering the water
PCNT_RENDER_TERRAIN, //! < rendering the terrain
PCNT_RENDER_OBJECTS, //! < rendering the 3D objects
PCNT_RENDER_INTERFACE, //! < rendering 2D interface
PCNT_RENDER_SHADOW_MAP, //! < rendering shadow map
PCNT_SWAP_BUFFERS, //! < swapping buffers and vsync
PCNT_ALL, //! < all counters together
PCNT_MAX
};
enum DebugMode
{
DEBUG_SYS_EVENTS = 1 << 0,
@ -292,13 +264,6 @@ public:
void SetLanguage(Language language);
//@}
//! Management of performance counters
//@{
void StartPerformanceCounter(PerformanceCounter counter);
void StopPerformanceCounter(PerformanceCounter counter);
float GetPerformanceCounterData(PerformanceCounter counter) const;
//@}
bool GetSceneTestMode();
//! Renders the image in window
@ -333,11 +298,6 @@ protected:
//! Internal procedure to reset time counters
void InternalResumeSimulation();
//! Resets all performance counters to zero
void ResetPerformanceCounters();
//! Updates performance counters from gathered timer data
void UpdatePerformanceCountersData();
protected:
//! System utils instance
CSystemUtils* m_systemUtils;
@ -382,9 +342,6 @@ protected:
SystemTimeStamp* m_lastTimeStamp;
SystemTimeStamp* m_curTimeStamp;
SystemTimeStamp* m_performanceCounters[PCNT_MAX][2];
float m_performanceCountersData[PCNT_MAX];
long long m_realAbsTimeBase;
long long m_realAbsTime;
long long m_realRelTime;

View File

@ -26,18 +26,20 @@
#include "app/app.h"
#include "app/signal_handlers.h"
#include "app/system.h"
#if PLATFORM_WINDOWS
#include "app/system_windows.h"
#endif
#include "common/logger.h"
#include "common/make_unique.h"
#include "common/profiler.h"
#include "common/restext.h"
#include "common/version.h"
#include "common/resources/resourcemanager.h"
#include "common/system/system.h"
#if PLATFORM_WINDOWS
#include "common/system/system_windows.h"
#endif
#if PLATFORM_WINDOWS
#include <windows.h>
#endif
@ -46,13 +48,22 @@
#include <vector>
#include <boost/filesystem.hpp>
/* Doxygen main page */
/**
\mainpage
Doxygen documentation of Colobot project
Doxygen documentation of Colobot: Gold Edition project.
<b>Colobot</b> <i>(COLOnize with BOTs)</i> is a game combining elements of real time strategy (RTS)
and educational game, aiming to teach programming through entertainment. You are playing as an astronaut
on a journey with robot helpers to find a planet for colonization. It features a C++ and Java-like,
object-oriented language, CBOT, which can be used to program the robots available in the game.
The original version of the game was developed by [Epsitec](http://www.epsitec.ch/) and released in 2001.
Later, in 2005 another version named Ceebot was released. In March 2012, through attempts
by Polish Colobot fans, Epsitec agreeed to release the source code of the game on GPLv3 license.
The license was given specfifically to our community, <b>TerranovaTeam</b>,
part of <b>International Colobot Community (ICC)</b> (previously known as <i>Polish Portal of Colobot (PPC)</i>;
Polish: <i>Polski Portal Colobota</i>) with our website at http://colobot.info/.
\section Intro Introduction
@ -69,17 +80,21 @@ The source code was split from the original all-in-one directory to subdirectori
each containing one major part of the project.
The current layout is the following:
- src/CBot - separate library with CBot language
- src/app - class CApplication and everything concerned with SDL plus other system-dependent
code such as displaying a message box, finding files, etc.
- src/app - class CApplication and everything concerned with SDL
- src/common - shared structs, enums, defines, etc.; should not have any external dependencies
- src/common/resources - filesystem management using PHYSFS library
- src/common/system - system-dependent code such as displaying a message box, finding files, etc.
- src/common/thread - wrapper classes for SDL threads
- src/graphics/core - abstract interface of graphics device (abstract CDevice class)
(split from old src/graphics/common)
- src/graphics/engine - main graphics engine based on abstract graphics device; is composed
of CEngine class and associated classes implementing the 3D engine (split from old src/graphics/common)
of CEngine class and associated classes implementing the 3D engine
- src/graphics/model - code related to loading/saving model files
- src/graphics/opengl - concrete implementation of CDevice class in OpenGL: CGLDevice
- src/graphics/d3d - in (far) future - perhaps a newer implementation in DirectX (9? 10?)
- src/math - mathematical structures and functions
- src/object - non-graphical game engine, that is robots, buildings, etc.
- src/object - non-grphical game object logic, that is robots, buildings, etc.
- src/level - main part of non-graphical game engine, that is loading levels etc.
- src/level/parser - parser for loading/saving level files from format known as <i>scene files</i>
- src/ui - 2D user interface (menu, buttons, check boxes, etc.)
- src/sound - sound and music engine written using fmod library
- src/physics - physics engine
@ -98,6 +113,8 @@ int main(int argc, char *argv[])
auto systemUtils = CSystemUtils::Create(); // platform-specific utils
systemUtils->Init();
CProfiler::SetSystemUtils(systemUtils.get());
// Add file output to the logger
std::string logFileName;
#if DEV_BUILD

View File

@ -23,15 +23,17 @@
#include "common/config.h"
#include "app/app.h"
#include "app/system.h"
#ifdef PLATFORM_WINDOWS
#include "app/system_windows.h"
#endif
#include "common/logger.h"
#include "common/resources/resourcemanager.h"
#include "common/system/system.h"
#ifdef PLATFORM_WINDOWS
#include "common/system/system_windows.h"
#endif
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>

View File

@ -19,13 +19,13 @@
#include "app/signal_handlers.h"
#include "app/system.h"
#include "common/stringutils.h"
#include "common/version.h"
#include "common/resources/resourcemanager.h"
#include "common/system/system.h"
#include "level/robotmain.h"
#include <csignal>

View File

@ -20,14 +20,14 @@
#include "common/config_file.h"
#include "app/system.h"
#include "common/logger.h"
#include "common/make_unique.h"
#include "common/resources/inputstream.h"
#include "common/resources/outputstream.h"
#include "common/system/system.h"
#include <memory>
#include <utility>
#include <cstring>

90
src/common/profiler.cpp Normal file
View File

@ -0,0 +1,90 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "common/profiler.h"
#include "common/system/system.h"
#include <cassert>
CSystemUtils* CProfiler::m_systemUtils = nullptr;
long long CProfiler::m_performanceCounters[PCNT_MAX] = {0};
long long CProfiler::m_prevPerformanceCounters[PCNT_MAX] = {0};
std::stack<SystemTimeStamp*> CProfiler::m_runningPerformanceCounters;
std::stack<PerformanceCounter> CProfiler::m_runningPerformanceCountersType;
void CProfiler::SetSystemUtils(CSystemUtils* systemUtils)
{
m_systemUtils = systemUtils;
}
void CProfiler::StartPerformanceCounter(PerformanceCounter counter)
{
if (counter == PCNT_ALL)
ResetPerformanceCounters();
SystemTimeStamp* timeStamp = m_systemUtils->CreateTimeStamp();
m_systemUtils->GetCurrentTimeStamp(timeStamp);
m_runningPerformanceCounters.push(timeStamp);
m_runningPerformanceCountersType.push(counter);
}
void CProfiler::StopPerformanceCounter(PerformanceCounter counter)
{
assert(m_runningPerformanceCountersType.top() == counter);
m_runningPerformanceCountersType.pop();
SystemTimeStamp* timeStamp = m_systemUtils->CreateTimeStamp();
m_systemUtils->GetCurrentTimeStamp(timeStamp);
m_performanceCounters[counter] += m_systemUtils->TimeStampExactDiff(m_runningPerformanceCounters.top(), timeStamp);
m_systemUtils->DestroyTimeStamp(timeStamp);
m_systemUtils->DestroyTimeStamp(m_runningPerformanceCounters.top());
m_runningPerformanceCounters.pop();
if (counter == PCNT_ALL)
SavePerformanceCounters();
}
long long CProfiler::GetPerformanceCounterTime(PerformanceCounter counter)
{
return m_prevPerformanceCounters[counter];
}
float CProfiler::GetPerformanceCounterFraction(PerformanceCounter counter)
{
return static_cast<float>(m_prevPerformanceCounters[counter]) / static_cast<float>(m_prevPerformanceCounters[PCNT_ALL]);
}
void CProfiler::ResetPerformanceCounters()
{
for (int i = 0; i < PCNT_MAX; ++i)
{
m_performanceCounters[i] = 0;
}
}
void CProfiler::SavePerformanceCounters()
{
assert(m_runningPerformanceCounters.empty());
for (int i = 0; i < PCNT_MAX; ++i)
{
m_prevPerformanceCounters[i] = m_performanceCounters[i];
}
}

80
src/common/profiler.h Normal file
View File

@ -0,0 +1,80 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
class CSystemUtils;
struct SystemTimeStamp;
#include <stack>
/**
* \enum PerformanceCounter
* \brief Type of counter testing performance
*/
enum PerformanceCounter
{
PCNT_EVENT_PROCESSING, //! < event processing (except update events)
PCNT_UPDATE_ALL, //! < the whole frame update process
PCNT_UPDATE_ENGINE, //! < frame update in CEngine
PCNT_UPDATE_PARTICLE, //! < frame update in CParticle
PCNT_UPDATE_GAME, //! < frame update in CRobotMain
PCNT_UPDATE_CBOT, //! < running CBot code (part of CRobotMain update)
PCNT_RENDER_ALL, //! < the whole rendering process
PCNT_RENDER_PARTICLE_WORLD, //! < rendering the particles in 3D
PCNT_RENDER_PARTICLE_IFACE, //! < rendering the particles in 2D interface
PCNT_RENDER_WATER, //! < rendering the water
PCNT_RENDER_TERRAIN, //! < rendering the terrain
PCNT_RENDER_OBJECTS, //! < rendering the 3D objects
PCNT_RENDER_INTERFACE, //! < rendering 2D interface
PCNT_RENDER_SHADOW_MAP, //! < rendering shadow map
PCNT_SWAP_BUFFERS, //! < swapping buffers and vsync
PCNT_ALL, //! < all counters together
PCNT_MAX
};
class CProfiler
{
public:
static void SetSystemUtils(CSystemUtils* systemUtils);
static void StartPerformanceCounter(PerformanceCounter counter);
static void StopPerformanceCounter(PerformanceCounter counter);
static long long GetPerformanceCounterTime(PerformanceCounter counter);
static float GetPerformanceCounterFraction(PerformanceCounter counter);
private:
static void ResetPerformanceCounters();
static void SavePerformanceCounters();
private:
static CSystemUtils* m_systemUtils;
static long long m_performanceCounters[PCNT_MAX];
static long long m_prevPerformanceCounters[PCNT_MAX];
static std::stack<SystemTimeStamp*> m_runningPerformanceCounters;
static std::stack<PerformanceCounter> m_runningPerformanceCountersType;
};

View File

@ -28,20 +28,20 @@ COutputStream::COutputStream()
{
}
COutputStream::COutputStream(const std::string& filename)
COutputStream::COutputStream(const std::string& filename, std::ios_base::openmode mode)
: COutputStreamBufferContainer(),
std::ostream(&m_buffer)
{
open(filename);
open(filename, mode);
}
COutputStream::~COutputStream()
{
}
void COutputStream::open(const std::string& filename)
void COutputStream::open(const std::string& filename, std::ios_base::openmode mode)
{
m_buffer.open(filename);
m_buffer.open(filename, mode);
}
void COutputStream::close()

View File

@ -35,10 +35,23 @@ class COutputStream : public COutputStreamBufferContainer, public std::ostream
{
public:
COutputStream();
COutputStream(const std::string& filename);
/** Construct and Open Stream for writing
*
* \param filename
* \param Mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file
*
*/
COutputStream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out);
virtual ~COutputStream();
void open(const std::string& filename);
/** Open Stream for writing
*
* \param filename
* \param Mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file
*
*/
void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out);
void close();
bool is_open();
};

View File

@ -45,10 +45,13 @@ COutputStreamBuffer::~COutputStreamBuffer()
}
void COutputStreamBuffer::open(const std::string &filename)
void COutputStreamBuffer::open(const std::string &filename, std::ios_base::openmode mode)
{
if (PHYSFS_isInit())
m_file = PHYSFS_openWrite(CResourceManager::CleanPath(filename).c_str());
{
if ( mode == std::ios_base::out ) m_file = PHYSFS_openWrite(CResourceManager::CleanPath(filename).c_str());
else if ( mode == std::ios_base::app ) m_file = PHYSFS_openAppend(CResourceManager::CleanPath(filename).c_str());
}
}

View File

@ -35,7 +35,13 @@ public:
COutputStreamBuffer(const COutputStreamBuffer &) = delete;
COutputStreamBuffer &operator= (const COutputStreamBuffer &) = delete;
void open(const std::string &filename);
/** Open Stream Buffer for writing
*
* \param filename
* \param Mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file
*
*/
void open(const std::string &filename, std::ios_base::openmode mode);
void close();
bool is_open();

View File

@ -23,7 +23,7 @@
#include "common/config.h"
#if PLATFORM_WINDOWS
#include "app/system_windows.h"
#include "common/system/system_windows.h"
#endif
#include "common/logger.h"

View File

@ -717,6 +717,7 @@ void InitializeRestext()
stringsCbot[CBot::CBotErrPrivate] = TR("Private element");
stringsCbot[CBot::CBotErrNoPublic] = TR("Public required");
stringsCbot[CBot::CBotErrNoExpression] = TR("Expression expected after =");
stringsCbot[CBot::CBotErrAmbiguousCall] = TR("Ambiguous call to overloaded function");
stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero");
stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized");

View File

@ -18,24 +18,25 @@
*/
#include "app/system.h"
#include "common/system/system.h"
#include "common/config.h"
#if defined(PLATFORM_WINDOWS)
#include "app/system_windows.h"
#include "common/system/system_windows.h"
#elif defined(PLATFORM_LINUX)
#include "app/system_linux.h"
#include "common/system/system_linux.h"
#elif defined(PLATFORM_MACOSX)
#include "app/system_macosx.h"
#include "common/system/system_macosx.h"
#else
#include "app/system_other.h"
#include "common/system/system_other.h"
#endif
#include "common/make_unique.h"
#include <cassert>
#include <iostream>
#include <algorithm>
std::unique_ptr<CSystemUtils> CSystemUtils::Create()
@ -152,11 +153,7 @@ SystemTimeStamp* CSystemUtils::CreateTimeStamp()
void CSystemUtils::DestroyTimeStamp(SystemTimeStamp *stamp)
{
for (auto& timeStamp : m_timeStamps)
{
if (timeStamp.get() == stamp)
timeStamp.reset();
}
m_timeStamps.erase(std::remove_if(m_timeStamps.begin(), m_timeStamps.end(), [&](const std::unique_ptr<SystemTimeStamp>& timeStamp) { return timeStamp.get() == stamp; }));
}
void CSystemUtils::CopyTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *src)

View File

@ -18,7 +18,7 @@
*/
/**
* \file app/system.h
* \file common/system/system.h
* \brief System functions: time stamps, info dialogs, etc.
*/

View File

@ -17,7 +17,7 @@
* along with this program. If not, see http://gnu.org/licenses
*/
#include "app/system_linux.h"
#include "common/system/system_linux.h"
#include "common/logger.h"

View File

@ -18,11 +18,11 @@
*/
/**
* \file app/system_linux.h
* \file common/system/system_linux.h
* \brief Linux-specific implementation of system functions
*/
#include "app/system.h"
#include "common/system/system.h"
#include <sys/time.h>

View File

@ -17,7 +17,7 @@
* along with this program. If not, see http://gnu.org/licenses
*/
#include "app/system_macosx.h"
#include "common/system/system_macosx.h"
#include "common/logger.h"

View File

@ -18,12 +18,12 @@
*/
/**
* \file app/system_macosx.h
* \file common/system/system_macosx.h
* \brief MacOSX-specific implementation of system functions
*/
#include "app/system.h"
#include "app/system_other.h"
#include "common/system/system.h"
#include "common/system/system_other.h"
//@colobot-lint-exclude UndefinedFunctionRule

View File

@ -17,7 +17,7 @@
* along with this program. If not, see http://gnu.org/licenses
*/
#include "app/system_other.h"
#include "common/system/system_other.h"
void CSystemUtilsOther::Init()

View File

@ -18,11 +18,11 @@
*/
/**
* \file app/system_other.h
* \file common/system/system_other.h
* \brief Fallback code for other systems
*/
#include "app/system.h"
#include "common/system/system.h"
#include <SDL.h>

View File

@ -17,7 +17,7 @@
* along with this program. If not, see http://gnu.org/licenses
*/
#include "app/system_windows.h"
#include "common/system/system_windows.h"
#include "common/logger.h"

View File

@ -18,11 +18,11 @@
*/
/**
* \file app/system_windows.h
* \file common/system/system_windows.h
* \brief Windows-specific implementation of system functions
*/
#include "app/system.h"
#include "common/system/system.h"
//@colobot-lint-exclude UndefinedFunctionRule

View File

@ -59,6 +59,11 @@ public:
m_name(name)
{}
~CResourceOwningThread()
{
SDL_DetachThread(m_thread);
}
void Start()
{
CSDLMutexWrapper mutex;
@ -74,7 +79,7 @@ public:
SDL_LockMutex(*mutex);
SDL_CreateThread(Run, !m_name.empty() ? m_name.c_str() : nullptr, reinterpret_cast<void*>(&data));
m_thread = SDL_CreateThread(Run, !m_name.empty() ? m_name.c_str() : nullptr, reinterpret_cast<void*>(&data));
while (!condition)
{
@ -84,6 +89,13 @@ public:
SDL_UnlockMutex(*mutex);
}
void Join()
{
if (m_thread == nullptr) return;
SDL_WaitThread(m_thread, nullptr);
m_thread = nullptr;
}
private:
static int Run(void* data)
{
@ -117,4 +129,5 @@ private:
ThreadFunctionPtr m_threadFunction;
ResourceUPtr m_resource;
std::string m_name;
SDL_Thread* m_thread = nullptr;
};

View File

@ -19,6 +19,8 @@
#pragma once
#include "common/thread/sdl_mutex_wrapper.h"
#include <SDL_thread.h>
/**
@ -45,6 +47,16 @@ public:
return m_cond;
}
void Signal()
{
SDL_CondSignal(m_cond);
}
void Wait(SDL_mutex* mutex)
{
SDL_CondWait(m_cond, mutex);
}
private:
SDL_cond* m_cond;
};

View File

@ -45,6 +45,16 @@ public:
return m_mutex;
}
void Lock()
{
SDL_LockMutex(m_mutex);
}
void Unlock()
{
SDL_UnlockMutex(m_mutex);
}
private:
SDL_mutex* m_mutex;
};

View File

@ -0,0 +1,77 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "common/make_unique.h"
#include "common/thread/resource_owning_thread.h"
#include <functional>
#include <string>
#include <memory>
/**
* \class CThread
* \brief Wrapper for using SDL_thread with std::function
*/
class CThread
{
public:
using ThreadFunctionPtr = std::function<void()>;
private:
struct ThreadData
{
ThreadFunctionPtr func;
};
public:
CThread(ThreadFunctionPtr func, std::string name = "")
: m_func(std::move(func))
, m_name(name)
{}
void Start()
{
std::unique_ptr<ThreadData> data = MakeUnique<ThreadData>();
data->func = m_func;
m_thread = MakeUnique<CResourceOwningThread<ThreadData>>(Run, std::move(data), m_name);
m_thread->Start();
}
void Join()
{
if (!m_thread) return;
m_thread->Join();
}
CThread(const CThread&) = delete;
CThread& operator=(const CThread&) = delete;
private:
static void Run(std::unique_ptr<ThreadData> data)
{
data->func();
}
std::unique_ptr<CResourceOwningThread<ThreadData>> m_thread;
ThreadFunctionPtr m_func;
std::string m_name;
};

View File

@ -0,0 +1,92 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "common/make_unique.h"
#include "common/thread/sdl_cond_wrapper.h"
#include "common/thread/sdl_mutex_wrapper.h"
#include "common/thread/thread.h"
#include <functional>
#include <string>
#include <queue>
/**
* \class CWorkerThread
* \brief Thread that runs functions, one at a time
*/
class CWorkerThread
{
public:
using ThreadFunctionPtr = std::function<void()>;
public:
CWorkerThread(std::string name = "")
: m_thread(std::bind(&CWorkerThread::Run, this), name)
{
m_thread.Start();
}
~CWorkerThread()
{
m_mutex.Lock();
m_running = false;
m_cond.Signal();
m_mutex.Unlock();
m_thread.Join();
}
void Start(ThreadFunctionPtr func)
{
m_mutex.Lock();
m_queue.push(func);
m_cond.Signal();
m_mutex.Unlock();
}
CWorkerThread(const CWorkerThread&) = delete;
CWorkerThread& operator=(const CWorkerThread&) = delete;
private:
void Run()
{
m_mutex.Lock();
while (true)
{
while (m_queue.empty() && m_running)
{
m_cond.Wait(*m_mutex);
}
if (!m_running) break;
ThreadFunctionPtr func = m_queue.front();
m_queue.pop();
func();
}
m_mutex.Unlock();
}
CThread m_thread;
CSDLMutexWrapper m_mutex;
CSDLCondWrapper m_cond;
bool m_running = true;
std::queue<ThreadFunctionPtr> m_queue;
};

View File

@ -11,3 +11,48 @@
* defining a border between pure graphics engine and other parts of application.
*/
/**
* \page graphics Graphics engine
*
* The graphics engine consists of 3 parts:
* * core - low-level device code (currently with only OpenGL implementation)
* * main engine - managing and displaying 3D environment (terrain, models, water, sky, effects, camera, etc.)
* * 2D interface - classes drawing the 2D interface (menus, buttons, editor, HUD elements)
*
* \section coords Drawing coordinates
*
* \subsection coords2d 2D interface
*
* 2D interface is drawn by setting orthogonal projection yielding the following 2D coordinate system:
*
* \image html 2d_coord.png
*
* Depth test is disabled for 2D interface, so Z coordinates are irrelevant.
*
* The coordinate system is constant and is independent of resolution or screen proportions.
*
* UI elements are laid out by computing these standard coordinates, using 640x480 resoultion as reference.
* That is, their coordinates are computed like so: x = 32.0f/640.0f, y = 400.0f/480.0f.
*
* \subsection coords3d 3D environment
*
* 3D environment is drawn using the following coordinate system:
*
* \image html 3d_canonical_coords.png
*
* The base coordinate system is like depicted above with viewport on Z=0 plane.
* The coordinates are then transformed by world, view and projection matrices to yield screen coordinates.
*
* The base coordinates are also model coordinates. All models must be modelled in this setup.
* Scale should be kept proportional to existing models.
*
* The world matrix defines the transformation from model coordinate system to the point and orientation
* in 3D scene. This matrix is defined one per every <i>graphics engine</i> object.
* (Note the emphasis - the objects as defined in game engine do not necessarily correspond to
* one graphics engine object.)
*
* The view and projection matrices define the viewing point and volume, and are mostly managed by Gfx::CCamera.
* View is defined by Math::LoadViewMatrix() function, that is using 3 vectors: eye position (eyePt),
* target point (lookatPt) and up vector (upVec). Projection is always perspective,
* with changing view angle (focus).
*/

View File

@ -22,14 +22,16 @@
#include "app/app.h"
#include "app/input.h"
#include "app/system.h"
#include "common/image.h"
#include "common/key.h"
#include "common/logger.h"
#include "common/make_unique.h"
#include "common/profiler.h"
#include "common/stringutils.h"
#include "common/system/system.h"
#include "common/thread/resource_owning_thread.h"
#include "graphics/core/device.h"
@ -447,9 +449,9 @@ void CEngine::FrameUpdate()
m_lightMan->UpdateProgression(rTime);
m_app->StartPerformanceCounter(PCNT_UPDATE_PARTICLE);
CProfiler::StartPerformanceCounter(PCNT_UPDATE_PARTICLE);
m_particle->FrameParticle(rTime);
m_app->StopPerformanceCounter(PCNT_UPDATE_PARTICLE);
CProfiler::StopPerformanceCounter(PCNT_UPDATE_PARTICLE);
ComputeDistance();
UpdateGeometry();
@ -3185,9 +3187,6 @@ void CEngine::Render()
color = m_backgroundColorDown;
m_device->SetClearColor(color);
// Render shadow map
if (m_drawWorld && m_shadowMapping)
RenderShadowMap();
// Begin the scene
m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true);
@ -3201,6 +3200,10 @@ void CEngine::Render()
}
else
{
// Render shadow map
if (m_drawWorld && m_shadowMapping)
RenderShadowMap();
UseMSAA(true);
DrawBackground(); // draws the background
@ -3219,9 +3222,9 @@ void CEngine::Render()
}
}
m_app->StartPerformanceCounter(PCNT_RENDER_INTERFACE);
CProfiler::StartPerformanceCounter(PCNT_RENDER_INTERFACE);
DrawInterface();
m_app->StopPerformanceCounter(PCNT_RENDER_INTERFACE);
CProfiler::StopPerformanceCounter(PCNT_RENDER_INTERFACE);
// End the scene
m_device->EndScene();
@ -3260,7 +3263,7 @@ void CEngine::Draw3DScene()
m_water->DrawBack(); // draws water background
m_app->StartPerformanceCounter(PCNT_RENDER_TERRAIN);
CProfiler::StartPerformanceCounter(PCNT_RENDER_TERRAIN);
// Draw terrain
@ -3320,11 +3323,11 @@ void CEngine::Draw3DScene()
if (!m_shadowMapping)
DrawShadowSpots();
m_app->StopPerformanceCounter(PCNT_RENDER_TERRAIN);
CProfiler::StopPerformanceCounter(PCNT_RENDER_TERRAIN);
// Draw other objects
m_app->StartPerformanceCounter(PCNT_RENDER_OBJECTS);
CProfiler::StartPerformanceCounter(PCNT_RENDER_OBJECTS);
bool transparent = false;
@ -3441,7 +3444,7 @@ void CEngine::Draw3DScene()
}
}
m_app->StopPerformanceCounter(PCNT_RENDER_OBJECTS);
CProfiler::StopPerformanceCounter(PCNT_RENDER_OBJECTS);
m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN);
@ -3454,9 +3457,9 @@ void CEngine::Draw3DScene()
m_lightMan->DebugDumpLights();
}
m_app->StartPerformanceCounter(PCNT_RENDER_WATER);
CProfiler::StartPerformanceCounter(PCNT_RENDER_WATER);
m_water->DrawSurf(); // draws water surface
m_app->StopPerformanceCounter(PCNT_RENDER_WATER);
CProfiler::StopPerformanceCounter(PCNT_RENDER_WATER);
m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
@ -3478,9 +3481,9 @@ void CEngine::Draw3DScene()
}
m_displayGoto.clear();
m_app->StartPerformanceCounter(PCNT_RENDER_PARTICLE);
CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD);
m_particle->DrawParticle(SH_WORLD); // draws the particles of the 3D world
m_app->StopPerformanceCounter(PCNT_RENDER_PARTICLE);
CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD);
m_device->SetRenderState(RENDER_STATE_LIGHTING, true);
@ -3706,7 +3709,7 @@ void CEngine::RenderShadowMap()
if (!m_shadowMapping) return;
m_app->StartPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
CProfiler::StartPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
// If no shadow map texture exists, create it
if (m_shadowMap.id == 0)
@ -3896,7 +3899,7 @@ void CEngine::RenderShadowMap()
m_device->SetColorMask(true, true, true, true);
m_device->Clear();
m_app->StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
m_device->SetRenderMode(RENDER_MODE_NORMAL);
m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false);
@ -4031,7 +4034,9 @@ void CEngine::DrawInterface()
if (!m_screenshotMode && m_renderInterface)
{
CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE);
m_particle->DrawParticle(SH_INTERFACE); // draws the particles of the interface
CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE);
}
// 3D objects drawn in front of interface
@ -5029,8 +5034,8 @@ void CEngine::DrawStats()
return;
float height = m_text->GetAscent(FONT_COLOBOT, 13.0f);
float width = 0.25f;
const int TOTAL_LINES = 20;
float width = 0.4f;
const int TOTAL_LINES = 22;
Math::Point pos(0.05f * m_size.x/m_size.y, 0.05f + TOTAL_LINES * height);
@ -5052,72 +5057,77 @@ void CEngine::DrawStats()
SetState(ENG_RSTATE_TEXT);
std::stringstream str;
auto drawStatsLine = [&](const std::string& name, const std::string& value)
auto drawStatsLine = [&](const std::string& name, const std::string& value, const std::string& value2)
{
if (!name.empty())
{
str.str("");
str << name << ": " << value;
m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
}
m_text->DrawText(name+":", FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
pos.x += 0.25f;
if (!value.empty())
m_text->DrawText(value, FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
pos.x += 0.15f;
if (!value2.empty())
m_text->DrawText(value2, FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_RIGHT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
pos.x -= 0.4f;
pos.y -= height;
};
auto drawStatsValue = [&](const std::string& name, float value)
auto drawStatsValue = [&](const std::string& name, long long time)
{
str.str("");
str << std::fixed << std::setprecision(2) << value;
drawStatsLine(name, str.str());
float value = static_cast<float>(time)/CProfiler::GetPerformanceCounterTime(PCNT_ALL);
drawStatsLine(name, StrUtils::Format("%.2f", value), StrUtils::Format("%.2f ms", time/1e6f));
};
auto drawStatsCounter = [&](const std::string& name, PerformanceCounter counter)
{
drawStatsValue(name, m_app->GetPerformanceCounterData(counter));
drawStatsValue(name, CProfiler::GetPerformanceCounterTime(counter));
};
// TODO: Find a more generic way to calculate these in CProfiler
float engineUpdate = m_app->GetPerformanceCounterData(PCNT_UPDATE_ENGINE) -
m_app->GetPerformanceCounterData(PCNT_UPDATE_PARTICLE);
long long engineUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) -
CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_PARTICLE);
float otherUpdate = m_app->GetPerformanceCounterData(PCNT_UPDATE_ALL) -
engineUpdate -
m_app->GetPerformanceCounterData(PCNT_UPDATE_PARTICLE) -
m_app->GetPerformanceCounterData(PCNT_UPDATE_GAME);
long long gameUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME) -
CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_CBOT);
float otherRender = m_app->GetPerformanceCounterData(PCNT_RENDER_ALL) -
m_app->GetPerformanceCounterData(PCNT_RENDER_PARTICLE) -
m_app->GetPerformanceCounterData(PCNT_RENDER_WATER) -
m_app->GetPerformanceCounterData(PCNT_RENDER_TERRAIN) -
m_app->GetPerformanceCounterData(PCNT_RENDER_OBJECTS) -
m_app->GetPerformanceCounterData(PCNT_RENDER_INTERFACE) -
m_app->GetPerformanceCounterData(PCNT_RENDER_SHADOW_MAP);
long long otherUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ALL) -
CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) -
CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME);
long long otherRender = CProfiler::GetPerformanceCounterTime(PCNT_RENDER_ALL) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_PARTICLE_WORLD) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_WATER) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_TERRAIN) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_OBJECTS) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_INTERFACE) -
CProfiler::GetPerformanceCounterTime(PCNT_RENDER_SHADOW_MAP);
drawStatsCounter("Event processing", PCNT_EVENT_PROCESSING);
drawStatsLine("", "");
drawStatsLine( "", "", "");
drawStatsCounter("Frame update", PCNT_UPDATE_ALL);
drawStatsValue (" Engine update", engineUpdate);
drawStatsCounter(" Particle update", PCNT_UPDATE_PARTICLE);
drawStatsCounter(" Game update", PCNT_UPDATE_GAME);
drawStatsValue (" Game update", gameUpdate);
drawStatsCounter(" CBot programs", PCNT_UPDATE_CBOT);
drawStatsValue( " Other update", otherUpdate);
drawStatsLine("", "");
drawStatsLine( "", "", "");
drawStatsCounter("Frame render", PCNT_RENDER_ALL);
drawStatsCounter(" Particle render", PCNT_RENDER_PARTICLE);
drawStatsCounter(" Particle render", PCNT_RENDER_PARTICLE_WORLD);
drawStatsCounter(" Water render", PCNT_RENDER_WATER);
drawStatsCounter(" Terrain render", PCNT_RENDER_TERRAIN);
drawStatsCounter(" Objects render", PCNT_RENDER_OBJECTS);
drawStatsCounter(" UI render", PCNT_RENDER_INTERFACE);
drawStatsCounter(" particles", PCNT_RENDER_PARTICLE_IFACE);
drawStatsCounter(" Shadow map render", PCNT_RENDER_SHADOW_MAP);
drawStatsValue( " Other render", otherRender);
drawStatsCounter("Swap buffers & VSync", PCNT_SWAP_BUFFERS);
drawStatsLine("", "");
drawStatsLine( "Triangles", StrUtils::ToString<int>(m_statisticTriangle));
drawStatsValue( "FPS", m_fps);
drawStatsLine("", "");
str.str("");
drawStatsLine( "", "", "");
drawStatsLine( "Triangles", StrUtils::ToString<int>(m_statisticTriangle), "");
drawStatsLine( "FPS", StrUtils::Format("%.3f", m_fps), "");
drawStatsLine( "", "", "");
std::stringstream str;
str << std::fixed << std::setprecision(2) << m_statisticPos.x << "; " << m_statisticPos.z;
drawStatsLine( "Position", str.str());
drawStatsLine( "Position", str.str(), "");
}
void CEngine::DrawTimer()

View File

@ -581,7 +581,7 @@ int CParticle::CreateTrack(Math::Vector pos, Math::Vector speed, Math::Point dim
if (!m_track[i].used) // free?
{
int rank = channel;
GetRankFromChannel(rank);
if (!CheckChannel(rank)) return -1;
m_particle[rank].trackRank = i;
m_track[i].used = true;
@ -636,27 +636,28 @@ void CParticle::CreateWheelTrace(const Math::Vector &p1, const Math::Vector &p2,
void CParticle::GetRankFromChannel(int &channel)
/** Adapts the channel so it can be used as an offset in m_particle */
bool CParticle::CheckChannel(int &channel)
{
int uniqueStamp = (channel>>16)&0xffff;
channel &= 0xffff;
if (channel < 0 || channel >= MAXPARTICULE*MAXPARTITYPE) throw std::runtime_error("Tried to access invalid particle channel (invalid ID)");
if (!m_particle[channel].used) throw std::runtime_error("Tried to access invalid particle channel (used=false)");
if (m_particle[channel].uniqueStamp != uniqueStamp) throw std::runtime_error("Tried to access invalid particle channel (uniqueStamp changed)");
}
if (channel < 0) return false;
if (channel >= MAXPARTICULE*MAXPARTITYPE) return false;
bool CParticle::ParticleExists(int channel)
{
try
{
GetRankFromChannel(channel);
return true;
}
catch (const std::runtime_error& e)
if (!m_particle[channel].used)
{
GetLogger()->Trace("Particle %d:%d doesn't exist anymore (used=false)\n", channel, uniqueStamp);
return false;
}
if (m_particle[channel].uniqueStamp != uniqueStamp)
{
GetLogger()->Trace("Particle %d:%d doesn't exist anymore (uniqueStamp changed)\n", channel, uniqueStamp);
return false;
}
return true;
}
void CParticle::DeleteRank(int rank)
@ -684,7 +685,7 @@ void CParticle::DeleteParticle(ParticleType type)
void CParticle::DeleteParticle(int channel)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
if (m_totalInterface[channel/MAXPARTICULE][m_particle[channel].sheet] > 0 )
m_totalInterface[channel/MAXPARTICULE][m_particle[channel].sheet]--;
@ -698,50 +699,50 @@ void CParticle::DeleteParticle(int channel)
void CParticle::SetObjectLink(int channel, CObject *object)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
m_particle[channel].objLink = object;
}
void CParticle::SetObjectFather(int channel, CObject *object)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
m_particle[channel].objFather = object;
}
void CParticle::SetPosition(int channel, Math::Vector pos)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
m_particle[channel].pos = pos;
}
void CParticle::SetDimension(int channel, Math::Point dim)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
m_particle[channel].dim = dim;
}
void CParticle::SetZoom(int channel, float zoom)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
m_particle[channel].zoom = zoom;
}
void CParticle::SetAngle(int channel, float angle)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
m_particle[channel].angle = angle;
}
void CParticle::SetIntensity(int channel, float intensity)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
m_particle[channel].intensity = intensity;
}
void CParticle::SetParam(int channel, Math::Vector pos, Math::Point dim, float zoom,
float angle, float intensity)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
m_particle[channel].pos = pos;
m_particle[channel].dim = dim;
m_particle[channel].zoom = zoom;
@ -751,16 +752,17 @@ void CParticle::SetParam(int channel, Math::Vector pos, Math::Point dim, float z
void CParticle::SetPhase(int channel, ParticlePhase phase, float duration)
{
GetRankFromChannel(channel);
if (!CheckChannel(channel)) return;
m_particle[channel].phase = phase;
m_particle[channel].duration = duration;
m_particle[channel].phaseTime = m_particle[channel].time;
}
Math::Vector CParticle::GetPosition(int channel)
bool CParticle::GetPosition(int channel, Math::Vector &pos)
{
GetRankFromChannel(channel);
return m_particle[channel].pos;
if (!CheckChannel(channel)) return false;
pos = m_particle[channel].pos;
return true;
}
void CParticle::SetFrameUpdate(int sheet, bool update)

View File

@ -162,9 +162,9 @@ enum ParticlePhase
struct Particle
{
bool used = false; //!< true if this channel is used, false if not
bool used = false; // TRUE -> particle used
bool ray = false; // TRUE -> ray with goal
unsigned short uniqueStamp = 0; //!< unique marker added to particle channel ID to make sure this is still the same particle
unsigned short uniqueStamp = 0; // unique mark
short sheet = 0; // sheet (0..n)
ParticleType type = {}; // type PARTI*
ParticlePhase phase = {}; // phase PARPH*
@ -279,7 +279,7 @@ public:
void SetPhase(int channel, ParticlePhase phase, float duration);
//! Returns the position of the particle
Math::Vector GetPosition(int channel);
bool GetPosition(int channel, Math::Vector &pos);
//! Returns the color if you're in the fog or black if you're not
Color GetFogColor(Math::Vector pos);
@ -291,18 +291,15 @@ public:
//! Draws all the particles
void DrawParticle(int sheet);
//! Checks if given particle channel still exists
bool ParticleExists(int channel);
protected:
//! Removes a particle of given rank
void DeleteRank(int rank);
/**
* \brief Adapts the channel so it can be used as an offset in m_particle
* \param channel Channel number to process, will be modified to be index of particle in m_particle
* \throw std::runtime_error if this particle does not exist any more
* \return true if success, false if particle doesn't exist anymore
**/
void GetRankFromChannel(int &channel);
bool CheckChannel(int &channel);
//! Draws a triangular particle
void DrawParticleTriangle(int i);
//! Draw a normal particle

View File

@ -0,0 +1,82 @@
/**
* \namespace Gfx::ModelInput
* \brief Functions related to model loading
*/
/**
* \namespace Gfx::ModelOutput
* \brief Functions related to model saving
*/
/**
* \page models Models
*
* Model formats and associated issues are described briefly for graphics designers and developers.
*
* \section format Model format
*
* \todo Update for the new model format
*
* Colobot models are basically a collection of triangles with some associated data.
* <span style="text-decoration: line-through;">In the code, the class Gfx::CModel (src/graphics/model/model.h)
* is responsible for reading/writing model files.</span> Each triangle of model contains the information
* as stored in Gfx::ModelTriangle struct defined in model_triangle.h header, that is:
* * 3 triangle points (coordinates, normals, UV texture coordinates for 2 textures)
* * material (ambient, diffuse, specular colors)
* * file names for 1st and 2nd texture (or, for 2nd texture - variable flag)
* * rendering state
* * <span style="text-decoration: line-through;">min and max values of LOD (= level of details)</span>
*
* \subsection textures Textures
*
* 1st texture is always static - the assigned image name.
*
* 2nd texture can be set explicitly in 2nd texture name, with variable flag set to false.
* It is then static as 1st texture.
*
* But if variable flag is set, the texture will be applied dynamically by the graphics engine.
* It will be one of dirtyXX.png textures, depending on current setup.
*
* \subsection renderstates Rendering states
*
* Rendering state is one of Gfx::CEngine's rendering states, that is a mask of enum Gfx::EngineRenderState values
* from src/graphics/engine/engine.h.
*
* For most purposes, the default render (Gfx::ENG_RSTATE_NORMAL = 0) state will be sufficient.
* This state enables regular one-texture rendering.
*
* To use 2nd texture, set one of Gfx::ENG_RSTATE_DUAL_BLACK or Gfx::ENG_RSTATE_DUAL_WHITE states.
* Other states, enabling specific behavior may be used in rare cases.
*
* \subsection lod Min and max LOD
*
* <span style="text-decoration: line-through;">LOD is used to display different model triangles based
* on distance to viewer. The given triangle will only be displayed if the distance is within bounds [min, max].</span>
*
* <span style="text-decoration: line-through;">For now, use standard definitions of min and max which
* fall into 3 categories:</span>
* * <span style="text-decoration: line-through;">min = 0, max = 100 - max detail</span>
* * <span style="text-decoration: line-through;">min = 100, max = 200 - medium detail</span>
* * <span style="text-decoration: line-through;">min = 200, max = 1 000 000 - low detail</span>
*
* \section fileformats File formats
*
* There are currently 3 file formats recognized by Gfx::ModelInput and Gfx::ModelOutput:
* * old binary format (in 3 versions, though mostly only the 3rd one is used) - this is the format
* of original model files; it is deprecated now and will be removed in the future
* * new text format - preferred for now, as it is easy to handle and convert to other formats as necessary
* * new binary format - contains the same information as new text format
*
* \section blenderimport Import/export in Blender
*
* The plugin to import and export models in Blender is contained in \p tools/blender-scipts.py.
* To use it, install it as per instructions [on Blender wiki](http://wiki.blender.org/index.php/Doc:2.6/Manual/Extensions/Python/Add-Ons).
* It will register new menu entries under File -> Import and File -> Export. Import is always active, but to export,
* you have to select a mesh object first.
*
* Textures are loaded from the same directory as the model file.
*
* Additional data like state, variable texture flag and min and max LOD can be given as user attributes.
*
* If you have any problems, please contact \b piotrdz on ICC forum or IRC channels.
*/

View File

@ -780,10 +780,10 @@ bool CRobotMain::ProcessEvent(Event &event)
if (event.type == EVENT_KEY_DOWN &&
event.GetData<KeyEventData>()->key == KEY(RETURN) && m_cmdEdit)
{
char cmd[50];
std::string cmd;
Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD));
if (pe == nullptr) return false;
pe->GetText(cmd, 50);
cmd = pe->GetText(50);
pe->SetText("");
pe->ClearState(Ui::STATE_VISIBLE);
m_interface->SetFocus(nullptr);
@ -981,10 +981,13 @@ bool CRobotMain::ProcessEvent(Event &event)
{
CLevelParserLine line("CreateObject");
line.AddParam("type", MakeUnique<CLevelParserParam>(obj->GetType()));
Math::Vector pos = obj->GetPosition()/g_unit;
pos.y = 0.0f;
line.AddParam("pos", MakeUnique<CLevelParserParam>(pos));
line.AddParam("dir", MakeUnique<CLevelParserParam>(obj->GetRotationY()));
float dir = Math::NormAngle(obj->GetRotationY()) / Math::PI;
line.AddParam("dir", MakeUnique<CLevelParserParam>(dir));
std::stringstream ss;
ss << line;
@ -2681,7 +2684,7 @@ void CRobotMain::ScenePerso()
m_lightMan->FlushLights();
m_particle->FlushParticle();
m_levelFile = "levels/other/perso000.txt";
m_levelFile = "levels/other/perso.txt";
try
{
CreateScene(false, true, false); // sets scene

View File

@ -131,7 +131,6 @@ Program* CProgramStorageObjectImpl::CloneProgram(Program* program)
// TODO: Is there any reason CScript doesn't have a function to get the program code directly?
auto edit = MakeUnique<Ui::CEdit>();
edit->SetMaxChar(Ui::EDITSTUDIOMAX);
program->script->PutScript(edit.get(), "");
newprog->script->GetScript(edit.get());

View File

@ -22,6 +22,7 @@
#include "CBot/CBot.h"
#include "common/global.h"
#include "common/profiler.h"
#include "level/robotmain.h"
@ -74,6 +75,7 @@ bool CProgrammableObjectImpl::EventProcess(const Event &event)
if ( GetActivity() )
{
CProfiler::StartPerformanceCounter(PCNT_UPDATE_CBOT);
if ( IsProgram() ) // current program?
{
if ( m_currentProgram->script->Continue() )
@ -86,6 +88,7 @@ bool CProgrammableObjectImpl::EventProcess(const Event &event)
{
TraceRecordFrame();
}
CProfiler::StopPerformanceCounter(PCNT_UPDATE_CBOT);
}
}

View File

@ -282,7 +282,10 @@ std::vector<CObject*> CObjectManager::RadarAll(CObject* pThis, Math::Vector this
RadarFilter filter_flying = static_cast<RadarFilter>(filter & (FILTER_ONLYLANDING | FILTER_ONLYFLYING));
RadarFilter filter_enemy = static_cast<RadarFilter>(filter & (FILTER_FRIENDLY | FILTER_ENEMY | FILTER_NEUTRAL));
std::map<float, CObject*> best;
// Use a multimap to allow for multiple objects at exactly the same distance
// from the origin to be returned.
std::multimap<float, CObject*> best;
for ( auto it = m_objects.begin() ; it != m_objects.end() ; ++it )
{
pObj = it->second.get();
@ -357,7 +360,7 @@ std::vector<CObject*> CObjectManager::RadarAll(CObject* pThis, Math::Vector this
a = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW !
if ( Math::TestAngle(a, iAngle-focus/2.0f, iAngle+focus/2.0f) || focus >= Math::PI*2.0f )
{
best[d] = pObj;
best.insert(std::make_pair(d, pObj));
}
}

View File

@ -2185,17 +2185,15 @@ void COldObject::PartiFrame(float rTime)
channel = m_objectPart[i].masterParti;
if ( channel == -1 ) continue;
if ( !m_particle->ParticleExists(channel) )
if ( !m_particle->GetPosition(channel, pos) )
{
m_objectPart[i].masterParti = -1; // particle no longer exists!
continue;
}
pos = m_particle->GetPosition(channel);
SetPartPosition(i, pos);
// Each part rotates differently
// Each song spins differently.
switch( i%5 )
{
case 0: factor = Math::Vector( 0.5f, 0.3f, 0.6f); break;

View File

@ -55,9 +55,9 @@ const int OBJECTMAXPART = 40;
struct ObjectPart
{
bool bUsed = false;
int object = -1; //!< identifier of the object in Gfx::CEngine
int parentPart = -1; //!< identifier of parent part
int masterParti = -1; //!< particle channel this part is connected to after explosion
int object = -1; // number of the object in CEngine
int parentPart = -1; // number of father part
int masterParti = -1; // master canal of the particle
Math::Vector position;
Math::Vector angle;
Math::Vector zoom;

View File

@ -105,9 +105,11 @@ void CScript::PutScript(Ui::CEdit* edit, const char* name)
bool CScript::GetScript(Ui::CEdit* edit)
{
int len = edit->GetTextLength();
m_script = MakeUniqueArray<char>(len+1);
m_script = MakeUniqueArray<char>(len+2);
std::string tmp = edit->GetText(len+1);
strncpy(m_script.get(), tmp.c_str(), len+1);
edit->GetText(m_script.get(), len+1);
edit->GetCursor(m_cursor2, m_cursor1);
m_len = strlen(m_script.get());
@ -592,13 +594,13 @@ void CScript::UpdateList(Ui::CList* list)
void CScript::ColorizeScript(Ui::CEdit* edit, int rangeStart, int rangeEnd)
{
if (rangeEnd > edit->GetMaxChar())
rangeEnd = edit->GetMaxChar();
if (rangeEnd > edit->GetTextLength())
rangeEnd = edit->GetTextLength();
edit->SetFormat(rangeStart, rangeEnd, Gfx::FONT_HIGHLIGHT_COMMENT); // anything not processed is a comment
// NOTE: Images are registered as index in some array, and that can be 0 which normally ends the string!
std::string text = std::string(edit->GetText(), edit->GetMaxChar());
std::string text = edit->GetText();
text = text.substr(rangeStart, rangeEnd-rangeStart);
auto tokens = CBot::CBotToken::CompileTokens(text.c_str());
@ -927,7 +929,6 @@ bool CScript::SendScript(const char* text)
if ( !Compile() ) return false;*/
Ui::CEdit* edit = m_interface->CreateEdit(Math::Point(0.0f, 0.0f), Math::Point(0.0f, 0.0f), 0, EVENT_EDIT9);
edit->SetMaxChar(Ui::EDITSTUDIOMAX);
edit->SetAutoIndent(m_engine->GetEditIndentMode());
edit->SetText(text, true);
GetScript(edit);
@ -947,7 +948,6 @@ bool CScript::ReadScript(const char* filename)
m_script.reset();
edit = m_interface->CreateEdit(Math::Point(0.0f, 0.0f), Math::Point(0.0f, 0.0f), 0, EVENT_EDIT9);
edit->SetMaxChar(Ui::EDITSTUDIOMAX);
edit->SetAutoIndent(m_engine->GetEditIndentMode());
edit->ReadText(filename);
GetScript(edit);
@ -966,7 +966,6 @@ bool CScript::WriteScript(const char* filename)
}
Ui::CEdit* edit = m_interface->CreateEdit(Math::Point(0.0f, 0.0f), Math::Point(0.0f, 0.0f), 0, EVENT_EDIT9);
edit->SetMaxChar(Ui::EDITSTUDIOMAX);
edit->SetAutoIndent(m_engine->GetEditIndentMode());
edit->SetText(m_script.get());
edit->WriteText(filename);

View File

@ -3000,10 +3000,18 @@ public:
m_file = std::move(os);
}
}
else if (mode == CBotFileAccessHandler::OpenMode::Append)
{
auto os = MakeUnique<COutputStream>(filename, std::ios_base::app);
if (os->is_open())
{
m_file = std::move(os);
}
}
if (Opened())
{
GetLogger()->Info("CBot open file '%s'\n", filename.c_str());
GetLogger()->Info("CBot open file '%s', mode '%c'\n", filename.c_str(), mode);
m_numFilesOpen++;
}
}
@ -3012,7 +3020,7 @@ public:
{
if (Opened())
{
GetLogger()->Debug("CBot close file\n");
GetLogger()->Info("CBot close file\n");
m_numFilesOpen--;
}
@ -3071,8 +3079,9 @@ public:
virtual bool DeleteFile(const std::string& filename) override
{
GetLogger()->Info("CBot delete file '%s'\n", filename.c_str());
return CResourceManager::Remove(PrepareFilename(filename));
std::string fname = PrepareFilename(filename);
GetLogger()->Info("CBot delete file '%s'\n", fname.c_str());
return CResourceManager::Remove(fname);
}
private:

View File

@ -32,7 +32,8 @@ CALSound::CALSound()
m_musicVolume(1.0f),
m_channelsLimit(2048),
m_device{},
m_context{}
m_context{},
m_thread("Music loading thread")
{
}
@ -144,18 +145,19 @@ bool CALSound::Cache(SoundType sound, const std::string &filename)
return false;
}
bool CALSound::CacheMusic(const std::string &filename)
void CALSound::CacheMusic(const std::string &filename)
{
if (m_music.find(filename) == m_music.end())
m_thread.Start([this, filename]()
{
auto buffer = MakeUnique<CBuffer>();
if (buffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
if (m_music.find(filename) == m_music.end())
{
m_music[filename] = std::move(buffer);
return true;
auto buffer = MakeUnique<CBuffer>();
if (buffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
{
m_music[filename] = std::move(buffer);
}
}
}
return false;
});
}
bool CALSound::IsCached(SoundType sound)
@ -573,53 +575,54 @@ void CALSound::SetListener(const Math::Vector &eye, const Math::Vector &lookat)
alListenerfv(AL_ORIENTATION, orientation);
}
bool CALSound::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
void CALSound::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
{
if (!m_enabled)
{
return false;
return;
}
CBuffer *buffer = nullptr;
// check if we have music in cache
if (m_music.find(filename) == m_music.end())
m_thread.Start([this, filename, repeat, fadeTime]()
{
GetLogger()->Debug("Music %s was not cached!\n", filename.c_str());
CBuffer* buffer = nullptr;
auto newBuffer = MakeUnique<CBuffer>();
buffer = newBuffer.get();
if (!newBuffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
// check if we have music in cache
if (m_music.find(filename) == m_music.end())
{
return false;
GetLogger()->Debug("Music %s was not cached!\n", filename.c_str());
auto newBuffer = MakeUnique<CBuffer>();
buffer = newBuffer.get();
if (!newBuffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
{
return;
}
m_music[filename] = std::move(newBuffer);
}
else
{
GetLogger()->Debug("Music loaded from cache\n");
buffer = m_music[filename].get();
}
m_music[filename] = std::move(newBuffer);
}
else
{
GetLogger()->Debug("Music loaded from cache\n");
buffer = m_music[filename].get();
}
if (m_currentMusic)
{
OldMusic old;
old.music = std::move(m_currentMusic);
old.fadeTime = fadeTime;
old.currentTime = 0.0f;
m_oldMusic.push_back(std::move(old));
}
if (m_currentMusic)
{
OldMusic old;
old.music = std::move(m_currentMusic);
old.fadeTime = fadeTime;
old.currentTime = 0.0f;
m_oldMusic.push_back(std::move(old));
}
m_currentMusic = MakeUnique<CChannel>();
m_currentMusic->SetBuffer(buffer);
m_currentMusic->SetVolume(m_musicVolume);
m_currentMusic->SetLoop(repeat);
m_currentMusic->Play();
return true;
m_currentMusic = MakeUnique<CChannel>();
m_currentMusic->SetBuffer(buffer);
m_currentMusic->SetVolume(m_musicVolume);
m_currentMusic->SetLoop(repeat);
m_currentMusic->Play();
});
}
bool CALSound::PlayPauseMusic(const std::string &filename, bool repeat)
void CALSound::PlayPauseMusic(const std::string &filename, bool repeat)
{
if (m_previousMusic.fadeTime > 0.0f)
{
@ -641,7 +644,7 @@ bool CALSound::PlayPauseMusic(const std::string &filename, bool repeat)
m_previousMusic.currentTime = 0.0f;
}
}
return PlayMusic(filename, repeat);
PlayMusic(filename, repeat);
}
void CALSound::StopPauseMusic()
@ -663,18 +666,6 @@ void CALSound::StopPauseMusic()
}
}
bool CALSound::RestartMusic()
{
if (!m_enabled || m_currentMusic == nullptr)
{
return false;
}
m_currentMusic->Stop();
m_currentMusic->Play();
return true;
}
void CALSound::StopMusic(float fadeTime)
{
if (!m_enabled || m_currentMusic == nullptr)
@ -699,16 +690,6 @@ bool CALSound::IsPlayingMusic()
return m_currentMusic->IsPlaying();
}
void CALSound::SuspendMusic()
{
if (!m_enabled || m_currentMusic == nullptr)
{
return;
}
m_currentMusic->Stop();
}
bool CALSound::CheckChannel(int &channel)
{
int id = (channel >> 16) & 0xffff;

View File

@ -26,6 +26,8 @@
#include "sound/sound.h"
#include "common/thread/worker_thread.h"
#include "sound/oalsound/buffer.h"
#include "sound/oalsound/channel.h"
#include "sound/oalsound/check.h"
@ -83,7 +85,7 @@ public:
bool Create() override;
bool Cache(SoundType, const std::string &) override;
bool CacheMusic(const std::string &) override;
void CacheMusic(const std::string &) override;
bool IsCached(SoundType) override;
bool IsCachedMusic(const std::string &) override;
@ -106,12 +108,10 @@ public:
bool StopAll() override;
bool MuteAll(bool mute) override;
bool PlayMusic(const std::string &filename, bool repeat, float fadeTime=2.0f) override;
bool RestartMusic() override;
void SuspendMusic() override;
void PlayMusic(const std::string &filename, bool repeat, float fadeTime = 2.0f) override;
void StopMusic(float fadeTime=2.0f) override;
bool IsPlayingMusic() override;
bool PlayPauseMusic(const std::string &filename, bool repeat) override;
void PlayPauseMusic(const std::string &filename, bool repeat) override;
void StopPauseMusic() override;
private:
@ -134,4 +134,5 @@ private:
OldMusic m_previousMusic;
Math::Vector m_eye;
Math::Vector m_lookat;
CWorkerThread m_thread;
};

View File

@ -52,22 +52,13 @@ void CSoundInterface::CacheAll()
}
}
void CSoundInterface::CacheCommonMusic()
{
CacheMusic("music/Intro1.ogg");
CacheMusic("music/Intro2.ogg");
CacheMusic("music/music010.ogg");
CacheMusic("music/music011.ogg");
}
bool CSoundInterface::Cache(SoundType sound, const std::string &file)
{
return true;
}
bool CSoundInterface::CacheMusic(const std::string &file)
void CSoundInterface::CacheMusic(const std::string &file)
{
return true;
}
bool CSoundInterface::IsCached(SoundType sound)
@ -156,17 +147,7 @@ bool CSoundInterface::MuteAll(bool mute)
return true;
}
bool CSoundInterface::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
{
return true;
}
bool CSoundInterface::RestartMusic()
{
return true;
}
void CSoundInterface::SuspendMusic()
void CSoundInterface::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
{
}
@ -176,12 +157,11 @@ void CSoundInterface::StopMusic(float fadeTime)
bool CSoundInterface::IsPlayingMusic()
{
return true;
return false;
}
bool CSoundInterface::PlayPauseMusic(const std::string &filename, bool repeat)
void CSoundInterface::PlayPauseMusic(const std::string &filename, bool repeat)
{
return true;
}
void CSoundInterface::StopPauseMusic()

View File

@ -72,9 +72,6 @@ public:
*/
void CacheAll();
/** Function called to add all music files to list */
void CacheCommonMusic();
/** Function called to cache sound effect file.
* This function is called by plugin interface for each file.
* \param sound - id of a file, will be used to identify sound files
@ -85,10 +82,10 @@ public:
/** Function called to cache music file.
* This function is called by CRobotMain for each file used in the mission.
* This function is executed asynchronously
* \param file - file to load
* \return return true on success
*/
virtual bool CacheMusic(const std::string &file);
virtual void CacheMusic(const std::string &file);
/** Function to check if sound effect file was cached.
* \param sound - id of a sound effect file
@ -205,22 +202,12 @@ public:
virtual bool MuteAll(bool mute);
/** Start playing music
* This function is executed asynchronously
* \param filename - name of file to play
* \param repeat - repeat playing
* \param fadeTime - time of transition between music
* \return return true on success
* \param fadeTime - time of transition between music, 0 to disable
*/
virtual bool PlayMusic(const std::string &filename, bool repeat, float fadeTime=2.0f);
/** Restart music
* \return return true on success
*/
virtual bool RestartMusic();
/** Susspend playing music
* \return nothing
*/
virtual void SuspendMusic();
virtual void PlayMusic(const std::string &filename, bool repeat, float fadeTime = 2.0f);
/** Stop playing music
* \return nothing
@ -233,11 +220,12 @@ public:
virtual bool IsPlayingMusic();
/** Start playing pause music
* This function is executed asynchronously
* \param filename - name of file to play
* \param repeat - repeat playing
* \return return true on success
*/
virtual bool PlayPauseMusic(const std::string &filename, bool repeat);
virtual void PlayPauseMusic(const std::string &filename, bool repeat);
/** Stop playing pause music and return to the mission music
* \return nothing

View File

@ -60,7 +60,6 @@ const float BIG_FONT = 1.6f;
//! Indicates whether a character is a space.
bool IsSpace(int character)
@ -90,11 +89,11 @@ bool IsSep(int character)
//! Object's constructor.
CEdit::CEdit()
: CControl(),
m_maxChar( std::numeric_limits<int>::max() ),
m_text(),
m_lineOffset(),
m_lineIndent()
{
m_maxChar = 100;
m_text = std::vector<char>(m_maxChar+1, '\0');
m_len = 0;
m_fontType = Gfx::FONT_COURIER;
@ -558,7 +557,7 @@ bool CEdit::IsLinkPos(Math::Point pos)
{
int i;
if ( m_format.size() == 0 ) return false;
if ( m_format.empty() ) return false;
i = MouseDetect(pos);
if ( i == -1 ) return false;
@ -721,7 +720,7 @@ int CEdit::MouseDetect(Math::Point mouse)
{
len = m_lineOffset[i+1] - m_lineOffset[i];
if ( m_format.size() == 0 )
if ( m_format.empty() )
{
// c = m_engine->GetText()->Detect(m_text.data()+m_lineOffset[i],
// len, offset, m_fontSize,
@ -1018,7 +1017,7 @@ void CEdit::Draw()
o1 = c1; if ( o1 < beg ) o1 = beg;
o2 = c2; if ( o2 > beg+len ) o2 = beg+len;
if ( m_format.size() == 0 )
if ( m_format.empty() )
{
start.x = ppos.x+m_engine->GetText()->GetStringWidth(std::string(m_text.data()+beg).substr(0, o1-beg), m_fontType, size);
end.x = m_engine->GetText()->GetStringWidth(std::string(m_text.data()+o1).substr(0, o2-o1), m_fontType, size);
@ -1052,7 +1051,7 @@ void CEdit::Draw()
eol = 2; // square (eot)
}
if ( !m_bMulti || !m_bDisplaySpec ) eol = 0;
if ( m_format.size() == 0 )
if ( m_format.empty() )
{
m_engine->GetText()->DrawText(std::string(m_text.data()+beg).substr(0, len), m_fontType, size, ppos, m_dim.x, Gfx::TEXT_ALIGN_LEFT, eol);
}
@ -1093,7 +1092,7 @@ void CEdit::Draw()
len = m_cursor1 - m_lineOffset[i];
if ( m_format.size() == 0 )
if ( m_format.empty() )
{
m_engine->GetText()->SizeText(std::string(m_text.data()+m_lineOffset[i]).substr(0, len), m_fontType,
size, pos, Gfx::TEXT_ALIGN_LEFT,
@ -1255,106 +1254,82 @@ void CEdit::DrawColor(Math::Point pos, Math::Point dim, Gfx::Color color)
// Give the text to edit.
void CEdit::SetText(const char *text, bool bNew)
void CEdit::SetText(const std::string& text, bool bNew)
{
int i, j, font;
bool bBOL;
if ( !bNew ) UndoMemorize(OPERUNDO_SPEC);
m_len = strlen(text);
if ( m_len > m_maxChar ) m_len = m_maxChar;
m_len = text.size();
if ( m_format.size() == 0 )
if( m_len >= GetMaxChar() ) m_len = GetMaxChar();
m_text.resize( m_len + 1, '\0' );
m_format.resize( m_len + 1, m_fontType );
font = m_fontType;
j = 0;
bBOL = true;
for ( i=0 ; i<m_len ; i++ )
{
if ( m_bAutoIndent )
{
j = 0;
bBOL = true;
for ( i=0 ; i<m_len ; i++ )
if ( text[i] == '\t' )
{
if ( text[i] == '\t' )
if ( !bBOL )
{
if ( !bBOL ) m_text[j++] = ' ';
continue; // removes tabs
m_text[j] = ' ';
m_format[j] = font;
j ++;
}
bBOL = ( text[i] == '\n' );
m_text[j++] = text[i];
continue; // removes tabs
}
bBOL = ( text[i] == '\n' );
}
if ( text[i] == '\\' && text[i+2] == ';' )
{
if ( text[i+1] == 'n' ) // normal ?
{
font &= ~Gfx::FONT_MASK_FONT;
font |= Gfx::FONT_COLOBOT;
i += 2;
}
else if ( text[i+1] == 'c' ) // cbot ?
{
font &= ~Gfx::FONT_MASK_FONT;
font |= Gfx::FONT_COURIER;
i += 2;
}
else if ( text[i+1] == 'b' ) // big title ?
{
font &= ~Gfx::FONT_MASK_TITLE;
font |= Gfx::FONT_TITLE_BIG;
i += 2;
}
else if ( text[i+1] == 't' ) // title ?
{
font &= ~Gfx::FONT_MASK_TITLE;
font |= Gfx::FONT_TITLE_NORM;
i += 2;
}
else if ( text[i+1] == 's' ) // subtitle ?
{
font &= ~Gfx::FONT_MASK_TITLE;
font |= Gfx::FONT_TITLE_LITTLE;
i += 2;
}
m_len = j;
}
else
{
strncpy(m_text.data(), text, m_len);
m_text[j] = text[i];
m_format[j] = font;
j ++;
font &= ~Gfx::FONT_MASK_TITLE; // reset title
}
}
else
{
font = m_fontType;
j = 0;
bBOL = true;
for ( i=0 ; i<m_len ; i++ )
{
if ( m_bAutoIndent )
{
if ( text[i] == '\t' )
{
if ( !bBOL )
{
m_text[j] = ' ';
m_format[j] = font;
j ++;
}
continue; // removes tabs
}
bBOL = ( text[i] == '\n' );
}
if ( text[i] == '\\' && text[i+2] == ';' )
{
if ( text[i+1] == 'n' ) // normal ?
{
font &= ~Gfx::FONT_MASK_FONT;
font |= Gfx::FONT_COLOBOT;
i += 2;
}
else if ( text[i+1] == 'c' ) // cbot ?
{
font &= ~Gfx::FONT_MASK_FONT;
font |= Gfx::FONT_COURIER;
i += 2;
}
else if ( text[i+1] == 'b' ) // big title ?
{
font &= ~Gfx::FONT_MASK_TITLE;
font |= Gfx::FONT_TITLE_BIG;
i += 2;
}
else if ( text[i+1] == 't' ) // title ?
{
font &= ~Gfx::FONT_MASK_TITLE;
font |= Gfx::FONT_TITLE_NORM;
i += 2;
}
else if ( text[i+1] == 's' ) // subtitle ?
{
font &= ~Gfx::FONT_MASK_TITLE;
font |= Gfx::FONT_TITLE_LITTLE;
i += 2;
}
}
else
{
m_text[j] = text[i];
m_format[j] = font;
j ++;
font &= ~Gfx::FONT_MASK_TITLE; // reset title
}
}
m_len = j;
}
m_len = j;
if ( bNew ) UndoFlush();
@ -1364,23 +1339,21 @@ void CEdit::SetText(const char *text, bool bNew)
ColumnFix();
}
// Returns a pointer to the edited text.
// Returns a const reference to the edited text.
char* CEdit::GetText()
const std::string& CEdit::GetText()
{
m_text[m_len] = 0;
return m_text.data();
return m_text;
}
// Returns the edited text.
void CEdit::GetText(char *buffer, int max)
std::string CEdit::GetText(int max)
{
if ( m_len < max ) max = m_len;
if ( m_len > max ) max = max-1;
strncpy(buffer, m_text.data(), max);
buffer[max] = 0;
return std::string( m_text, 0, max );
}
// Returns the length of the text.
@ -1437,15 +1410,15 @@ void CEdit::FreeImage()
// Read from a text file.
bool CEdit::ReadText(std::string filename, int addSize)
bool CEdit::ReadText(std::string filename)
{
int len, i, j, n, font, iLines, iCount;
int len, len2, i, j, n, font, iLines, iCount;
char iName[50];
float iWidth;
InputSlot slot;
bool bInSoluce, bBOL;
if ( filename == "" ) return false;
if ( filename.empty() ) return false;
CInputStream stream;
stream.open(filename);
@ -1457,26 +1430,22 @@ bool CEdit::ReadText(std::string filename, int addSize)
}
len = stream.size();
len2 = len + 1;
m_maxChar = len+addSize+100;
m_len = len;
m_cursor1 = 0;
m_cursor2 = 0;
FreeImage();
m_text = std::vector<char>(m_maxChar+1, '\0');
m_text = std::string(len2+1, '\0');
std::vector<char> buffer(m_maxChar+1, '\0');
std::vector<char> buffer(len2+1, '\0');
stream.read(buffer.data(), len);
m_format.clear();
m_format.reserve(m_maxChar+1);
for (i = 0; i <= m_maxChar+1; i++)
{
m_format.push_back(m_fontType);
}
m_format.resize(len2+1, m_fontType);
stream.close();
@ -1922,14 +1891,10 @@ void CEdit::SetMaxChar(int max)
m_maxChar = max;
m_text = std::vector<char>(m_maxChar+1, '\0');
m_text.resize( m_maxChar + 1, '\0' );
m_format.clear();
m_format.reserve(m_maxChar+1);
for (int i = 0; i <= m_maxChar+1; i++)
{
m_format.push_back(m_fontType);
}
m_format.resize(m_maxChar + 1, m_fontType);
m_len = 0;
m_cursor1 = 0;
@ -2121,11 +2086,7 @@ void CEdit::SetMultiFont(bool bMulti)
if (bMulti)
{
m_format.reserve(m_maxChar+1);
for (int i = 0; i <= m_maxChar+1; i++)
{
m_format.push_back(m_fontType);
}
m_format.resize( m_text.size() + 1, m_fontType );
}
}
@ -2419,7 +2380,7 @@ void CEdit::MoveLine(int move, bool bWord, bool bSelect)
column -= indentLength*m_lineIndent[line];
}
if ( m_format.size() == 0 )
if ( m_format.empty() )
{
c = m_engine->GetText()->Detect(std::string(m_text.data()+m_lineOffset[line]),
m_fontType, m_fontSize,
@ -2450,7 +2411,7 @@ void CEdit::ColumnFix()
line = GetCursorLine(m_cursor1);
if ( m_format.size() == 0 )
if ( m_format.empty() )
{
m_column = m_engine->GetText()->GetStringWidth(
std::string(m_text.data()+m_lineOffset[line]),
@ -2706,7 +2667,10 @@ void CEdit::InsertOne(char character)
DeleteOne(0); // deletes the selected characters
}
if ( m_len >= m_maxChar ) return;
if ( m_len >= GetMaxChar() ) return;
m_text.resize( m_text.size() + 1, '\0' );
m_format.resize( m_format.size() + 1, m_fontType );
for ( i=m_len ; i>m_cursor1 ; i-- )
{
@ -2938,13 +2902,16 @@ bool CEdit::MinMaj(bool bMaj)
void CEdit::Justif()
{
float width, size, indentLength = 0.0f;
int i, j, line, indent;
int i, j, k, line, indent;
bool bDual, bString, bRem;
m_lineOffset.clear();
m_lineIndent.clear();
indent = 0;
m_lineTotal = 0;
m_lineOffset[m_lineTotal] = 0;
m_lineIndent[m_lineTotal] = indent;
m_lineOffset.push_back( 0 );
m_lineIndent.push_back( indent );
m_lineTotal ++;
if ( m_bAutoIndent )
@ -2954,7 +2921,7 @@ void CEdit::Justif()
}
bString = bRem = false;
i = 0;
i = k = 0;
while ( true )
{
bDual = false;
@ -2965,7 +2932,7 @@ void CEdit::Justif()
width -= indentLength*m_lineIndent[m_lineTotal-1];
}
if ( m_format.size() == 0 )
if ( m_format.empty() )
{
// TODO check if good
@ -3014,26 +2981,27 @@ void CEdit::Justif()
if ( indent < 0 ) indent = 0;
}
m_lineOffset[m_lineTotal] = i;
m_lineIndent[m_lineTotal] = indent;
m_lineOffset.push_back( i );
m_lineIndent.push_back( indent );
m_lineTotal ++;
if ( bDual )
{
m_lineOffset[m_lineTotal] = i;
m_lineIndent[m_lineTotal] = indent;
m_lineOffset.push_back( i );
m_lineIndent.push_back( indent );
m_lineTotal ++;
}
if ( m_lineTotal >= EDITLINEMAX-2 ) break;
if ( k == i ) break;
k = i;
}
if ( m_len > 0 && m_text[m_len-1] == '\n' )
{
m_lineOffset[m_lineTotal] = m_len;
m_lineIndent[m_lineTotal] = 0;
m_lineOffset.push_back( m_len );
m_lineIndent.push_back( 0 );
m_lineTotal ++;
}
m_lineOffset[m_lineTotal] = m_len;
m_lineIndent[m_lineTotal] = 0;
m_lineOffset.push_back( m_len );
m_lineIndent.push_back( 0 );
if ( m_bAutoIndent )
{
@ -3168,7 +3136,7 @@ bool CEdit::UndoRecall()
bool CEdit::ClearFormat()
{
if ( m_format.size() == 0 )
if ( m_format.empty() )
{
SetMultiFont(true);
}

View File

@ -34,21 +34,15 @@ namespace Ui
class CScroll;
//! maximum number of characters in CBOT edit
const int EDITSTUDIOMAX = 20000;
//! maximum total number of lines
const int EDITLINEMAX = 1000;
//! max number of levels preserves
const int EDITHISTORYMAX = 50;
//! max number of successive undo
const int EDITUNDOMAX = 20;
struct EditUndo
{
//! original text
std::vector<char> text;
std::string text;
//! length of the text
int len = 0;
//! offset cursor
@ -124,12 +118,12 @@ public:
bool EventProcess(const Event &event) override;
void Draw() override;
void SetText(const char *text, bool bNew=true);
void GetText(char *buffer, int max);
char* GetText();
int GetTextLength();
void SetText(const std::string& text, bool bNew=true);
std::string GetText(int max);
const std::string& GetText();
int GetTextLength();
bool ReadText(std::string filename, int addSize=0);
bool ReadText(std::string filename);
bool WriteText(std::string filename);
void SetMaxChar(int max);
@ -234,8 +228,8 @@ protected:
protected:
std::unique_ptr<CScroll> m_scroll; // vertical scrollbar on the right
int m_maxChar; // max length of the buffer m_text
std::vector<char> m_text; // text (without zero terminator)
int m_maxChar;
std::string m_text; // text (without zero terminator)
std::vector<Gfx::FontMetaChar> m_format; // format characters
int m_len; // length used in m_text
int m_cursor1; // offset cursor
@ -256,14 +250,14 @@ protected:
int m_lineVisible; // total number of viewable lines
int m_lineFirst; // the first line displayed
int m_lineTotal; // number lines used (in m_lineOffset)
int m_lineOffset[EDITLINEMAX];
char m_lineIndent[EDITLINEMAX];
std::vector<int> m_lineOffset;
std::vector<char> m_lineIndent;
std::vector<ImageLine> m_image;
std::vector<HyperLink> m_link;
std::vector<HyperMarker> m_marker;
int m_historyTotal;
int m_historyCurrent;
HyperHistory m_history[EDITHISTORYMAX];
std::array<HyperHistory, EDITHISTORYMAX> m_history;
float m_time; // absolute time
float m_timeBlink;
float m_timeLastClick;
@ -276,7 +270,7 @@ protected:
bool m_bUndoForce;
OperUndo m_undoOper;
EditUndo m_undo[EDITUNDOMAX];
std::array<EditUndo, EDITUNDOMAX> m_undo;
};

View File

@ -298,13 +298,13 @@ void CEditValue::SetValue(float value, bool bSendMessage)
float CEditValue::GetValue()
{
char text[100];
std::string text;
float value = 0.0f;
if ( m_edit != nullptr )
{
m_edit->GetText(text, 100);
sscanf(text, "%f", &value);
text = m_edit->GetText(100);
sscanf(text.c_str(), "%f", &value);
if ( m_type == EVT_100 )
{

View File

@ -213,9 +213,10 @@ void CMainUserInterface::ChangePhase(Phase phase)
if ( IsMainMenuPhase(m_phase) )
{
if (!m_sound->IsPlayingMusic() && m_sound->IsCachedMusic("music/Intro1.ogg"))
if (!m_sound->IsPlayingMusic())
{
m_sound->PlayMusic("music/Intro1.ogg", false);
m_sound->CacheMusic("music/Intro2.ogg");
}
}

View File

@ -275,7 +275,7 @@ bool CObjectInterface::EventProcess(const Event &event)
}
if( action == EVENT_STUDIO_CLONE )
{
StopEditScript(false);
StopEditScript(true);
Program* newProgram = m_programStorage->CloneProgram(m_programStorage->GetProgram(m_selScript));
m_selScript = m_programStorage->GetProgramIndex(newProgram);
m_main->SaveOneScript(m_object);
@ -738,12 +738,12 @@ void CObjectInterface::StartEditScript(Program* program, std::string name)
// End of editing a program.
void CObjectInterface::StopEditScript(bool bCancel)
void CObjectInterface::StopEditScript(bool closeWithErrors)
{
if ( !m_studio->StopEditScript(bCancel) ) return;
if ( !m_studio->StopEditScript(closeWithErrors) ) return;
m_studio.reset();
if ( !bCancel ) m_programStorage->SetActiveVirus(false);
if ( !closeWithErrors ) m_programStorage->SetActiveVirus(false);
CreateInterface(true); // puts the control buttons
}

Some files were not shown because too many files have changed in this diff Show More