mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
130 Commits
v5.0.0-bet
...
v5.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a8ae298194 | ||
![]() |
dda2effad0 | ||
![]() |
4da0149b6b | ||
![]() |
9602caaccc | ||
![]() |
be2892b759 | ||
![]() |
e1552f649c | ||
![]() |
74bbd45265 | ||
![]() |
5aa32f0eca | ||
![]() |
23987f63c7 | ||
![]() |
d415d3cbee | ||
![]() |
f7f32d6be5 | ||
![]() |
b223175bf9 | ||
![]() |
47463e6ca2 | ||
![]() |
e289059d1f | ||
![]() |
6320d19043 | ||
![]() |
833ae75632 | ||
![]() |
6058a4b1be | ||
![]() |
52173ac07d | ||
![]() |
a55ad60038 | ||
![]() |
0ef235261f | ||
![]() |
3bd9432b5e | ||
![]() |
6fd82f4f30 | ||
![]() |
5572536bff | ||
![]() |
ab197464cc | ||
![]() |
00f2873f48 | ||
![]() |
c4329cac4f | ||
![]() |
4917c7ca2a | ||
![]() |
13c0c8992b | ||
![]() |
06dc0c5323 | ||
![]() |
6039342fcf | ||
![]() |
32c98d4dd4 | ||
![]() |
3a05067ea2 | ||
![]() |
d9bcf3daca | ||
![]() |
29577af049 | ||
![]() |
5b5f213bd5 | ||
![]() |
19a00ce2dc | ||
![]() |
c243200370 | ||
![]() |
09edeaa96b | ||
![]() |
c94964b2db | ||
![]() |
b40f51e03c | ||
![]() |
4319ddcd0f | ||
![]() |
f0163fc1d1 | ||
![]() |
f5be2c73ce | ||
![]() |
a4b0f316f1 | ||
![]() |
efa9ea4ea2 | ||
![]() |
5170ff509c | ||
![]() |
2471021114 | ||
![]() |
3f956289dc | ||
![]() |
f731ae8b21 | ||
![]() |
76dfe55e25 | ||
![]() |
22d46711df | ||
![]() |
ca5198a08d | ||
![]() |
037551ec77 | ||
![]() |
3c309c200f | ||
![]() |
6231cbc5f1 | ||
![]() |
9e98605030 | ||
![]() |
6834efbb9a | ||
![]() |
c74fbc6c43 | ||
![]() |
949f370118 | ||
![]() |
91359e4674 | ||
![]() |
f790fff439 | ||
![]() |
e13384dbb7 | ||
![]() |
0c0539bb2a | ||
![]() |
caf089489b | ||
![]() |
18c0f05bb3 | ||
![]() |
a9a0faaaaa | ||
![]() |
df166e4d5d | ||
![]() |
f661b41d74 | ||
![]() |
00e887e5f9 | ||
![]() |
e6fc7aac9d | ||
![]() |
65346edc4a | ||
![]() |
3620e0aef6 | ||
![]() |
0211ab40e4 | ||
![]() |
ae945fc0bc | ||
![]() |
4537dbf723 | ||
![]() |
94358cd265 | ||
![]() |
337a36ee90 | ||
![]() |
07fd7f77bd | ||
![]() |
69a774bec1 | ||
![]() |
dc9200feb5 | ||
![]() |
e7d1f99470 | ||
![]() |
76165908fc | ||
![]() |
b19a27e6cf | ||
![]() |
d78054b4d4 | ||
![]() |
40a68b6a40 | ||
![]() |
15069a4363 | ||
![]() |
3ee2dd9218 | ||
![]() |
972967307a | ||
![]() |
73e2d2a109 | ||
![]() |
cd46581e64 | ||
![]() |
f670689a40 | ||
![]() |
e043ed8cca | ||
![]() |
7dc2565dde | ||
![]() |
14cc48fa98 | ||
![]() |
9d46a73978 | ||
![]() |
228468759e | ||
![]() |
d56fa6c95c | ||
![]() |
b7189a7994 | ||
![]() |
1e48ffb84b | ||
![]() |
12c5df67fa | ||
![]() |
ac0b85e101 | ||
![]() |
320f5841dd | ||
![]() |
2313ea5d3b | ||
![]() |
9536bda674 | ||
![]() |
d2be0e815e | ||
![]() |
8b83cf3518 | ||
![]() |
064704a457 | ||
![]() |
829ce23dc2 | ||
![]() |
04c37b6186 | ||
![]() |
4fd7698907 | ||
![]() |
6016790f03 | ||
![]() |
1e6227e549 | ||
![]() |
6ade0c7125 | ||
![]() |
807f3bb3b2 | ||
![]() |
a83fe5af2f | ||
![]() |
0cd64ca4d1 | ||
![]() |
541953256a | ||
![]() |
27d0569c75 | ||
![]() |
689583fe75 | ||
![]() |
d1d9eaca3a | ||
![]() |
d1aa4beb98 | ||
![]() |
94b3148112 | ||
![]() |
1fb211c9ba | ||
![]() |
1b140d7b23 | ||
![]() |
3fbe3a2abb | ||
![]() |
d9313f803d | ||
![]() |
33487fb0f6 | ||
![]() |
fbde5edfae | ||
![]() |
4d908578c1 | ||
![]() |
aa6bd993e6 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,8 +1,5 @@
|
||||
/Core/GDCore/Tools/VersionPriv.h
|
||||
/docs/GDJS Runtime Documentation
|
||||
/docs/GDJS Documentation
|
||||
/docs/GDCpp Documentation
|
||||
/docs/GDCore Documentation
|
||||
/docs
|
||||
/ExtLibs/SFML
|
||||
/ExtLibs/*.7z
|
||||
/scripts/logs/*.txt
|
||||
|
@@ -12,16 +12,17 @@ cache:
|
||||
env:
|
||||
global:
|
||||
- GCC_VERSION="4.8"
|
||||
|
||||
|
||||
services:
|
||||
# Virtual Framebuffer 'fake' X server for SFML
|
||||
- xvfb
|
||||
|
||||
|
||||
addons:
|
||||
artifacts:
|
||||
s3_region: "us-east-1"
|
||||
target_paths:
|
||||
- /$(git rev-parse HEAD)
|
||||
- /$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)/commit/$(git rev-parse HEAD)
|
||||
- /$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)/latest
|
||||
paths:
|
||||
- Binaries/Output/libGD.js/Release
|
||||
apt:
|
||||
|
@@ -1,592 +1,25 @@
|
||||
/**
|
||||
* \mainpage GDevelop Core
|
||||
* \image html gdlogo.png
|
||||
* \section welcome Welcome
|
||||
* \section welcome GDevelop Core documentation
|
||||
*
|
||||
* The **GDevelop Core** library contains the main concepts, classes and tools that are used by the *platforms* and the *GDevelop IDE*.<br>
|
||||
* This ensures that the IDE, or any tool based on GDevelop Core, is able to work with projects based on any arbitrary platform.
|
||||
*
|
||||
* Two official platforms are available for GDevelop:
|
||||
* - The *C++ Platform* (GDCpp) to create native games.
|
||||
* - The *JS Platform* (GDJS) to create HTML5 games.
|
||||
* The **GDevelop Core** library contains the structure of a GDevelop game, classes and tools that are used by the *platforms* and the *GDevelop IDE*.
|
||||
*
|
||||
* \section gettingstarted Getting started
|
||||
* First, please refer to these pages to install the required tools and to get help about setting up a basic extension:<br>
|
||||
*
|
||||
* -# \subpage setupDevEnv
|
||||
* -# \ref overview
|
||||
* -# \ref writeANewExtension
|
||||
* In most cases, you should start by <a href="https://github.com/4ian/GDevelop/blob/master/newIDE/README.md">installing and launching the development version of GDevelop</a>.
|
||||
*
|
||||
* You can also read \subpage recommendedToolsAndConventions.
|
||||
* - If you're interested in writing extensions for GDevelop, read <a href="https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md">the documentation about extensions</a>.
|
||||
* - If you want to dig more into how GDevelop is architectured and work on the core, read <a href="https://github.com/4ian/GDevelop/blob/master/Core/GDevelop-Architecture-Overview.md">GDevelop Architecture Overview</a>. Then, you can browse this reference to get more information about a class or function.
|
||||
*
|
||||
* \section aboutdoc About this documentation
|
||||
* \section other Other documentations
|
||||
*
|
||||
* If you never used GDevelop Core before, take a look at \ref overview.
|
||||
*
|
||||
* As everything that is developed around GDevelop is based on this library, you should take a look at it quite often: platforms, extensions
|
||||
* and the IDE are intensively using the classes and tools offered by GDCore.
|
||||
* When developing an extension for the C++ or JS platform, read these documentations:
|
||||
* GDevelop is architectured around a `Core` (this library), platforms (`GDJS`, `GDCpp`) and extensions (`Extensions` folder). The editor (`newIDE` folder) is using all of these libraries.
|
||||
*
|
||||
* - [Open GDevelop C++ Platform documentation](../GDCpp Documentation/index.html)
|
||||
* - [Open GDevelop JS Platform documentation](../GDJS Documentation/index.html)
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page setupDevEnv Setting up the development environment
|
||||
*
|
||||
* If you didn't already downloaded GDevelop, get and extract the source from [GitHub](https://github.com/4ian/GD).
|
||||
*
|
||||
* Follow these steps to be able to compile GDevelop, it's super easy:
|
||||
*
|
||||
* <b>Windows</b>
|
||||
*
|
||||
* -# \subpage installWinCompiler
|
||||
* -# \ref installAndUseCMake
|
||||
* <br>
|
||||
*
|
||||
* <b>GNU/Linux</b>
|
||||
* -# \subpage installLinuxLib
|
||||
* -# \subpage installAndUseCMake
|
||||
*
|
||||
* <b>OS X</b>
|
||||
* -# \subpage installMacOSTools
|
||||
* -# \ref installAndUseCMake
|
||||
*
|
||||
* See the recommended tools and conventions for working on GDevelop on this page:
|
||||
* \subpage recommendedToolsAndConventions
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page installWinCompiler (Windows) Install TDM-GCC compiler
|
||||
*
|
||||
* GDevelop is compiled with TDM-GCC under Windows.<br>
|
||||
* So as to prevent incompatibilities between the compiler (and the standard C++ library provided with) used by GDevelop and
|
||||
* the compiler used by the extensions, GDevelop require the extensions and the platforms to use the same version of TDM-GCC.
|
||||
*
|
||||
* While a recent GCC version should work, if you compile GDevelop for an "official" distribution it's better
|
||||
* to use the specific version provided here.
|
||||
*
|
||||
* \section installWinCompiler_download Download
|
||||
*
|
||||
* Download the current version of the compiler used by GDevelop on Windows here:
|
||||
*
|
||||
* https://sourceforge.net/projects/tdm-gcc/files/TDM-GCC%20Installer/Previous/1.1309.0/tdm-gcc-4.9.2.exe/download
|
||||
*
|
||||
* \section installWinCompiler_install Installation
|
||||
*
|
||||
* The installation is fairly simple :<br>
|
||||
* <br>
|
||||
* - Launch the installer.<br>
|
||||
* - Choose Create.<br>
|
||||
|
||||
\image html compilerInstall1.png
|
||||
|
||||
* - Choose an installation directory.<br>
|
||||
|
||||
\image html compilerInstall2.png
|
||||
|
||||
* - Choose the components to be installed. You don't have to change anything, the default options are good enough.<br>
|
||||
|
||||
\image html compilerInstall3.png
|
||||
|
||||
* - Click on install so as to launch the installation process.<br>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page installAndUseCMake (All) Install CMake & launch the build
|
||||
*
|
||||
* Building is done using CMake: it is an open-source build system that can generate build files for lots of IDE and build tools (Makefiles...).
|
||||
*
|
||||
* \section installAndUseCMake_download Download and install CMake
|
||||
*
|
||||
* First, install CMake:
|
||||
* Download it [here](http://www.cmake.org/cmake/resources/software.html) for Windows, get it using your package manager if you're
|
||||
* using a Linux distribution or using Homebrew for Mac OS X.
|
||||
*
|
||||
* \section installAndUseCMake_use Using CMake to generate the build files
|
||||
* Using CMake is not difficult and require only a few clicks/commands to enter. Windows users may use
|
||||
* the GUI as shown in the next section. Linux and Mac OS X users may prefer to use the command line as shown
|
||||
* as the end of this page.
|
||||
*
|
||||
* \subsection installAndUseCMake_use_gui Using the GUI
|
||||
*
|
||||
* - Start the CMake user interface (_cmake-gui_). Choose the GD root directory as the source directory, and Binaries/build as the directory where to build the binaries:
|
||||
|
||||
\image html usecmake1.png
|
||||
|
||||
* - Click on *Configure*. If asked to create the build directory, answer yes. Choose then your favorite generator: *MinGW Makefiles* (on Windows) or *Unix Makefiles* (on Linux/OS X) generate a traditional Makefile that can be built using the
|
||||
* *mingw32-make* (on Windows) or *make* (on Linux/OS X) command. You can also choose the *Ninja* generator to use the [Ninja build system](http://martine.github.io/ninja/).
|
||||
|
||||
\image html usecmake2.png
|
||||
|
||||
* - When you click on Finish, CMake do a first configuration. If **errors occurred*, make sure that you have download all required development libraries.
|
||||
* - Adjust any variable if necessary (no changes is needed by default), then click on Generate.
|
||||
|
||||
\image html usecmake3.png
|
||||
|
||||
* - You can then launch a terminal/command prompt, go to the build folder (`cd path/to/GD/Binaries/build`) and launch the build
|
||||
* using the generator you've choosen: `mingw32-make`, or `make` on Linux/OS X.
|
||||
*
|
||||
* \subsection installAndUseCMake_use_cmd Using the command line
|
||||
*
|
||||
* Using the commandline with CMake is also easy:
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~
|
||||
* cd /path/to/GD/Binaries
|
||||
* mkdir build
|
||||
* cd build
|
||||
* cmake ../..
|
||||
* make
|
||||
* ~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* For Windows, replace `cmake ../..` by `cmake ../.. -G "MinGW Makefiles"` and `make` by `mingw32-make`.
|
||||
*
|
||||
* or using the fast [Ninja build system](http://martine.github.io/ninja/) :
|
||||
* ~~~~~~~~~~~~~~~~~~~~~
|
||||
* cd /path/to/GD/Binaries
|
||||
* mkdir build
|
||||
* cd build
|
||||
* cmake ../.. -G "Ninja"
|
||||
* ninja
|
||||
* ~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* \section installAndUseCMake_launch Launch GDevelop
|
||||
*
|
||||
* Binaries are created into *Binaries/Output/Release_{OS}* folder.
|
||||
*
|
||||
* To launch GDevelop in Windows, double click on **GDIDE**. For Linux, launch **StartGDevelop.sh**.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page installLinuxLib (Linux) Install development files
|
||||
*
|
||||
* \section installLibs Install development libraries
|
||||
*
|
||||
* GDevelop is compiled with gcc under Linux.<br>
|
||||
* You need to have some packages to be installed before starting to build GD. These packages can vary according to the distribution you use.
|
||||
* On Ubuntu, you may want to install these packages:
|
||||
\code
|
||||
sudo apt-get install libopenal-dev
|
||||
sudo apt-get install libjpeg-dev
|
||||
sudo apt-get install libglew-dev
|
||||
sudo apt-get install libudev-dev
|
||||
sudo apt-get install libxrandr-dev
|
||||
sudo apt-get install libsndfile1-dev
|
||||
sudo apt-get install libglu1-mesa-dev
|
||||
sudo apt-get install libfreetype6-dev
|
||||
\endcode
|
||||
* Make sure you also have some basic tools installed:
|
||||
\code
|
||||
sudo apt-get install build-essential
|
||||
sudo apt-get install p7zip-full
|
||||
sudo apt-get install curl
|
||||
\endcode
|
||||
*
|
||||
* If you want to package the app, you can also install:
|
||||
\code
|
||||
sudo apt-get install devscripts
|
||||
\endcode
|
||||
*
|
||||
*
|
||||
* \subsection installcmake Install CMake
|
||||
* You'll need CMake to build GDevelop: See more on \subpage installAndUseCMake.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page installMacOSTools (Mac OS X) Install development tools
|
||||
*
|
||||
* Make sure that you have Apple Developer Tools installed (if you have Xcode and git, that should be the case).
|
||||
*
|
||||
* \section installTools Install development tools
|
||||
*
|
||||
* The simplest way of installing dependencies required by GDevelop is to use [Homebrew](http://brew.sh/). Install it
|
||||
* and install these packages, using the terminal:
|
||||
\code
|
||||
brew install cmake
|
||||
brew install p7zip
|
||||
brew install pkgconfig
|
||||
brew install freetype
|
||||
\endcode
|
||||
* If you want to generate the documentation and translations, install Doxygen and Gettext:
|
||||
\code
|
||||
brew install doxygen
|
||||
brew install gettext
|
||||
\endcode
|
||||
*
|
||||
* \section launchCompilation Launch compilation
|
||||
*
|
||||
* You should be able to compile GD using CMake. Go with a terminal to the GD source folder:
|
||||
\code
|
||||
cd /path/to/GD
|
||||
mkdir build && cd build
|
||||
cmake ../..
|
||||
make -j4
|
||||
\endcode
|
||||
*
|
||||
* More information about compilation here: \ref installAndUseCMake
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \page recommendedToolsAndConventions Recommended tools and advices to work with GD
|
||||
*
|
||||
* \section git Git and GitHub
|
||||
*
|
||||
* Git is an amazing *version control system*. If you never used it before, take a look at some tutorials, there
|
||||
* are plenty of them on the internet.<br>
|
||||
* Windows users could be interested in using [TortoiseGit](code.google.com/p/tortoisegit) or the official
|
||||
* [GitHub client](https://windows.github.com/).
|
||||
*
|
||||
* \subsection pullrequest Submitting code thanks to Pull Request.
|
||||
*
|
||||
* Using *Pull request*, you can easily submit your changes so that they are integrated into the official
|
||||
* GDevelop repository (http://github.com/4ian/gd).
|
||||
*
|
||||
* See this article on *GitHub help* about pull requests: https://help.github.com/articles/using-pull-requests.<br>
|
||||
* Pull requests are extremely easy to use and the best way to contribute to GD!
|
||||
*
|
||||
* ------
|
||||
*
|
||||
* \section codingstyle Coding style
|
||||
*
|
||||
* As a rule of thumb, try to retain the original coding style used in a file when editing it, or look at other
|
||||
* files when creating a new extension/dialog/feature/class.
|
||||
*
|
||||
* For both C++ and Javascript, *code indentation* should be 4 spaces (or tab set to a width of 4 spaces).<br>
|
||||
* Lines should be cutted when reaching column 110 so that two files can be displayed side-by-side on a same screen.
|
||||
* When cutting a line, indent the new lines with an additional 4 spaces.
|
||||
*
|
||||
* \subsection cpp C++
|
||||
*
|
||||
* *Naming* conventions:
|
||||
* - Classes should be *CamelCase* (starting with a capital).
|
||||
* - All variables (including member variables) should be *camelCase* (no capital for the first letter).
|
||||
* - All functions (including class methods) should be *CamelCase* (starting with a capital).
|
||||
*
|
||||
* *Comments*:
|
||||
* - Comment your classes and functions using *Doxygen* comments.
|
||||
*
|
||||
* \subsection js Javascript
|
||||
*
|
||||
* *Naming* conventions:
|
||||
* - "Classes" should be *CamelCase* (starting with a capital).
|
||||
* - All variables (including member variables) should be *camelCase* (no capital for the first letter).
|
||||
* - All functions (including class methods) should be *camelCase* (no capital for the first letter).
|
||||
*
|
||||
* *Comments*:
|
||||
* - Comment your classes and functions using *yuidoc* comments.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page overview Overview of GDCore
|
||||
*
|
||||
* \section platformstructure Structure of a platform
|
||||
*
|
||||
* A platform for GDevelop Core is a class inheriting from gd::Platform.<br>
|
||||
* They contains the extensions of the platform (see below) and offer various methods, like gd::Platform::GetProjectExporters which
|
||||
* is called by the IDE to export a gd::Project to a stand-alone game.
|
||||
*
|
||||
* \subsection platformloading Platforms loading
|
||||
* A platform is stored in memory and managed by gd::PlatformManager. It loaded from a dynamic library file (.dll on windows, .so on Linux)
|
||||
* thanks to gd::PlatformLoader.<br>
|
||||
* It is responsibility of the IDE, or any other application using GDCore,
|
||||
* to call the appropriate method of gd::PlatformLoader to trigger the loading of the platforms when needed.
|
||||
*
|
||||
* gd::PlatformLoader search for two symbols in the dynamic library file: *CreateGDPlatform* and
|
||||
* *DestroyGDPlatform*. These symbols must exists and must create (or destroy) the platform class. For example:
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~
|
||||
* extern "C" gd::Platform * GD_API CreateGDPlatform() {
|
||||
* return &JsPlatform::Get(); //Return the singleton object representing the JS Platform
|
||||
* }
|
||||
*
|
||||
* extern "C" void GD_API DestroyGDPlatform() {
|
||||
* JsPlatform::DestroySingleton(); //Destroy the singleton.
|
||||
* }
|
||||
* ~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* The platform dynamic library file is often located inside <i>GDevelop directory</i>/xxxPlatform (*xxx* being replaced by the platform acronym).
|
||||
*
|
||||
* In this folder, the platform can store basically anything it needs. For example, both GDJS and GDCpp are storing a folder called *Runtime* containing
|
||||
* the game engine.
|
||||
* If there is a sub directory called *Extensions*, the gd::PlatformLoader tries to load the extensions contained inside (see below).
|
||||
*
|
||||
* \section extensionsstructure Structure of an extension
|
||||
*
|
||||
* **Extensions** are seen by GDevelop Core as classes inheriting from gd::PlatformExtension.<br>
|
||||
* They are stored inside the platform they belong to, and they are also loaded from a dynamic library file thanks to gd::ExtensionsLoader. The main
|
||||
* job of an extension is to <b>declare</b> everything it provides: objects, actions and conditions, behaviors, expressions.<br>
|
||||
* This is done directly using the standard method provided by gd::PlatformExtension, notably:
|
||||
* - gd::PlatformExtension::AddCondition and gd::PlatformExtension::AddAction,
|
||||
* - gd::PlatformExtension::AddExpression (and gd::PlatformExtension::AddStrExpression),
|
||||
* - gd::PlatformExtension::AddObject and gd::PlatformExtension::AddBehavior
|
||||
*
|
||||
*
|
||||
* Some platforms (like the C++ Platform) offer another base class which must be used instead of gd::PlatformExtension when declaring a platorm: as this base class
|
||||
* inherits from gd::PlatformExtension, standard methods still work, but you may be able to declare some others features (the C++ Platform offers
|
||||
* the possibility of declaring debugger related functions).
|
||||
*
|
||||
* \subsection extensionloading Extensions loading
|
||||
*
|
||||
* A single dynamic library file can contains an extension for more than one platform:<br>
|
||||
* You just have to declare a class deriving from gd::PlatformExtension for each platform supported, and a creation function for each platform
|
||||
* (the C++ platform expects a function called *CreateGDExtension* while JS Platform search for a function called *CreateGDJSExtension*).
|
||||
*
|
||||
* \subsection extensionexample Edit or write a new extension
|
||||
*
|
||||
* Refer to these pages for more information about extensions:
|
||||
* - \subpage AboutExtensionCpp
|
||||
* - \subpage writeANewExtension
|
||||
*
|
||||
* \section utf8section UTF8 strings
|
||||
*
|
||||
* Most parts of the codebase support UTF8 strings thanks to gd::String class. gd::String is a wrapper around std::string, exposing a similar
|
||||
* interface as well as a few tool member functions and operators that are all UTF8 aware.
|
||||
*
|
||||
* Its usage is easy, especially if you're familiar with std::string. Some extra functions can be really useful, in particular
|
||||
* the ones to convert the string from/to a number.
|
||||
*
|
||||
\code
|
||||
gd::String str = "Hello";
|
||||
str += " world";
|
||||
str += " " + gd::String::From(2);
|
||||
//str now contains "Hello world 2";
|
||||
|
||||
gd::String twopointfiveStr = "2.5";
|
||||
double twopointfive = twopointfive.To<double>();
|
||||
//twopointfive == 2.5
|
||||
\endcode
|
||||
*
|
||||
*
|
||||
* For more information, see the complete reference of the class. Tests cases have also been made for most functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page writeANewExtension Write a new extension
|
||||
*
|
||||
* \section writeANewExtension_createNewExtension Create a new extension
|
||||
*
|
||||
* Creation of a new extension can be made by following these steps:<br>
|
||||
*
|
||||
* - Copy the directory of an extension and rename it:
|
||||
* \image html createnew1.png
|
||||
* - Rename then the sources files :
|
||||
* \image html createnew2.png
|
||||
* - Open the *CMakeLists.txt* file and replace every occurrence of the extension old name with the new name.<br>
|
||||
* - Open all the source files and again, replace every occurrence of the extension old name with the new name.<br>
|
||||
* - In the *Extensions* directory, open the *CMakeLists.txt* file and add a line such as <code>ADD_SUBDIRECTORY(MyExtension)</code>.
|
||||
* You can then start to modify the extension.<br>
|
||||
* If your extension is fairly simple, you can create it from the AES Extension. <br>
|
||||
* If your extension need an object, you can use for instance the TextObject Extension as a starting point.<br>
|
||||
* <br>
|
||||
* - You can compile your extension by relaunching CMake like described [here](\ref installAndUseCMake). After doing that, just compile as usual.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page AboutExtensionCpp About Extension.cpp
|
||||
*
|
||||
* An extension has to define (usually in a file called *Extension.cpp* for the C++ Platform or *JsExtension.cpp* for the JS Platform)
|
||||
* a class that is derived from the gd::PlatformExtension class. This class contains, in its constructor, the declarations
|
||||
* of everything that is provided by the extension.
|
||||
|
||||
* \section extensionDeclaration Declare the extension information
|
||||
|
||||
* The declarations are made using the methods provided by gd::PlatformExtension.
|
||||
*
|
||||
* The first declaration if often the information about the extension:
|
||||
|
||||
* \code
|
||||
Extension()
|
||||
{
|
||||
SetExtensionInformation("TextObject",
|
||||
_("Text object"),
|
||||
_("Extension allowing to use an object displaying a text."),
|
||||
"Florian Rival",
|
||||
"Open Source (MIT License)");
|
||||
* \endcode
|
||||
|
||||
The first parameter is the name of the extension. Choose carefully the name of the extension, as projects are directly referring to it.
|
||||
|
||||
* \section instructionsDeclaration Declare actions, conditions and expressions
|
||||
|
||||
Actions are declared like this :
|
||||
|
||||
* \code
|
||||
AddAction("ActionName",
|
||||
_("Name displayed to users"),
|
||||
_("Description"),
|
||||
_("Sentence displayed in event editor"),
|
||||
_("Category"),
|
||||
"path-to-an-24-by-24-icon-file.png",
|
||||
"path-to-an-16-by-16-icon-file.png")
|
||||
.AddParameter("theTypeOfTheParameter", _("Parameter1"))
|
||||
.AddParameter("theTypeOfTheParameter", _("Parameter2"))
|
||||
.SetFunctionName("MyFunctionName").SetIncludeFile("MyExtension/MyIncludeFile.h");
|
||||
|
||||
* \endcode
|
||||
* Declare conditions and expressions in a similar way.<br>
|
||||
* Parameters are added using gd::InstructionMetadata::AddParameter.
|
||||
*
|
||||
* The last line set the function name that will be called when generating the code of an event using the action:<br>
|
||||
* You can either do it after declaring the function, or later using this syntax:
|
||||
*
|
||||
* \code
|
||||
GetAllActions()["ExtensionName::ActionName"].SetFunctionName("MyFunctionName");
|
||||
* \endcode
|
||||
*
|
||||
* Both methods are ok, but the latest allows to use the same code to declare an extension for the C++ and JS platform,
|
||||
* then customize the names of the functions to call.
|
||||
*
|
||||
* \section objectsDeclaration Declare objects
|
||||
*
|
||||
* Adding an object is made using gd::PlatformExtension::AddObject method.
|
||||
*
|
||||
* \code
|
||||
gd::ObjectMetadata & obj = AddObject<MyObject>(
|
||||
"Name",
|
||||
_("Name displayed to users"),
|
||||
_("Description"),
|
||||
"path-to-a-32-by-32-icon.png");
|
||||
* \endcode
|
||||
*
|
||||
* The *C++ platform* also requires that you call *AddRuntimeObject* to declare the RuntimeObject class associated to the object being declared:<br>
|
||||
* It has two template parameters: the first one is the corresponding object class declared with *AddObject* (class inheriting from *gd::Object*) and the
|
||||
* second one is the *RuntimeObject* class.
|
||||
* You must pass as parameter the metadata from the object previously declared and the name of the class inheriting from RuntimeObject.
|
||||
*
|
||||
* You will also want to specify the .h file associated to the object using gd::ObjectMetadata::SetIncludeFile. For example:
|
||||
* \code
|
||||
//obj is the gd::ObjectMetadata returned when you called AddObject.
|
||||
AddRuntimeObject<TextObject, RuntimeTextObject>(obj, "RuntimeTextObject");
|
||||
obj.SetIncludeFile("TextObject/TextObject.h");
|
||||
* \endcode
|
||||
*
|
||||
* You can then declare the actions, conditions, and expressions related to the objects, using the AddAction/AddCondition/AddExpression methods provided
|
||||
* by <i>obj</i>.
|
||||
|
||||
* \section eventsDeclaration Declaring events
|
||||
*
|
||||
* Events are declared like this :
|
||||
* \code
|
||||
AddEvent("Name",
|
||||
_("Name displayed to users"),
|
||||
"Description",
|
||||
"Group",
|
||||
"path-to-a-16-by-16-icon.png",
|
||||
std::make_shared<EventClassName>())
|
||||
* \endcode
|
||||
*
|
||||
* The event must be able to generate its code when events are being translated to C++ or Javascript:<br>
|
||||
* This is done by calling SetCodeGenerator. For example:
|
||||
*
|
||||
* \code
|
||||
AddEvent("Standard",
|
||||
_("Standard event"),
|
||||
_("Standard event: Actions are run if conditions are fulfilled."),
|
||||
"",
|
||||
"res/eventaddicon.png",
|
||||
std::make_shared<gd::StandardEvent>())
|
||||
.SetCodeGenerator(std::shared_ptr<gd::EventMetadata::CodeGenerator>(codeGen));
|
||||
* \endcode
|
||||
|
||||
* \section behaviorsDeclaration Declaring the behaviors
|
||||
|
||||
Behaviors are declared like objects:
|
||||
|
||||
|
||||
* \code
|
||||
gd::BehaviorMetadata & aut = AddBehavior("Name",
|
||||
_("Name displayed to users"),
|
||||
_("DefaultNameUsedInEditor"),
|
||||
_("Description."),
|
||||
"Group",
|
||||
"path-to-a-32-by-32-icon.png",
|
||||
"BehaviorClassName",
|
||||
std::make_shared<BehaviorClassName>(),
|
||||
std::make_shared<BehaviorSharedDataClassName>());
|
||||
* \endcode
|
||||
* The last line can be replaced by <code>std::shared_ptr<gd::BehaviorsSharedData>()</code> if no shared data are being used.
|
||||
*
|
||||
* You can then declare the actions, conditions, and expressions related to the behavior like objects:<br>
|
||||
* Call AddAction/AddCondition/AddExpression on the <i>aut</i> object.
|
||||
|
||||
* \section excludingNonRuntimeDeclaration (C++ platform) Excluding elements declaration from runtime
|
||||
* When your extension is compiled for the C++ platform Runtime, GDevelop does not known anything about action/condition or even events classes.<br>
|
||||
* You have then to exclude all actions/conditions/expressions/events declaration from extension at runtime (only Extension/Object/Behaviors declarations have to be kept).
|
||||
|
||||
* Use the *<code>GD_IDE_ONLY</code> define* to achieve this goal, as demonstrated in this skeleton of a complete extension declaration:
|
||||
* \code
|
||||
class Extension : public ExtensionBase //For C++ platform, extensions must derive from ExtensionBase
|
||||
{
|
||||
public:
|
||||
Extension()
|
||||
{
|
||||
SetExtensionInformation("MyExtension",
|
||||
_("Extension name"),
|
||||
_("Extension declaration"),
|
||||
"Author",
|
||||
"license");
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
AddAction(...);
|
||||
AddCondition(...);
|
||||
AddExpression(...);
|
||||
#endif
|
||||
|
||||
{
|
||||
gd::ObjectMetadata & obj = AddObject("ObjectName",
|
||||
_("Object name"),
|
||||
_("Description"),
|
||||
"CppPlatform/Extensions/myicon.png",
|
||||
&CreateMyObject,
|
||||
&DestroyMyObject);
|
||||
|
||||
AddRuntimeObject(obj, "RuntimeObjectName", CreateRuntimeObjectName);
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
obj.SetIncludeFile("MyExtension/MyIncludeFile.h");
|
||||
|
||||
obj.AddAction(...);
|
||||
obj.AddCondition(...);
|
||||
obj.AddExpression(...);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
gd::BehaviorMetadata & aut = AddBehavior("BehaviorName",
|
||||
_("Behavior name"),
|
||||
"defaultGDname",
|
||||
_("Description"),
|
||||
"",
|
||||
"CppPlatform/Extensions/myicon.png",
|
||||
"PhysicsBehavior",
|
||||
std::make_shared<BehaviorClassName>(),
|
||||
std::make_shared<BehaviorSharedDataClassName>());
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
behaviorInfo.SetIncludeFile("MyExtension/MyIncludeFile.h");
|
||||
|
||||
aut.AddAction(...);
|
||||
aut.AddCondition(...);
|
||||
aut.AddExpression(...);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
|
||||
};
|
||||
virtual ~Extension() {};
|
||||
};
|
||||
|
||||
// Used by GDevelop to create the extension class
|
||||
// -- Do not need to be modified. --
|
||||
extern "C" ExtensionBase * GD_EXTENSION_API CreateGDExtension() {
|
||||
return new Extension;
|
||||
}
|
||||
* \endcode
|
||||
* - <a href="https://github.com/4ian/GDevelop/blob/master/newIDE/README.md">Getting started with the editor</a>
|
||||
* - <a href="https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md">Getting started with the extensions</a>
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@@ -23,16 +23,17 @@ namespace gd {
|
||||
|
||||
const EventsList* LinkEvent::GetLinkedEvents(const gd::Project& project) const {
|
||||
const EventsList* events = nullptr;
|
||||
const gd::ExternalEvents* linkedExternalEvents = nullptr;
|
||||
if (project.HasExternalEventsNamed(GetTarget())) {
|
||||
linkedExternalEvents = &project.GetExternalEvents(GetTarget());
|
||||
events = &project.GetExternalEvents(GetTarget()).GetEvents();
|
||||
} else if (project.HasLayoutNamed(GetTarget()))
|
||||
events = &project.GetLayout(GetTarget()).GetEvents();
|
||||
const gd::ExternalEvents& linkedExternalEvents = project.GetExternalEvents(GetTarget());
|
||||
events = &linkedExternalEvents.GetEvents();
|
||||
} else if (project.HasLayoutNamed(GetTarget())) {
|
||||
const gd::Layout& linkedLayout = project.GetLayout(GetTarget());
|
||||
events = &linkedLayout.GetEvents();
|
||||
}
|
||||
|
||||
// If the link only includes an events group, search it inside the
|
||||
// layout/external events
|
||||
if (includeConfig == INCLUDE_EVENTS_GROUP) {
|
||||
if (events != nullptr && includeConfig == INCLUDE_EVENTS_GROUP) {
|
||||
std::size_t i = 0;
|
||||
std::size_t eventsCount = events->GetEventsCount();
|
||||
for (; i < eventsCount; ++i) {
|
||||
|
@@ -1115,10 +1115,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("expression", _("Ray maximum distance (in pixels)"))
|
||||
.AddParameter(
|
||||
"scenevar",
|
||||
_("Variable where to store the X position of the intersection"))
|
||||
_("Scene variable where to store the X position of the intersection"))
|
||||
.AddParameter(
|
||||
"scenevar",
|
||||
_("Variable where to store the Y position of the intersection"))
|
||||
_("Scene variable where to store the Y position of the intersection"))
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
@@ -1143,10 +1143,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.AddParameter("expression", _("Ray target Y position"))
|
||||
.AddParameter(
|
||||
"scenevar",
|
||||
_("Variable where to store the X position of the intersection"))
|
||||
_("Scene variable where to store the X position of the intersection"))
|
||||
.AddParameter(
|
||||
"scenevar",
|
||||
_("Variable where to store the Y position of the intersection"))
|
||||
_("Scene variable where to store the Y position of the intersection"))
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
|
@@ -98,7 +98,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
.AddAction(
|
||||
"LireFichierExp",
|
||||
_("Read a value"),
|
||||
_("Read the value saved in the specified element and store it in a "
|
||||
_("Read the value saved in the specified element and store it in a scene"
|
||||
"variable.\nSpecify the structure leading to the element using / "
|
||||
"(example : Root/Level/Current)\nSpaces are forbidden in element "
|
||||
"names."),
|
||||
@@ -115,7 +115,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
.AddAction(
|
||||
"LireFichierTxt",
|
||||
_("Read a text"),
|
||||
_("Read the text saved in the specified element and store it in a "
|
||||
_("Read the text saved in the specified element and store it in a scene"
|
||||
"variable.\nSpecify the structure leading to the element using / "
|
||||
"(example : Root/Level/Current)\nSpaces are forbidden in element "
|
||||
"names."),
|
||||
|
@@ -322,7 +322,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
extension
|
||||
.AddExpression("trunc",
|
||||
_("Truncation"),
|
||||
_("Troncate a number"),
|
||||
_("Truncate a number"),
|
||||
_("Mathematical tools"),
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
@@ -48,7 +48,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
"",
|
||||
true)
|
||||
.AddParameter(
|
||||
"scenevar", _("Store the response in this variable"), "", true)
|
||||
"scenevar", _("Store the response in this scene variable"), "", true)
|
||||
.MarkAsComplex();
|
||||
|
||||
extension
|
||||
@@ -68,8 +68,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"JSONToVariableStructure",
|
||||
_("Convert JSON to a variable"),
|
||||
_("Parse a JSON object and store it into a variable"),
|
||||
_("Convert JSON to a scene variable"),
|
||||
_("Parse a JSON object and store it into a scene variable"),
|
||||
_("Parse JSON string _PARAM0_ and store it into variable _PARAM1_"),
|
||||
_("Network"),
|
||||
"res/actions/net24.png",
|
||||
@@ -109,11 +109,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
|
||||
extension
|
||||
.AddStrExpression("ToJSON",
|
||||
_("Convert variable to JSON"),
|
||||
_("Convert a variable to JSON"),
|
||||
_("Convert scene variable to JSON"),
|
||||
_("Convert a scene variable to JSON"),
|
||||
_("Conversion"),
|
||||
"res/conditions/toujours24.png")
|
||||
.AddParameter("scenevar", _("The variable to be stringified"));
|
||||
.AddParameter("scenevar", _("Scene variable to be stringified"));
|
||||
|
||||
extension
|
||||
.AddStrExpression("GlobalVarToJSON",
|
||||
|
@@ -91,6 +91,18 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddCondition("SceneJustResumed",
|
||||
_("Scene just resumed"),
|
||||
_("The scene has just resumed after being paused."),
|
||||
_("Scene just resumed"),
|
||||
_("Scene"),
|
||||
"res/conditions/depart24.png",
|
||||
"res/conditions/depart.png")
|
||||
.SetHelpPath("/interface/scene-editor/events")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddAction("Scene",
|
||||
_("Change the scene"),
|
||||
|
@@ -631,10 +631,12 @@ void WholeProjectRefactorer::DoRenameBehavior(
|
||||
}
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::ObjectRemovedInLayout(gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
const gd::String& objectName,
|
||||
bool removeEventsAndGroups) {
|
||||
void WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
|
||||
gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
const gd::String& objectName,
|
||||
bool isObjectGroup,
|
||||
bool removeEventsAndGroups) {
|
||||
// Remove object in the current layout
|
||||
if (removeEventsAndGroups) {
|
||||
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
|
||||
@@ -642,12 +644,17 @@ void WholeProjectRefactorer::ObjectRemovedInLayout(gd::Project& project,
|
||||
layout,
|
||||
layout.GetEvents(),
|
||||
objectName);
|
||||
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
|
||||
if (layout.GetObjectGroups()[g].Find(objectName))
|
||||
layout.GetObjectGroups()[g].RemoveObject(objectName);
|
||||
}
|
||||
}
|
||||
layout.GetInitialInstances().RemoveInitialInstancesOfObject(objectName);
|
||||
if (!isObjectGroup) { // Object groups can't have instances or be in other
|
||||
// groups
|
||||
if (removeEventsAndGroups) {
|
||||
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
|
||||
if (layout.GetObjectGroups()[g].Find(objectName))
|
||||
layout.GetObjectGroups()[g].RemoveObject(objectName);
|
||||
}
|
||||
}
|
||||
layout.GetInitialInstances().RemoveInitialInstancesOfObject(objectName);
|
||||
}
|
||||
|
||||
// Remove object in external events
|
||||
if (removeEventsAndGroups) {
|
||||
@@ -674,19 +681,23 @@ void WholeProjectRefactorer::ObjectRemovedInLayout(gd::Project& project,
|
||||
}
|
||||
|
||||
// Remove object in external layouts
|
||||
std::vector<gd::String> externalLayoutsNames =
|
||||
GetAssociatedExternalLayouts(project, layout);
|
||||
for (gd::String name : externalLayoutsNames) {
|
||||
auto& externalLayout = project.GetExternalLayout(name);
|
||||
externalLayout.GetInitialInstances().RemoveInitialInstancesOfObject(
|
||||
objectName);
|
||||
if (!isObjectGroup) { // Object groups can't have instances
|
||||
std::vector<gd::String> externalLayoutsNames =
|
||||
GetAssociatedExternalLayouts(project, layout);
|
||||
for (gd::String name : externalLayoutsNames) {
|
||||
auto& externalLayout = project.GetExternalLayout(name);
|
||||
externalLayout.GetInitialInstances().RemoveInitialInstancesOfObject(
|
||||
objectName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::ObjectRenamedInLayout(gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName) {
|
||||
void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
|
||||
gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName,
|
||||
bool isObjectGroup) {
|
||||
// Rename object in the current layout
|
||||
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
|
||||
project,
|
||||
@@ -694,9 +705,13 @@ void WholeProjectRefactorer::ObjectRenamedInLayout(gd::Project& project,
|
||||
layout.GetEvents(),
|
||||
oldName,
|
||||
newName);
|
||||
layout.GetInitialInstances().RenameInstancesOfObject(oldName, newName);
|
||||
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
|
||||
layout.GetObjectGroups()[g].RenameObject(oldName, newName);
|
||||
|
||||
if (!isObjectGroup) { // Object groups can't have instances or be in other
|
||||
// groups
|
||||
layout.GetInitialInstances().RenameInstancesOfObject(oldName, newName);
|
||||
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
|
||||
layout.GetObjectGroups()[g].RenameObject(oldName, newName);
|
||||
}
|
||||
}
|
||||
|
||||
// Rename object in external events
|
||||
@@ -723,36 +738,96 @@ void WholeProjectRefactorer::ObjectRenamedInLayout(gd::Project& project,
|
||||
}
|
||||
|
||||
// Rename object in external layouts
|
||||
std::vector<gd::String> externalLayoutsNames =
|
||||
GetAssociatedExternalLayouts(project, layout);
|
||||
for (gd::String name : externalLayoutsNames) {
|
||||
auto& externalLayout = project.GetExternalLayout(name);
|
||||
externalLayout.GetInitialInstances().RenameInstancesOfObject(oldName,
|
||||
newName);
|
||||
if (!isObjectGroup) { // Object groups can't have instances
|
||||
std::vector<gd::String> externalLayoutsNames =
|
||||
GetAssociatedExternalLayouts(project, layout);
|
||||
for (gd::String name : externalLayoutsNames) {
|
||||
auto& externalLayout = project.GetExternalLayout(name);
|
||||
externalLayout.GetInitialInstances().RenameInstancesOfObject(oldName,
|
||||
newName);
|
||||
}
|
||||
}
|
||||
}
|
||||
void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
|
||||
gd::Project& project,
|
||||
gd::EventsFunction& eventsFunction,
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer,
|
||||
const gd::String& objectName,
|
||||
bool isObjectGroup,
|
||||
bool removeEventsAndGroups) {
|
||||
// Remove object in the current layout
|
||||
if (removeEventsAndGroups) {
|
||||
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
eventsFunction.GetEvents(),
|
||||
objectName);
|
||||
}
|
||||
if (!isObjectGroup) { // Object groups can't be in other groups
|
||||
if (removeEventsAndGroups) {
|
||||
for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size();
|
||||
++g) {
|
||||
if (eventsFunction.GetObjectGroups()[g].Find(objectName))
|
||||
eventsFunction.GetObjectGroups()[g].RemoveObject(objectName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::GlobalObjectRenamed(gd::Project& project,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName) {
|
||||
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
|
||||
project.GetObjectGroups()[g].RenameObject(oldName, newName);
|
||||
void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
|
||||
gd::Project& project,
|
||||
gd::EventsFunction& eventsFunction,
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName,
|
||||
bool isObjectGroup) {
|
||||
// Rename object in the current layout
|
||||
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
eventsFunction.GetEvents(),
|
||||
oldName,
|
||||
newName);
|
||||
|
||||
if (!isObjectGroup) { // Object groups can't be in other groups
|
||||
for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size(); ++g) {
|
||||
eventsFunction.GetObjectGroups()[g].RenameObject(oldName, newName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
|
||||
gd::Project& project,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName,
|
||||
bool isObjectGroup) {
|
||||
if (!isObjectGroup) { // Object groups can't be in other groups
|
||||
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
|
||||
project.GetObjectGroups()[g].RenameObject(oldName, newName);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
|
||||
gd::Layout& layout = project.GetLayout(i);
|
||||
if (layout.HasObjectNamed(oldName)) continue;
|
||||
|
||||
ObjectRenamedInLayout(project, layout, oldName, newName);
|
||||
ObjectOrGroupRenamedInLayout(
|
||||
project, layout, oldName, newName, isObjectGroup);
|
||||
}
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::GlobalObjectRemoved(gd::Project& project,
|
||||
const gd::String& objectName,
|
||||
bool removeEventsAndGroups) {
|
||||
if (removeEventsAndGroups) {
|
||||
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
|
||||
project.GetObjectGroups()[g].RemoveObject(objectName);
|
||||
void WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
|
||||
gd::Project& project,
|
||||
const gd::String& objectName,
|
||||
bool isObjectGroup,
|
||||
bool removeEventsAndGroups) {
|
||||
if (!isObjectGroup) { // Object groups can't be in other groups
|
||||
if (removeEventsAndGroups) {
|
||||
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
|
||||
project.GetObjectGroups()[g].RemoveObject(objectName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,7 +835,8 @@ void WholeProjectRefactorer::GlobalObjectRemoved(gd::Project& project,
|
||||
gd::Layout& layout = project.GetLayout(i);
|
||||
if (layout.HasObjectNamed(objectName)) continue;
|
||||
|
||||
ObjectRemovedInLayout(project, layout, objectName, removeEventsAndGroups);
|
||||
ObjectOrGroupRemovedInLayout(
|
||||
project, layout, objectName, isObjectGroup, removeEventsAndGroups);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,7 @@ class Layout;
|
||||
class String;
|
||||
class EventsFunctionsExtension;
|
||||
class EventsFunction;
|
||||
class ObjectsContainer;
|
||||
class EventsBasedBehavior;
|
||||
class ArbitraryEventsWorker;
|
||||
class ArbitraryEventsWorkerWithContext;
|
||||
@@ -24,10 +25,10 @@ namespace gd {
|
||||
* \brief Tool functions to do refactoring on the whole project after
|
||||
* changes like deletion or renaming of an object.
|
||||
*
|
||||
* \TODO Ideally ObjectRenamedInLayout, ObjectRemovedInLayout,
|
||||
* GlobalObjectRenamed, GlobalObjectRemoved would be implemented using
|
||||
* ExposeProjectEvents.
|
||||
**/
|
||||
* \TODO Ideally ObjectOrGroupRenamedInLayout, ObjectOrGroupRemovedInLayout,
|
||||
* GlobalObjectOrGroupRenamed, GlobalObjectOrGroupRemoved would be implemented
|
||||
* using ExposeProjectEvents.
|
||||
*/
|
||||
class GD_CORE_API WholeProjectRefactorer {
|
||||
public:
|
||||
/**
|
||||
@@ -103,10 +104,11 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
* This will update the layout, all external layouts associated with it
|
||||
* and all external events used by the layout.
|
||||
*/
|
||||
static void ObjectRenamedInLayout(gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName);
|
||||
static void ObjectOrGroupRenamedInLayout(gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName,
|
||||
bool isObjectGroup);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after an object is removed in a layout
|
||||
@@ -114,10 +116,39 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
* This will update the layout, all external layouts associated with it
|
||||
* and all external events used by the layout.
|
||||
*/
|
||||
static void ObjectRemovedInLayout(gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
const gd::String& objectName,
|
||||
bool removeEventsAndGroups = true);
|
||||
static void ObjectOrGroupRemovedInLayout(gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
const gd::String& objectName,
|
||||
bool isObjectGroup,
|
||||
bool removeEventsAndGroups = true);
|
||||
|
||||
/**
|
||||
* \brief Refactor the events function after an object or group is renamed
|
||||
*
|
||||
* This will update the events of the function and groups.
|
||||
*/
|
||||
static void ObjectOrGroupRenamedInEventsFunction(
|
||||
gd::Project& project,
|
||||
gd::EventsFunction& eventsFunction,
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName,
|
||||
bool isObjectGroup);
|
||||
|
||||
/**
|
||||
* \brief Refactor the events function after an object or group is renamed
|
||||
*
|
||||
* This will update the events of the function and groups.
|
||||
*/
|
||||
static void ObjectOrGroupRemovedInEventsFunction(
|
||||
gd::Project& project,
|
||||
gd::EventsFunction& eventsFunction,
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer,
|
||||
const gd::String& objectName,
|
||||
bool isObjectGroup,
|
||||
bool removeEventsAndGroups = true);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after a global object is renamed.
|
||||
@@ -125,9 +156,10 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
* This will update all the layouts, all external layouts associated with them
|
||||
* and all external events used by the layouts.
|
||||
*/
|
||||
static void GlobalObjectRenamed(gd::Project& project,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName);
|
||||
static void GlobalObjectOrGroupRenamed(gd::Project& project,
|
||||
const gd::String& oldName,
|
||||
const gd::String& newName,
|
||||
bool isObjectGroup);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project after a global object is removed.
|
||||
@@ -135,9 +167,10 @@ class GD_CORE_API WholeProjectRefactorer {
|
||||
* This will update all the layouts, all external layouts associated with them
|
||||
* and all external events used by the layouts.
|
||||
*/
|
||||
static void GlobalObjectRemoved(gd::Project& project,
|
||||
const gd::String& objectName,
|
||||
bool removeEventsAndGroups = true);
|
||||
static void GlobalObjectOrGroupRemoved(gd::Project& project,
|
||||
const gd::String& objectName,
|
||||
bool isObjectGroup,
|
||||
bool removeEventsAndGroups = true);
|
||||
|
||||
/**
|
||||
* \brief Return the set of all the types of the objects that are using the
|
||||
|
@@ -93,6 +93,9 @@ class GD_CORE_API EventsFunctionsContainer
|
||||
void MoveEventsFunction(std::size_t oldIndex, std::size_t newIndex) {
|
||||
return Move(oldIndex, newIndex);
|
||||
};
|
||||
std::size_t GetEventsFunctionPosition(const gd::EventsFunction& eventsFunction) {
|
||||
return GetPosition(eventsFunction);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Provide a raw access to the vector containing the functions.
|
||||
@@ -141,4 +144,4 @@ class GD_CORE_API EventsFunctionsContainer
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_EVENTSFUNCTIONSCONTAINER_H
|
||||
#endif
|
||||
#endif
|
||||
|
@@ -4,12 +4,12 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "GDCore/Project/ResourcesManager.h"
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ResourcesManager.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
@@ -553,13 +553,35 @@ void JsonResource::SetFile(const gd::String& newFile) {
|
||||
void JsonResource::UnserializeFrom(const SerializerElement& element) {
|
||||
SetUserAdded(element.GetBoolAttribute("userAdded"));
|
||||
SetFile(element.GetStringAttribute("file"));
|
||||
DisablePreload(element.GetBoolAttribute("disablePreload", false));
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
void JsonResource::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("userAdded", IsUserAdded());
|
||||
element.SetAttribute("file", GetFile());
|
||||
element.SetAttribute("disablePreload", IsPreloadDisabled());
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> JsonResource::GetProperties(
|
||||
gd::Project& project) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties["disablePreload"]
|
||||
.SetValue(disablePreload ? "true" : "false")
|
||||
.SetType("Boolean")
|
||||
.SetLabel(_("Disable preloading at game startup"));
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
bool JsonResource::UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) {
|
||||
if (name == "disablePreload") disablePreload = value == "1";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
#ifndef GDCORE_RESOURCESMANAGER_H
|
||||
#define GDCORE_RESOURCESMANAGER_H
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
@@ -13,7 +14,7 @@ class Project;
|
||||
class ResourceFolder;
|
||||
class SerializerElement;
|
||||
class PropertyDescriptor;
|
||||
}
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -82,7 +83,9 @@ class GD_CORE_API Resource {
|
||||
* \note Can be used by external editors to store extra information, for
|
||||
* example the configuration used to produce a sound.
|
||||
*/
|
||||
virtual void SetMetadata(const gd::String& metadata_) { metadata = metadata_; }
|
||||
virtual void SetMetadata(const gd::String& metadata_) {
|
||||
metadata = metadata_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the (optional) metadata associated to the resource
|
||||
@@ -300,7 +303,7 @@ class GD_CORE_API VideoResource : public Resource {
|
||||
*/
|
||||
class GD_CORE_API JsonResource : public Resource {
|
||||
public:
|
||||
JsonResource() : Resource() { SetKind("json"); };
|
||||
JsonResource() : Resource(), disablePreload(false) { SetKind("json"); };
|
||||
virtual ~JsonResource(){};
|
||||
virtual JsonResource* Clone() const override {
|
||||
return new JsonResource(*this);
|
||||
@@ -311,12 +314,30 @@ class GD_CORE_API JsonResource : public Resource {
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual bool UseFile() override { return true; }
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
gd::Project& project) const override;
|
||||
bool UpdateProperty(const gd::String& name,
|
||||
const gd::String& value,
|
||||
gd::Project& project) override;
|
||||
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
#endif
|
||||
|
||||
void UnserializeFrom(const SerializerElement& element) override;
|
||||
|
||||
/**
|
||||
* \brief Return true if the loading at game startup must be disabled
|
||||
*/
|
||||
bool IsPreloadDisabled() const { return disablePreload; }
|
||||
|
||||
/**
|
||||
* \brief Set if the json preload at game startup must be disabled
|
||||
*/
|
||||
void DisablePreload(bool disable = true) { disablePreload = disable; }
|
||||
|
||||
private:
|
||||
bool disablePreload; ///< If "true", don't load the JSON at game startup
|
||||
gd::String file;
|
||||
};
|
||||
|
||||
@@ -366,7 +387,8 @@ class GD_CORE_API ResourcesManager {
|
||||
|
||||
/**
|
||||
* \brief Add an already constructed resource.
|
||||
* \note A copy of the resource is made and stored inside the ResourcesManager.
|
||||
* \note A copy of the resource is made and stored inside the
|
||||
* ResourcesManager.
|
||||
*/
|
||||
bool AddResource(const gd::Resource& resource);
|
||||
|
||||
@@ -403,7 +425,7 @@ class GD_CORE_API ResourcesManager {
|
||||
bool MoveResourceDownInList(const gd::String& name);
|
||||
|
||||
/**
|
||||
* Change the position of the specified resource.
|
||||
* \brief Change the position of the specified resource.
|
||||
*/
|
||||
void MoveResource(std::size_t oldIndex, std::size_t newIndex);
|
||||
|
||||
|
@@ -129,6 +129,11 @@ class SerializableWithNameList {
|
||||
*/
|
||||
bool Has(const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Get the position of an element in the list
|
||||
*/
|
||||
std::size_t GetPosition(const T& element) const;
|
||||
|
||||
/** \name std::vector-like API
|
||||
* These functions ensure that the class can be used just like a std::vector
|
||||
* for iterations.
|
||||
|
@@ -109,6 +109,15 @@ void SerializableWithNameList<T>::Move(std::size_t oldIndex,
|
||||
elements.insert(elements.begin() + newIndex, std::move(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t SerializableWithNameList<T>::GetPosition(const T& element) const {
|
||||
for(std::size_t index = 0;index<elements.size();++index) {
|
||||
if (&element == elements[index].get()) return index;
|
||||
}
|
||||
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SerializableWithNameList<T>::SerializeElementsTo(
|
||||
const gd::String& elementName, SerializerElement& serializerElement) const {
|
||||
|
122
Core/GDevelop-Architecture-Overview.md
Normal file
122
Core/GDevelop-Architecture-Overview.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# GDevelop Architecture Overview
|
||||
|
||||
GDevelop is architectured around a `Core` library, platforms (`GDJS`, `GDCpp`) and extensions (`Extensions` folder). The editor (`newIDE` folder) is using all of these libraries. This is a recap table of the main folders:
|
||||
|
||||
| Directory | ℹ️ Description |
|
||||
| --- | --- |
|
||||
| `Core` | GDevelop core library, containing common tools to implement the IDE and work with GDevelop games. |
|
||||
| `GDCpp` | GDevelop C++ game engine, used to **build native games**. |
|
||||
| `GDJS` | GDevelop JS game engine, used to build **HTML5 games**. |
|
||||
| `GDevelop.js` | Bindings of Core/GDCpp/GDJS and Extensions to JavaScript (used by the IDE). |
|
||||
| `newIDE` | The game editor, written in JavaScript with React, Electron and Pixi.js. |
|
||||
| `Extensions` | Extensions for C++ or JS game engines, providing objects, events and new features. |
|
||||
|
||||
The rest of this page is an introduction to the main concepts of GDevelop architecture.
|
||||
|
||||
## Some vocabulary: "Runtime" and "IDE"
|
||||
|
||||
**IDE** stands for "Integrated Development Environment". A synonym for it is also simply "editor". In GDevelop, the software itself, that is used to create games and running as an app or a web-app, is called the "GDevelop Editor" or the "GDevelop IDE"
|
||||
|
||||
> This term "IDE" is also used in some folders. When you browse `Core`, `GDCpp` or `GDJS` subfolders, some folders are called `IDE`. They contain classes and tools that are **only useful for the editor** (they are not per se mandatory to describe the structure of a game).
|
||||
|
||||
**Runtime** is a word used to describe classes, tools and source files being used during a game. This could also be called "in game" or a "game engine".
|
||||
|
||||
> When you browse `GDCpp` or `GDJS` subfolders, you can find folders called `Runtime`. They contain the **game engine** of GDevelop.
|
||||
|
||||
Extensions do have the same distinction between the "**IDE**" part and the "**Runtime**" part. For example, most extensions have:
|
||||
|
||||
* A file called [`JsExtension.js`(https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/JsExtension.js)], which contains the *declaration* of the extension for the **IDE**
|
||||
* One or more files implementing the feature for the game, in other words for **Runtime**. This can be a [Runtime Object](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimeobject.js) or a [Runtime Behavior](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/dummyruntimebehavior.js), [functions called by actions or conditions](https://github.com/4ian/GDevelop/blob/master/Extensions/ExampleJsExtension/examplejsextensiontools.js) or by the game engine.
|
||||
|
||||
### "Runtime" and "IDE" difference using an example: the `gd::Variable` class
|
||||
|
||||
In GDevelop, developers can associate and manipulate variables in their games. To represent them, we have two things:
|
||||
|
||||
* The **editor** `gd::Variable` that is part of the structure of the game, so living in [GDCore in Variable.h](https://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_variable.html). This is what is shown in the editor, saved on disk in the project file.
|
||||
* The **game engine** variable, called `gdjs.Variable` in GDJS. [Documentation is in the GDJS **game engine**](http://4ian.github.io/GD-Documentation/GDJS%20Runtime%20Documentation/gdjs.Variable.html). This JavaScript class is what is used during a game.
|
||||
|
||||
The editor `gd::Variable` **know nothing** about the game engine class `gdjs.Variable`. And the `gdjs.Variable` class in the game engine know almost nothing from `gd::Variable` (apart from how it's written in JSON, to be able to load a game default variables).
|
||||
|
||||
> Note that the name `gdjs.Variable` is maybe a *bad decision*: it should have been called a `gdjs.RuntimeVariable`, like `gdjs.RuntimeObject` and like most other classes of the game engine.
|
||||
|
||||
## What's inside "Core" (`GDCore` folder)?
|
||||
|
||||
GDevelop "Core" is basically containing everything that is used to **describe and manipulate the structure of a game** (called a `Project` internally). This includes events, scenes, objects, behaviors, events etc... All of these are implemented using C++ classes, in [this folder named `Project`](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/Project).
|
||||
|
||||
GDevelop "Core" also contains **tools** to manipulate these `Project`. In particular, `Core/GDCore/IDE` folder is containing C++ classes allowing to do operations on the structure of a game. For example, [WholeProjectRefactorer](https://github.com/4ian/GDevelop/blob/master/Core/GDCore/IDE/WholeProjectRefactorer.cpp) is a very powerful tool, used to rename all objects in a game, update events after an object is deleted and more generally do Project wide refactoring. The directory contains other "tool" functions to manipulate [resources of projects](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/IDE/Project) or do [search in events](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/IDE/Events).
|
||||
|
||||
## What's inside GDJS? Why do I see some C++ file there?
|
||||
|
||||
While `GDJS/Runtime` folder is the game engine that is executed inside a game, `GDJS/GDJS` is the "IDE" part, which are the C++ classes describing to the editor various things like:
|
||||
|
||||
* How [do you do an export](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/IDE),
|
||||
* What are [the default extensions](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Extensions/Builtin),
|
||||
* How do you [generate JS code from events](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Events/CodeGeneration),
|
||||
|
||||
The game engine is in GDJS/Runtime and is all JavaScript.
|
||||
|
||||
## What about events?
|
||||
|
||||
An "**event**" is by default something that [is mostly empty](http://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_base_event.html). In a more traditional programming language, an event can be seen as a scope or block (example: `{ some code here }` in a C style language like JavaScript, Java or C++).
|
||||
|
||||
[Default events are defined](https://github.com/4ian/GDevelop/tree/master/Core/GDCore/Events/Builtin) in GDevelop Core.
|
||||
In particular, there is StandardEvent, which has conditions and actions. Conditions and actions are both a list of [`gd::Instruction`](https://4ian.github.io/GD-Documentation/GDCore%20Documentation/classgd_1_1_instruction.html). A `gd::Instruction` is either a condition or an action (it depends only on the context where they are used).
|
||||
|
||||
A `gd::Instruction` is "just" a type (the name of the action or condition), and some parameters. You can think of it as a function in a programming language (`add(2, 3)` is calling a function called "add" with parameter "2" and "3"). Conditions have the special thing that they are functions returning true or false (and potentially being used to do filter on the objects being picked for next conditions and actions).
|
||||
|
||||
### Why can't I see any "RuntimeEvent" or events during the game?
|
||||
|
||||
They do not exist anymore! ✨
|
||||
|
||||
Events are translated (we also say "transpiled" or "generated") into "real" code in a programming language. This process is called "Code Generation", and is [done here for the JavaScript game engine](https://github.com/4ian/GDevelop/tree/master/GDJS/GDJS/Events/CodeGeneration).
|
||||
|
||||
### Can I extract Events classes and code generator to make a development environment based on GDevelop events?
|
||||
|
||||
You're welcome to do so! Please get in touch :)
|
||||
|
||||
## I can see more than one Extensions folder, why?
|
||||
|
||||
The idea of GDevelop editor and game engine is to have a lean game engine, supporting almost nothing. Then, one can add "mods", "plugins", "modules" for GDevelop. We chose to call them "**Extensions**" in GDevelop.
|
||||
|
||||
* `GDevelop/Core/GDCore/Extensions` is the **declaration** of default (we say "builtin") extensions, that are available for any game and are "mandatory". They are called Extensions but they could be named "Extensions that will always be in your game". In programming languages, this is called a "[Standard Library](https://en.wikipedia.org/wiki/Standard_library)" (but don't get too distracted by this naming).
|
||||
* `GDevelop/GDJS/GDJS/Extensions/` and `GDevelop/GDCpp/GDCpp/Extensions/` are reusing these **declarations** and **adding** their own declarations. Mainly, they are setting the name of the functions to be called (either in JS or in C++) for each action, condition or expression.
|
||||
* `GDevelop/Extensions/` is the folder for the "mods"/"plugins" for GDevelop - the one that are not mandatory. They are not part of GDCore - they work on their own.
|
||||
|
||||
> In theory, all extensions could be moved to `GDevelop/Extensions/`. In practice, it's more pragmatic to have a set of "builtin" extensions with basic features.
|
||||
|
||||
## What's GDevelop.js? Do we care about this?
|
||||
|
||||
Everything in GDevelop.js is meant to create a "bridge" allowing to run and use C++ from JavaScript for the **IDE**, so that [we can write an editor entirely in JavaScript](https://github.com/4ian/GDevelop/tree/master/newIDE) and have it working in a web browser.
|
||||
|
||||
* We're using Emscripten which is compiling C++, but instead of writing a "native binary", it's writing a file that works in a browser (basically, JavaScript!).
|
||||
|
||||
* The most useful file is [Bindings.idl](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Bindings.idl) that is describing everything in C++ that must be exposed to JavaScript (in the editor, not in game. Remember that during the game, we're at **Runtime**, so all of this does not exist anymore)
|
||||
|
||||
* Rest of the files are mostly bridges doing "weird stuff" to translate from JS to C++ or vice versa. It requires a bit of knowledge about how the ["bridge", made by Emscripten, called WebIDL](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.html) is working.
|
||||
|
||||
You don't need to work with these unless you want to expose something that is written in C++ to the editor, and writing the interface in Bindings.idl is not sufficient.
|
||||
|
||||
90% of the time, you can just read or write about a class in [Bindings.idl](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Bindings.idl). If you work in C++ classes, you may have sometime to add a header file in Wrapper.cpp and your C++ class is "automagically" compiled and available in JavaScript after writing the corresponding interface in Bindings.idl.
|
||||
|
||||
### I want all the gory details about GDevelop.js 🧐
|
||||
|
||||
All the required C++ files are imported into this huge list: https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Wrapper.cpp#L1-L79. C++ compilation is done with a "build system" called CMake, which is using [this file](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/CMakeLists.txt#L82-L101) to see what to compile.
|
||||
|
||||
> In an ideal world, there would be something to do this automatically, so GDevelop.js folder would not even exist 😉
|
||||
> If you're interested and want to know more about GDevelop.js bridging between C++ and JavaScript, look at [this talk from GDevelop original author](https://www.youtube.com/watch?v=6La7jSCnYyk).
|
||||
|
||||
## Misc questions
|
||||
|
||||
### What's the deal with C++? Why so much of it?
|
||||
|
||||
GDevelop was originally written in C++. It's a scary language at first but is portable across almost any existing machine in this universe, can be pretty good, safe and readable with the latest C++ features.
|
||||
|
||||
### What's the deal with JavaScript? Why so much of it?
|
||||
|
||||
JavaScript, with the latest language proposals, is actually a very capable language, fast to write and safe with typing:
|
||||
|
||||
* Performance is getting pretty good with recent browsers JIT features.
|
||||
* Frontend frameworks like React, used for GDevelop IDE, allow for very fast and modular interface development.
|
||||
* The web is an incredible and unmatched target when it comes to cross platform (and cross form factor) apps.
|
||||
|
||||
More generally, the web is a great target for games with the rise of WebGL and WebAssembly - though GDevelop should stay modular to adapt to newer platforms for the exported games in the future.
|
@@ -11,6 +11,7 @@
|
||||
#include "GDCore/Events/Builtin/LinkEvent.h"
|
||||
#include "GDCore/Events/Builtin/StandardEvent.h"
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
@@ -151,7 +152,7 @@ gd::EventsFunctionsExtension &SetupProjectWithEventsFunctionExtension(
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
SECTION("Object deleted") {
|
||||
SECTION("Object deleted (in layout)") {
|
||||
SECTION("Groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
@@ -168,9 +169,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object2", 0);
|
||||
|
||||
gd::WholeProjectRefactorer::ObjectRemovedInLayout(
|
||||
project, layout1, "Object1");
|
||||
gd::WholeProjectRefactorer::GlobalObjectRemoved(project, "GlobalObject1");
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
|
||||
project, layout1, "Object1", /* isObjectGroup =*/false);
|
||||
gd::WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
|
||||
project, "GlobalObject1", /* isObjectGroup =*/false);
|
||||
REQUIRE(layout1.GetObjectGroups()[0].Find("Object1") == false);
|
||||
REQUIRE(layout1.GetObjectGroups()[0].Find("Object2") == true);
|
||||
REQUIRE(layout1.GetObjectGroups()[0].Find("NotExistingObject") == true);
|
||||
@@ -196,9 +198,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
layout1.GetInitialInstances().InsertInitialInstance(instance2);
|
||||
layout1.GetInitialInstances().InsertInitialInstance(instance3);
|
||||
|
||||
gd::WholeProjectRefactorer::ObjectRemovedInLayout(
|
||||
project, layout1, "Object1");
|
||||
gd::WholeProjectRefactorer::GlobalObjectRemoved(project, "GlobalObject1");
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
|
||||
project, layout1, "Object1", /* isObjectGroup =*/false);
|
||||
gd::WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
|
||||
project, "GlobalObject1", /* isObjectGroup =*/false);
|
||||
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object1") ==
|
||||
false);
|
||||
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object2") ==
|
||||
@@ -237,9 +240,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
externalLayout2.GetInitialInstances().InsertInitialInstance(instance2);
|
||||
externalLayout2.GetInitialInstances().InsertInitialInstance(instance3);
|
||||
|
||||
gd::WholeProjectRefactorer::ObjectRemovedInLayout(
|
||||
project, layout1, "Object1");
|
||||
gd::WholeProjectRefactorer::GlobalObjectRemoved(project, "GlobalObject1");
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
|
||||
project, layout1, "Object1", /* isObjectGroup =*/false);
|
||||
gd::WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
|
||||
project, "GlobalObject1", /* isObjectGroup =*/false);
|
||||
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
|
||||
"Object1") == false);
|
||||
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
|
||||
@@ -255,7 +259,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Object renamed") {
|
||||
SECTION("Object renamed (in layout)") {
|
||||
SECTION("Groups") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
@@ -272,10 +276,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object2", 0);
|
||||
|
||||
gd::WholeProjectRefactorer::ObjectRenamedInLayout(
|
||||
project, layout1, "Object1", "Object3");
|
||||
gd::WholeProjectRefactorer::GlobalObjectRenamed(
|
||||
project, "GlobalObject1", "GlobalObject3");
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
|
||||
project, layout1, "Object1", "Object3", /* isObjectGroup =*/false);
|
||||
gd::WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
|
||||
project, "GlobalObject1", "GlobalObject3", /* isObjectGroup =*/false);
|
||||
REQUIRE(layout1.GetObjectGroups()[0].Find("Object1") == false);
|
||||
REQUIRE(layout1.GetObjectGroups()[0].Find("Object2") == true);
|
||||
REQUIRE(layout1.GetObjectGroups()[0].Find("Object3") == true);
|
||||
@@ -302,10 +306,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
layout1.GetInitialInstances().InsertInitialInstance(instance2);
|
||||
layout1.GetInitialInstances().InsertInitialInstance(instance3);
|
||||
|
||||
gd::WholeProjectRefactorer::ObjectRenamedInLayout(
|
||||
project, layout1, "Object1", "Object3");
|
||||
gd::WholeProjectRefactorer::GlobalObjectRenamed(
|
||||
project, "GlobalObject1", "GlobalObject3");
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
|
||||
project, layout1, "Object1", "Object3", /* isObjectGroup =*/false);
|
||||
gd::WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
|
||||
project, "GlobalObject1", "GlobalObject3", /* isObjectGroup =*/false);
|
||||
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object1") ==
|
||||
false);
|
||||
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object3") ==
|
||||
@@ -346,10 +350,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
externalLayout2.GetInitialInstances().InsertInitialInstance(instance2);
|
||||
externalLayout2.GetInitialInstances().InsertInitialInstance(instance3);
|
||||
|
||||
gd::WholeProjectRefactorer::ObjectRenamedInLayout(
|
||||
project, layout1, "Object1", "Object3");
|
||||
gd::WholeProjectRefactorer::GlobalObjectRenamed(
|
||||
project, "GlobalObject1", "GlobalObject3");
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
|
||||
project, layout1, "Object1", "Object3", /* isObjectGroup =*/false);
|
||||
gd::WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
|
||||
project, "GlobalObject1", "GlobalObject3", /* isObjectGroup =*/false);
|
||||
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
|
||||
"Object1") == false);
|
||||
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
|
||||
@@ -372,6 +376,89 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
"GlobalObject3") == true);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Object renamed (in events function)") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsExtension =
|
||||
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
|
||||
|
||||
// Add a (free) function with an object group
|
||||
gd::EventsFunction &eventsFunction =
|
||||
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
|
||||
gd::ObjectGroup &objectGroup =
|
||||
eventsFunction.GetObjectGroups().InsertNew("MyGroup", 0);
|
||||
objectGroup.AddObject("Object1");
|
||||
objectGroup.AddObject("Object2");
|
||||
// In theory, we would add the object parameters, but we're not testing
|
||||
// events in this test.
|
||||
|
||||
// Create the objects container for the events function
|
||||
gd::ObjectsContainer globalObjectsContainer;
|
||||
gd::ObjectsContainer objectsContainer;
|
||||
gd::ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
project, eventsFunction.GetParameters(), objectsContainer);
|
||||
// (this is strictly not necessary because we're not testing events in this
|
||||
// test)
|
||||
|
||||
// Trigger the refactoring after the renaming of an object
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
|
||||
project,
|
||||
eventsFunction,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
"Object1",
|
||||
"RenamedObject1",
|
||||
/* isObjectGroup=*/false);
|
||||
|
||||
REQUIRE(objectGroup.Find("Object1") == false);
|
||||
REQUIRE(objectGroup.Find("RenamedObject1") == true);
|
||||
REQUIRE(objectGroup.Find("Object2") == true);
|
||||
|
||||
// Events are not tested
|
||||
}
|
||||
|
||||
SECTION("Object deleted (in events function)") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &eventsExtension =
|
||||
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
|
||||
|
||||
// Add a (free) function with an object group
|
||||
gd::EventsFunction &eventsFunction =
|
||||
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
|
||||
gd::ObjectGroup &objectGroup =
|
||||
eventsFunction.GetObjectGroups().InsertNew("MyGroup", 0);
|
||||
objectGroup.AddObject("Object1");
|
||||
objectGroup.AddObject("Object2");
|
||||
// In theory, we would add the object parameters, but we're not testing
|
||||
// events in this test.
|
||||
|
||||
// Create the objects container for the events function
|
||||
gd::ObjectsContainer globalObjectsContainer;
|
||||
gd::ObjectsContainer objectsContainer;
|
||||
gd::ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
project, eventsFunction.GetParameters(), objectsContainer);
|
||||
// (this is strictly not necessary because we're not testing events in this
|
||||
// test)
|
||||
|
||||
// Trigger the refactoring after the renaming of an object
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
|
||||
project,
|
||||
eventsFunction,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
"Object1",
|
||||
/* isObjectGroup=*/false);
|
||||
|
||||
REQUIRE(objectGroup.Find("Object1") == false);
|
||||
REQUIRE(objectGroup.Find("Object2") == true);
|
||||
|
||||
// Events are not tested
|
||||
}
|
||||
|
||||
SECTION("Events extension renamed") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
@@ -604,12 +691,11 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
auto &eventsBasedBehavior =
|
||||
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
|
||||
|
||||
gd::WholeProjectRefactorer::RenameBehaviorProperty(
|
||||
project,
|
||||
eventsExtension,
|
||||
eventsBasedBehavior,
|
||||
"MyProperty",
|
||||
"MyRenamedProperty");
|
||||
gd::WholeProjectRefactorer::RenameBehaviorProperty(project,
|
||||
eventsExtension,
|
||||
eventsBasedBehavior,
|
||||
"MyProperty",
|
||||
"MyRenamedProperty");
|
||||
|
||||
// Check if events based behaviors property has been renamed in
|
||||
// instructions
|
||||
|
@@ -5,15 +5,13 @@ if(GIT_FOUND)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} clone "https://www.github.com/SFML/SFML.git" SFML
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
|
||||
ERROR_QUIET
|
||||
OUTPUT_QUIET)
|
||||
|
||||
message( "Resetting SFML source code to version 2.4.1..." )
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} reset --hard 2.4.1
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
|
||||
ERROR_QUIET
|
||||
OUTPUT_QUIET)
|
||||
OUTPUT_QUIET)
|
||||
|
||||
message( "Applying the patches..." )
|
||||
file(GLOB SFML_PATCHES
|
||||
@@ -26,9 +24,8 @@ if(GIT_FOUND)
|
||||
foreach(SFML_PATCH ${SFML_PATCHES})
|
||||
message( "Applying patch: ${SFML_PATCH}..." )
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} apply ${SFML_PATCH}
|
||||
COMMAND ${GIT_EXECUTABLE} apply ${SFML_PATCH} --ignore-whitespace
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SFML
|
||||
ERROR_QUIET
|
||||
OUTPUT_QUIET)
|
||||
endforeach()
|
||||
endif()
|
||||
|
@@ -22,6 +22,8 @@ gdjs.DestroyOutsideRuntimeBehavior.thisIsARuntimeBehaviorConstructor = "DestroyO
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
// is not necessarily in the middle of the object (for sprites for example).
|
||||
var ow = this.owner.getWidth();
|
||||
var oh = this.owner.getHeight();
|
||||
var ocx = this.owner.getDrawableX()+this.owner.getCenterX();
|
||||
|
@@ -39,7 +39,9 @@ module.exports = {
|
||||
)
|
||||
.addParameter(
|
||||
'scenevar',
|
||||
_('Scene variable that holds the Yarn Json data')
|
||||
_('Scene variable that holds the Yarn Json data'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/DialogueTree/dialoguetools.js')
|
||||
@@ -61,7 +63,9 @@ module.exports = {
|
||||
.addCodeOnlyParameter('currentScene', '')
|
||||
.addParameter(
|
||||
'jsonResource',
|
||||
_('Json file that holds the Yarn Json data')
|
||||
_('Json file that holds the Yarn Json data'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/DialogueTree/dialoguetools.js')
|
||||
@@ -70,7 +74,7 @@ module.exports = {
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'StarDialogueFromBranch',
|
||||
'StartDialogueFromBranch',
|
||||
_('Start dialogue from branch'),
|
||||
_(
|
||||
'Start dialogue from branch. Use this to initiate the dialogue from a specified branch.'
|
||||
@@ -80,16 +84,29 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('Dialogue branch'))
|
||||
.addParameter('string', _('Dialogue branch'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.startFrom');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'StopRunningDialogue',
|
||||
_('Stop running dialogue'),
|
||||
_('Stop the running dialogue. Use this to interrupt dialogue parsing.'),
|
||||
_('Stop running dialogue'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.stopRunningDialogue');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'GoToNextLine',
|
||||
_('Go to the next dialogue line'),
|
||||
_(
|
||||
'Go to the next dialogue line. Use this to advance to the next dialogue line when the player presses a button.'
|
||||
'Go to the next dialogue line. Use this to advance to the next dialogue line when the player presses a button.'
|
||||
),
|
||||
_('Go to the next dialogue line'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
@@ -156,7 +173,7 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('expression', _('Option index number'))
|
||||
.addParameter('expression', _('Option index number'), '', false)
|
||||
.setDefaultValue('0')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.selectPreviousOption');
|
||||
@@ -176,6 +193,21 @@ module.exports = {
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.scrollClippedText');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'CompleteClippedTextScrolling',
|
||||
_('Complete clipped text scrolling'),
|
||||
_(
|
||||
'Complete the clipped text scrolling. Use this action whenever you want to skip scrolling.'
|
||||
),
|
||||
_('Complete clipped text scrolling'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.completeClippedTextScrolling');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SetVariable',
|
||||
@@ -188,8 +220,8 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('State Variable Name'))
|
||||
.addParameter('expression', _('Variable Value'))
|
||||
.addParameter('string', _('State Variable Name'), '', false)
|
||||
.addParameter('expression', _('Variable Value'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.setVariable');
|
||||
|
||||
@@ -221,7 +253,7 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('globalvar', _('Global Variable'))
|
||||
.addParameter('globalvar', _('Global Variable'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.loadState');
|
||||
|
||||
@@ -260,10 +292,42 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('expression', _('Option Index Number'))
|
||||
.addParameter('expression', _('Option Index Number'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.getLineOption');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'HorizontalOptionsList',
|
||||
_('Get a Horizontal list of options from the Options line type'),
|
||||
_(
|
||||
"Get the text of all available options from an Options line type as a horizontal list. You can also pass the selected option's cursor string, which by default is ->"
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('Options Selection Cursor'), '', false)
|
||||
.setDefaultValue('>')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.getLineOptionsTextHorizontal');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'VerticalOptionsList',
|
||||
_('Get a Vertical list of options from the Options line type'),
|
||||
_(
|
||||
"Get the text of all available options from an Options line type as a vertical list. You can also pass the selected option's cursor string, which by default is ->"
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('Options Selection Cursor'), '', false)
|
||||
.setDefaultValue('>')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.getLineOptionsTextVertical');
|
||||
|
||||
extension
|
||||
.addExpression(
|
||||
'SelectedOptionIndex',
|
||||
@@ -283,7 +347,7 @@ module.exports = {
|
||||
'ClippedLineText',
|
||||
_('Get dialogue line text clipped'),
|
||||
_(
|
||||
'Get dialogue line text clipped by the typewriter effect. Use the ScrollClippedText action to control the typewriter effect.'
|
||||
'Get dialogue line text clipped by the typewriter effect. Use the "Scroll clipped text" action to control the typewriter effect.'
|
||||
),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
@@ -295,8 +359,8 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'BranchTitle',
|
||||
_('Get the title of the current branch of running dialogue'),
|
||||
_('Get the title of the current branch of running dialogue'),
|
||||
_('Get the title of the current branch of the running dialogue'),
|
||||
_('Get the title of the current branch of the running dialogue'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
@@ -307,8 +371,8 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'BranchTags',
|
||||
_('Get the tags of the current branch of running dialogue'),
|
||||
_('Get the tags of the current branch of running dialogue'),
|
||||
_('Get the tags of the current branch of the running dialogue'),
|
||||
_('Get the tags of the current branch of the running dialogue'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
@@ -319,13 +383,13 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'BranchTag',
|
||||
_('Get a tag of the current branch of running dialogue via number'),
|
||||
_('Get a tag of the current branch of running dialogue via number'),
|
||||
_('Get a tag of the current branch of the running dialogue via its index'),
|
||||
_('Get a tag of the current branch of the running dialogue via its index'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('expression', _('Tag Index Number'))
|
||||
.addParameter('expression', _('Tag Index Number'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.getBranchTag');
|
||||
|
||||
@@ -388,7 +452,7 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'BranchText',
|
||||
_('Get the raw text of the current branch'),
|
||||
_('Get the full raw text of the current branch'),
|
||||
_('Get the full raw text of the current branch'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
@@ -406,13 +470,13 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('Variable Name'))
|
||||
.addParameter('string', _('Variable Name'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.getVariable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'Is command called',
|
||||
'IsCommandCalled',
|
||||
_('Command is called'),
|
||||
_(
|
||||
'Check if a specific Command is called. If it is a <<command withParameter>>, you can even get the parameter with the CommandParameter expression.'
|
||||
@@ -422,14 +486,14 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('Command String'))
|
||||
.addParameter('string', _('Command String'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.isCommandCalled');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsDialogueLineType',
|
||||
_('The dialogue line type is'),
|
||||
_('Dialogue line type'),
|
||||
_(
|
||||
'Check if the the current dialogue line line is one of the three existing types. Use this to set what logic is executed for each type.\nThe three types are as follows:\n- text: when displaying dialogue text.\n- options: when displaying [[branching/options]] for dialogue choices.\n-command: when <<commands>> are triggered by the dialogue data.'
|
||||
),
|
||||
@@ -449,12 +513,12 @@ module.exports = {
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'Is running',
|
||||
_('Is running'),
|
||||
'IsDialogueRunning',
|
||||
_('Dialogue is running'),
|
||||
_(
|
||||
'Check if the dialogue is running. Use this to for things like locking the player movement while speaking with a non player character.'
|
||||
),
|
||||
_('Is running'),
|
||||
_('Dialogue is running'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
@@ -474,7 +538,7 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('Branch name'))
|
||||
.addParameter('string', _('Branch name'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.hasDialogueBranch');
|
||||
|
||||
@@ -485,7 +549,7 @@ module.exports = {
|
||||
_(
|
||||
'Check if a selected option has changed when the current dialogue line type is options. Use this to detect when the player has selected another option, so you can re-draw where the selection arrow is.'
|
||||
),
|
||||
_('Has selected option changed'),
|
||||
_('Selected option has changed'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
@@ -495,8 +559,8 @@ module.exports = {
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'Current branch title is',
|
||||
_('Current dialogue branch title is'),
|
||||
'CurrentBranchTitle',
|
||||
_('Current dialogue branch title'),
|
||||
_(
|
||||
'Check if the current dialogue branch title is equal to a string. Use this to trigger game events when the player has visited a specific dialogue branch.'
|
||||
),
|
||||
@@ -505,13 +569,13 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('title name'))
|
||||
.addParameter('string', _('title name'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.branchTitleIs');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'Current branch contains Tag',
|
||||
'CurrentBranchContainsTag',
|
||||
_('Current dialogue branch contains a tag'),
|
||||
_(
|
||||
'Check if the current dialogue branch contains a specific tag. Tags are an alternative useful way to <<commands>> to drive game logic with the dialogue data.'
|
||||
@@ -521,13 +585,13 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('tag name'))
|
||||
.addParameter('string', _('tag name'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.branchContainsTag');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'Branch has been visited before',
|
||||
'WasBranchVisited',
|
||||
_('Branch title has been visited before'),
|
||||
_('Check if the current branch has been visited before'),
|
||||
_('Branch title _PARAM0_ has been visited before'),
|
||||
@@ -535,13 +599,13 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('branch title'))
|
||||
.addParameter('string', _('branch title'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.branchTitleHasBeenVisited');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'Compare dialogue state variable',
|
||||
'CompareDialogueStateVariable',
|
||||
_('Compare dialogue state variable'),
|
||||
_(
|
||||
'Compare dialogue state variable. Use this to trigger game events via dialogue variables.'
|
||||
@@ -551,19 +615,19 @@ module.exports = {
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
)
|
||||
.addParameter('string', _('State variable'))
|
||||
.addParameter('string', _('Equal to'))
|
||||
.addParameter('string', _('State variable'), '', false)
|
||||
.addParameter('string', _('Equal to'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('gdjs.dialogueTree.compareVariable');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'HasClippedTextScrollingCompleted',
|
||||
_('Has clipped text scrolling completed'),
|
||||
_('Clipped text has completed scrolling'),
|
||||
_(
|
||||
'Check if the clipped text scrolling has completed. Use this to prevent the player from going to the next dialogue line before the typing effect has revealed the entire text.'
|
||||
),
|
||||
_('Has clipped text scrolling completed'),
|
||||
_('Clipped text has completed scrolling'),
|
||||
_('Dialogue Tree (experimental)'),
|
||||
'JsPlatform/Extensions/yarn24.png',
|
||||
'JsPlatform/Extensions/yarn32.png'
|
||||
|
@@ -52,7 +52,15 @@ gdjs.dialogueTree.loadFromJsonFile = function(
|
||||
} else {
|
||||
if (!content) return;
|
||||
gdjs.dialogueTree.yarnData = content;
|
||||
gdjs.dialogueTree.runner.load(gdjs.dialogueTree.yarnData);
|
||||
|
||||
try {
|
||||
gdjs.dialogueTree.runner.load(gdjs.dialogueTree.yarnData);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'An error happened while loading parsing the dialogue tree data:',
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
if (startDialogueNode && startDialogueNode.length > 0) {
|
||||
gdjs.dialogueTree.startFrom(startDialogueNode);
|
||||
@@ -61,11 +69,29 @@ gdjs.dialogueTree.loadFromJsonFile = function(
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop the currently running dialogue
|
||||
*/
|
||||
gdjs.dialogueTree.stopRunningDialogue = function() {
|
||||
if (this.dialogueIsRunning) this.dialogueIsRunning = false;
|
||||
if (this.dialogueData) this.dialogueData = null;
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the Dialogue Tree is currently parsing data.
|
||||
* For example, you can do things like disabling player movement while talking to a NPC.
|
||||
*/
|
||||
gdjs.dialogueTree.isRunning = function() {
|
||||
if (
|
||||
this.dialogueIsRunning &&
|
||||
!this.dialogueData &&
|
||||
this.dialogueText &&
|
||||
this.clipTextEnd >= this.dialogueText.length
|
||||
) {
|
||||
this.dialogueIsRunning = false;
|
||||
}
|
||||
return this.dialogueIsRunning;
|
||||
};
|
||||
|
||||
@@ -80,6 +106,15 @@ gdjs.dialogueTree.scrollClippedText = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the clipped text to its end, so the entire text is printed. This can be useful in keeping the event sheet logic simpler, while supporting more variation.
|
||||
*/
|
||||
gdjs.dialogueTree.completeClippedTextScrolling = function() {
|
||||
if (this.pauseScrolling || !this.dialogueIsRunning || !this.dialogueText)
|
||||
return;
|
||||
this.clipTextEnd = this.dialogueText.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if text scrolling has completed.
|
||||
* Useful to prevent the user from skipping to next line before the current one has been printed fully.
|
||||
@@ -106,7 +141,7 @@ gdjs.dialogueTree.getClippedLineText = function() {
|
||||
* Note that using this instead getClippedLineText will skip any <<wait>> commands entirely.
|
||||
*/
|
||||
gdjs.dialogueTree.getLineText = function() {
|
||||
this.clipTextEnd = this.dialogueText.length;
|
||||
this.completeClippedTextScrolling();
|
||||
return this.dialogueText.length ? this.dialogueText : '';
|
||||
};
|
||||
|
||||
@@ -195,6 +230,37 @@ gdjs.dialogueTree.getLineOption = function(optionIndex) {
|
||||
return this.options[optionIndex];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text of the options the player can select, along with the selection cursor.
|
||||
* @param {string} optionSelectionCursor The string used to draw the currently selected option's cursor
|
||||
* @param {boolean} addNewLine when true each option is rendered on a new line.
|
||||
*/
|
||||
gdjs.dialogueTree.getLineOptionsText = function(
|
||||
optionSelectionCursor,
|
||||
addNewLine
|
||||
) {
|
||||
if (!this.options.length) return '';
|
||||
var textResult = '';
|
||||
this.options.forEach(function(optionText, index) {
|
||||
if (index === gdjs.dialogueTree.selectedOption) {
|
||||
textResult += optionSelectionCursor;
|
||||
} else {
|
||||
textResult += optionSelectionCursor.replace(/.*/g, ' ');
|
||||
}
|
||||
textResult += optionText;
|
||||
if (addNewLine) textResult += '\n';
|
||||
});
|
||||
return textResult;
|
||||
};
|
||||
gdjs.dialogueTree.getLineOptionsTextHorizontal = function(
|
||||
optionSelectionCursor
|
||||
) {
|
||||
return this.getLineOptionsText(optionSelectionCursor, false);
|
||||
};
|
||||
gdjs.dialogueTree.getLineOptionsTextVertical = function(optionSelectionCursor) {
|
||||
return this.getLineOptionsText(optionSelectionCursor, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of options that are presented to the player, during the parsing of an Options type line.
|
||||
* @returns {number} The number of options
|
||||
@@ -218,9 +284,16 @@ gdjs.dialogueTree.confirmSelectOption = function() {
|
||||
this.selectedOption !== -1
|
||||
) {
|
||||
this.commandCalls = [];
|
||||
this.dialogueData.select(this.selectedOption);
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
try {
|
||||
this.dialogueData.select(this.selectedOption);
|
||||
this.dialogueData = this.dialogue.next().value;
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`An error happened when trying to access the dialogue branch!`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -304,11 +377,12 @@ gdjs.dialogueTree.isDialogueLineType = function(type) {
|
||||
if (
|
||||
this.commandCalls &&
|
||||
this.commandCalls.some(function(call) {
|
||||
return gdjs.dialogueTree.clipTextEnd > call.time;
|
||||
return gdjs.dialogueTree.clipTextEnd > call.time && call.cmd === 'wait';
|
||||
})
|
||||
) {
|
||||
return true;
|
||||
return !this.pauseScrolling;
|
||||
}
|
||||
|
||||
return this.dialogueIsRunning ? this.dialogueDataType === type : false;
|
||||
};
|
||||
|
||||
@@ -344,6 +418,7 @@ gdjs.dialogueTree.startFrom = function(startDialogueNode) {
|
||||
this.dialogueData = null;
|
||||
this.dialogueDataType = '';
|
||||
this.dialogueText = '';
|
||||
this.clipTextEnd = 0;
|
||||
this.commandCalls = [];
|
||||
this.commandParameters = [];
|
||||
this.pauseScrolling = false;
|
||||
@@ -425,11 +500,6 @@ gdjs.dialogueTree.goToNextDialogueLine = function() {
|
||||
this.dialogueDataType = 'command';
|
||||
gdjs.dialogueTree.goToNextDialogueLine();
|
||||
}
|
||||
|
||||
// Dialogue has finished
|
||||
if (!this.dialogueData) {
|
||||
this.dialogueIsRunning = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -401,7 +401,9 @@ module.exports = {
|
||||
registerEditorConfigurations: function(objectsEditorService) {
|
||||
objectsEditorService.registerEditorConfiguration(
|
||||
'MyDummyExtension::DummyObject',
|
||||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor()
|
||||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||||
helpPagePath: '/extensions/extend-gdevelop',
|
||||
})
|
||||
);
|
||||
},
|
||||
/**
|
||||
|
@@ -30,7 +30,7 @@ module.exports = {
|
||||
"SavePlayerData",
|
||||
_("Save player data"),
|
||||
_(
|
||||
"Save the content of the given variable in the player data, stored on Facebook Instant Games servers"
|
||||
"Save the content of the given scene variable in the player data, stored on Facebook Instant Games servers"
|
||||
),
|
||||
_(
|
||||
"Save the content of _PARAM1_ in key _PARAM0_ of player data (store success message in _PARAM2_ or error in _PARAM3_)"
|
||||
@@ -40,7 +40,7 @@ module.exports = {
|
||||
"JsPlatform/Extensions/facebookicon16.png"
|
||||
)
|
||||
.addParameter("string", 'Data key name (e.g: "Lives")', "", false)
|
||||
.addParameter("scenevar", "Variable with the content to save", "", false)
|
||||
.addParameter("scenevar", "Scene variable with the content to save", "", false)
|
||||
.addParameter(
|
||||
"scenevar",
|
||||
_("Variable where to store the success message (optional)"),
|
||||
@@ -49,7 +49,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter(
|
||||
"scenevar",
|
||||
_("Variable where to error message (optional, if an error occurs)"),
|
||||
_("Variable where to store the error message (optional, if an error occurs)"),
|
||||
"",
|
||||
true
|
||||
)
|
||||
@@ -80,7 +80,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter(
|
||||
"scenevar",
|
||||
_("Variable where to error message (optional, if an error occurs)"),
|
||||
_("Variable where to store the error message (optional, if an error occurs)"),
|
||||
"",
|
||||
true
|
||||
)
|
||||
@@ -125,7 +125,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter(
|
||||
"scenevar",
|
||||
_("Variable where to error message (optional, if an error occurs)"),
|
||||
_("Variable where to store the error message (optional, if an error occurs)"),
|
||||
"",
|
||||
true
|
||||
)
|
||||
@@ -173,7 +173,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter(
|
||||
"scenevar",
|
||||
_("Variable where to error message (optional, if an error occurs)"),
|
||||
_("Variable where to store the error message (optional, if an error occurs)"),
|
||||
"",
|
||||
true
|
||||
)
|
||||
@@ -233,7 +233,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter(
|
||||
"scenevar",
|
||||
_("Variable where to error message (optional, if an error occurs)"),
|
||||
_("Variable where to store the error message (optional, if an error occurs)"),
|
||||
"",
|
||||
true
|
||||
)
|
||||
@@ -255,7 +255,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter(
|
||||
"scenevar",
|
||||
_("Variable where to error message (optional, if an error occurs)"),
|
||||
_("Variable where to store the error message (optional, if an error occurs)"),
|
||||
"",
|
||||
true
|
||||
)
|
||||
@@ -299,7 +299,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter(
|
||||
"scenevar",
|
||||
_("Variable where to error message (optional, if an error occurs)"),
|
||||
_("Variable where to store the error message (optional, if an error occurs)"),
|
||||
"",
|
||||
true
|
||||
)
|
||||
@@ -321,7 +321,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter(
|
||||
"scenevar",
|
||||
_("Variable where to error message (optional, if an error occurs)"),
|
||||
_("Variable where to store the error message (optional, if an error occurs)"),
|
||||
"",
|
||||
true
|
||||
)
|
||||
|
@@ -172,8 +172,8 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
|
||||
|
||||
extension
|
||||
.AddAction("SerializeToVariable",
|
||||
_("Save an inventory in a variable"),
|
||||
_("Save all the items of the inventory in a variable, so that "
|
||||
_("Save an inventory in a scene variable"),
|
||||
_("Save all the items of the inventory in a scene variable, so that "
|
||||
"it can be restored later."),
|
||||
_("Save inventory _PARAM1_ in variable _PARAM2_"),
|
||||
_("Inventories/Variables"),
|
||||
@@ -188,8 +188,8 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
|
||||
|
||||
extension
|
||||
.AddAction("UnserializeFromVariable",
|
||||
_("Load an inventory from a variable"),
|
||||
_("Load the content of the inventory from a variable."),
|
||||
_("Load an inventory from a scene variable"),
|
||||
_("Load the content of the inventory from a scene variable."),
|
||||
_("Load inventory _PARAM1_ from variable _PARAM2_"),
|
||||
_("Inventories/Variables"),
|
||||
"CppPlatform/Extensions/Inventoryicon24.png",
|
||||
|
@@ -204,7 +204,7 @@ void ExtensionSubDeclaration2(gd::ObjectMetadata& obj) {
|
||||
.SetManipulatedType("number");
|
||||
|
||||
obj.AddCondition("ParticleSize1",
|
||||
_("SIze, parameter 1"),
|
||||
_("Size, parameter 1"),
|
||||
_("Test parameter 1 of the size of particles"),
|
||||
_("Parameter 1 of the size of _PARAM0_ is _PARAM1__PARAM2_"),
|
||||
_("Common"),
|
||||
|
@@ -608,6 +608,8 @@ gdjs.PlatformerObjectRuntimeBehavior.prototype._updatePotentialCollidingObjects
|
||||
var o2w = obj2.getWidth();
|
||||
var o2h = obj2.getHeight();
|
||||
|
||||
// This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
// is not necessarily in the middle of the object (for sprites for example).
|
||||
var x = this.owner.getDrawableX()+this.owner.getCenterX()-(obj2.getDrawableX()+obj2.getCenterX());
|
||||
var y = this.owner.getDrawableY()+this.owner.getCenterY()-(obj2.getDrawableY()+obj2.getCenterY());
|
||||
var obj2BoundingRadius = Math.sqrt(o2w*o2w+o2h*o2h)/2.0;
|
||||
|
@@ -50,6 +50,8 @@ gdjs.PlatformObjectsManager.prototype.removePlatform = function(platformBehavior
|
||||
* @return An array with all platforms near the object.
|
||||
*/
|
||||
gdjs.PlatformObjectsManager.prototype.getAllPlatformsAround = function(object, maxMovementLength, result) {
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
// is not necessarily in the middle of the object (for sprites for example).
|
||||
var ow = object.getWidth();
|
||||
var oh = object.getHeight();
|
||||
var x = object.getDrawableX()+object.getCenterX();
|
||||
|
@@ -15,7 +15,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
|
||||
"PrimitiveDrawing",
|
||||
_("Primitive drawing"),
|
||||
_("This Extension allows you to draw shapes and manipulate images."),
|
||||
"Florian Rival",
|
||||
"Florian Rival and Aurélien Vivet",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects/shape_painter");
|
||||
|
||||
@@ -31,25 +31,25 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
|
||||
obj.AddAction("Rectangle",
|
||||
_("Rectangle"),
|
||||
_("Draw a rectangle on screen"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a "
|
||||
"rectangle with _PARAM0_"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a rectangle "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/rectangle24.png",
|
||||
"res/actions/rectangle.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("Top left side: X Position"))
|
||||
.AddParameter("expression", _("Top left side : Y Position"))
|
||||
.AddParameter("expression", _("Bottom right side : X Position"))
|
||||
.AddParameter("expression", _("Bottom right side : Y Position"))
|
||||
.AddParameter("expression", _("Top left side: X position"))
|
||||
.AddParameter("expression", _("Top left side: Y position"))
|
||||
.AddParameter("expression", _("Bottom right side: X position"))
|
||||
.AddParameter("expression", _("Bottom right side: Y position"))
|
||||
.SetFunctionName("DrawRectangle")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("Circle",
|
||||
_("Circle"),
|
||||
_("Draw a circle on screen"),
|
||||
_("Draw at _PARAM1_;_PARAM2_ a circle of radius _PARAM3_ with "
|
||||
"_PARAM0_"),
|
||||
_("Draw at _PARAM1_;_PARAM2_ a circle of radius _PARAM3_ "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/circle24.png",
|
||||
"res/actions/circle.png")
|
||||
@@ -57,35 +57,295 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of center"))
|
||||
.AddParameter("expression", _("Y position of center"))
|
||||
.AddParameter("expression", _("Radius ( in pixels )"))
|
||||
.AddParameter("expression", _("Radius (in pixels)"))
|
||||
.SetFunctionName("DrawCircle")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("Line",
|
||||
_("Line"),
|
||||
_("Draw a line on screen"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a line "
|
||||
"(thickness : _PARAM5_) with _PARAM0_"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a line (thickness: _PARAM5_) "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/line24.png",
|
||||
"res/actions/line.png")
|
||||
|
||||
.SetHidden()
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of start point"))
|
||||
.AddParameter("expression", _("Y position of start point"))
|
||||
.AddParameter("expression", _("X position of end point"))
|
||||
.AddParameter("expression", _("Y position of end point"))
|
||||
.AddParameter("expression", _("Thickness (in pixels)"))
|
||||
.SetFunctionName("DrawLine")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("LineV2",
|
||||
_("Line"),
|
||||
_("Draw a line on screen"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a line (thickness: _PARAM5_) "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/line24.png",
|
||||
"res/actions/line.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X Position of start point"))
|
||||
.AddParameter("expression", _("Y Position of start point"))
|
||||
.AddParameter("expression", _("X Position of end point"))
|
||||
.AddParameter("expression", _("Y Position of end point"))
|
||||
.AddParameter("expression", _("Thickness ( in pixels )"))
|
||||
.SetFunctionName("DrawLine")
|
||||
.AddParameter("expression", _("X position of start point"))
|
||||
.AddParameter("expression", _("Y position of start point"))
|
||||
.AddParameter("expression", _("X position of end point"))
|
||||
.AddParameter("expression", _("Y position of end point"))
|
||||
.AddParameter("expression", _("Thickness (in pixels)"))
|
||||
.SetFunctionName("drawLineV2")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("Ellipse",
|
||||
_("Ellipse"),
|
||||
_("Draw an ellipse on screen"),
|
||||
_("Draw at _PARAM1_;_PARAM2_ an ellipse of width _PARAM3_ and height _PARAM4_ "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/ellipse24.png",
|
||||
"res/actions/ellipse.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of center"))
|
||||
.AddParameter("expression", _("Y position of center"))
|
||||
.AddParameter("expression", _("The width of the ellipse"))
|
||||
.AddParameter("expression", _("The height of the ellipse"))
|
||||
.SetFunctionName("DrawEllipse")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("RoundedRectangle",
|
||||
_("Rounded rectangle"),
|
||||
_("Draw a rounded rectangle on screen"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a rounded rectangle (radius: _PARAM5_) "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/roundedRectangle24.png",
|
||||
"res/actions/roundedRectangle.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("Top left side: X position"))
|
||||
.AddParameter("expression", _("Top left side: Y position"))
|
||||
.AddParameter("expression", _("Bottom right side: X position"))
|
||||
.AddParameter("expression", _("Bottom right side: Y position"))
|
||||
.AddParameter("expression", _("Radius (in pixels)"))
|
||||
.SetFunctionName("DrawRoundedRectangle")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("Star",
|
||||
_("Star"),
|
||||
_("Draw a star on screen"),
|
||||
_("Draw at _PARAM1_;_PARAM2_ a star with _PARAM3_ points and radius: _PARAM4_ (inner radius: _PARAM5_, rotation: _PARAM6_) "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/star24.png",
|
||||
"res/actions/star.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of center"))
|
||||
.AddParameter("expression", _("X position of center"))
|
||||
.AddParameter("expression", _("Number of points of the star (minimum: 2)"))
|
||||
.AddParameter("expression", _("Radius (in pixels)"))
|
||||
.AddParameter("expression", _("Inner radius (in pixels, half radius by default)"))
|
||||
.AddParameter("expression", _("Rotation (in degrees)"))
|
||||
.SetFunctionName("DrawStar")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
|
||||
obj.AddAction("Arc",
|
||||
_("Arc"),
|
||||
_("Draw an arc on screen. If \"Close path\" is set to yes, a line will be drawn between the start and end point of the arc, closing the shape."),
|
||||
_("Draw at _PARAM1_;_PARAM2_ an arc with radius: _PARAM3_, start angle: _PARAM4_, end angle: _PARAM5_ (anticlockwise: _PARAM6_, close path: _PARAM7_) "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/arc24.png",
|
||||
"res/actions/arc.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of center"))
|
||||
.AddParameter("expression", _("Y position of center"))
|
||||
.AddParameter("expression", _("Radius (in pixels)"))
|
||||
.AddParameter("expression", _("Start angle of the arc (in degrees)"))
|
||||
.AddParameter("expression", _("End angle of the arc (in degrees)"))
|
||||
.AddParameter("yesorno", _("Anticlockwise"))
|
||||
.AddParameter("yesorno", _("Close path"))
|
||||
.SetFunctionName("DrawArc")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("BezierCurve",
|
||||
_("Bezier curve"),
|
||||
_("Draw a bezier curve on screen"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM7_;_PARAM8_ a bezier curve (first control point: _PARAM3_;_PARAM4_, second control point: _PARAM5_;_PARAM6_) "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/bezierCurve24.png",
|
||||
"res/actions/bezierCurve.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of start point"))
|
||||
.AddParameter("expression", _("Y position of start point"))
|
||||
.AddParameter("expression", _("First control point x"))
|
||||
.AddParameter("expression", _("First control point y"))
|
||||
.AddParameter("expression", _("Second Control point x"))
|
||||
.AddParameter("expression", _("Second Control point y"))
|
||||
.AddParameter("expression", _("Destination point x"))
|
||||
.AddParameter("expression", _("Destination point y"))
|
||||
.SetFunctionName("drawBezierCurve")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("QuadraticCurve",
|
||||
_("Quadratic curve"),
|
||||
_("Draw a quadratic curve on screen"),
|
||||
_("Draw from _PARAM1_;_PARAM2_ to _PARAM5_;_PARAM6_ a quadratic curve (control point: _PARAM3_;_PARAM4_) "
|
||||
"with _PARAM0_"),
|
||||
_("Drawing"),
|
||||
"res/actions/quadraticCurve24.png",
|
||||
"res/actions/quadraticCurve.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of start point"))
|
||||
.AddParameter("expression", _("Y position of start point"))
|
||||
.AddParameter("expression", _("Control point x"))
|
||||
.AddParameter("expression", _("Control point y"))
|
||||
.AddParameter("expression", _("Destination point x"))
|
||||
.AddParameter("expression", _("Destination point y"))
|
||||
.SetFunctionName("drawQuadraticCurve")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("BeginFillPath",
|
||||
_("Begin fill path"),
|
||||
_("Begin to draw a simple one-color fill. Subsequent actions, such as \"Path line\" (in the Advanced category) can be used to draw. Be sure to use \"End fill path\" action when you're done drawing the shape."),
|
||||
_("Begins drawing filling of an advanced path "
|
||||
"with _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/beginFillPath24.png",
|
||||
"res/actions/beginFillPath.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("Start drawing x"))
|
||||
.AddParameter("expression", _("Start drawing y"))
|
||||
.SetFunctionName("beginFillPath")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("EndFillPath",
|
||||
_("End fill path"),
|
||||
_("Finish the filling drawing in an advanced path"),
|
||||
_("Finish the filling drawing in an advanced path "
|
||||
"with _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/endFillPath24.png",
|
||||
"res/actions/endFillPath.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.SetFunctionName("endFillPath")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("MovePathTo",
|
||||
_("Move path drawing position"),
|
||||
_("Move the drawing position for the current path"),
|
||||
_("Move the drawing position of the path to _PARAM1_;_PARAM2_ "
|
||||
"with _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/position24.png",
|
||||
"res/actions/position.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of start point"))
|
||||
.AddParameter("expression", _("Y position of start point"))
|
||||
.SetFunctionName("drawPathMoveTo")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("PathLineTo",
|
||||
_("Path line"),
|
||||
_("Add to a path a line to a position. The origin comes from the previous action or from \"Begin fill path\" or \"Move path drawing position\". By default, the start position will be the object's position."),
|
||||
_("Add to a path a line to the position _PARAM1_;_PARAM2_ "
|
||||
"with _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/line24.png",
|
||||
"res/actions/line.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("X position of start point"))
|
||||
.AddParameter("expression", _("Y position of start point"))
|
||||
.SetFunctionName("drawPathLineTo")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("PathBezierCurveTo",
|
||||
_("Path bezier curve"),
|
||||
_("Add to a path a bezier curve to a position. The origin comes from the previous action or from \"Begin fill path\" or \"Move path drawing position\". By default, the start position will be the object's position."),
|
||||
_("Add to a path a bezier curve to the position _PARAM5_;_PARAM6_ (first control point: _PARAM1_;_PARAM2_, second control point: _PARAM3_;_PARAM4_) "
|
||||
"with _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/bezierCurve24.png",
|
||||
"res/actions/bezierCurve.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("First control point x"))
|
||||
.AddParameter("expression", _("First control point y"))
|
||||
.AddParameter("expression", _("Second Control point x"))
|
||||
.AddParameter("expression", _("Second Control point y"))
|
||||
.AddParameter("expression", _("Destination point x"))
|
||||
.AddParameter("expression", _("Destination point y"))
|
||||
.SetFunctionName("drawPathBezierCurveTo")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("PathArc",
|
||||
_("Path arc"),
|
||||
_("Add to a path an arc to a position. The origin comes from the previous action or from \"Begin fill path\" or \"Move path drawing position\". By default, the start position will be the object's position."),
|
||||
_("Add to a path an arc at the position _PARAM1_;_PARAM2_ (radius: _PARAM3_, start angle: _PARAM4_, end angle: _PARAM5_, anticlockwise: _PARAM6_) "
|
||||
"with _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/arc24.png",
|
||||
"res/actions/arc.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("Center x of circle"))
|
||||
.AddParameter("expression", _("Center y of circle"))
|
||||
.AddParameter("expression", _("Radius (in pixels)"))
|
||||
.AddParameter("expression", _("Start angle"))
|
||||
.AddParameter("expression", _("End angle"))
|
||||
.AddParameter("yesorno", _("Anticlockwise"))
|
||||
.SetFunctionName("drawPathArc")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("PathQuadraticCurveTo",
|
||||
_("Path quadratic curve"),
|
||||
_("Add to a path a quadratic curve to a position. The origin comes from the previous action or from \"Begin fill path\" or \"Move path drawing position\". By default, the start position will be the object's position."),
|
||||
_("Add to a path a quadratic curve to the position _PARAM3_;_PARAM4_ (control point: _PARAM1_;_PARAM2_) "
|
||||
"with _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/quadraticCurve24.png",
|
||||
"res/actions/quadraticCurve.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("expression", _("Control point x"))
|
||||
.AddParameter("expression", _("Control point y"))
|
||||
.AddParameter("expression", _("Destination point x"))
|
||||
.AddParameter("expression", _("Destination point y"))
|
||||
.SetFunctionName("drawPathQuadraticCurveTo")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("closePath",
|
||||
_("Close Path"),
|
||||
_("Close the path of the advanced shape. This closes the outline between the last and the first point."),
|
||||
_("Close the path "
|
||||
"with _PARAM0_"),
|
||||
_("Advanced"),
|
||||
"res/actions/closePath24.png",
|
||||
"res/actions/closePath.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.SetFunctionName("closePath")
|
||||
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
|
||||
|
||||
obj.AddAction("FillColor",
|
||||
_("Fill color"),
|
||||
_("Change the color used when filling"),
|
||||
_("Change fill color of _PARAM0_ to _PARAM1_"),
|
||||
_("Setup"),
|
||||
"res/actions/text24.png",
|
||||
"res/actions/text.png")
|
||||
"res/actions/color24.png",
|
||||
"res/actions/color.png")
|
||||
|
||||
.AddParameter("object", _("Shape Painter object"), "Drawer")
|
||||
.AddParameter("color", _("Fill color"))
|
||||
|
@@ -39,8 +39,72 @@ class PrimitiveDrawingJsExtension : public gd::PlatformExtension {
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Circle"]
|
||||
.SetFunctionName("drawCircle");
|
||||
GetAllActionsForObject("PrimitiveDrawing::Drawer")["PrimitiveDrawing::Line"]
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Line"]
|
||||
.SetFunctionName("drawLine");
|
||||
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::LineV2"]
|
||||
.SetFunctionName("drawLineV2");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Ellipse"]
|
||||
.SetFunctionName("drawEllipse");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::RoundedRectangle"]
|
||||
.SetFunctionName("drawRoundedRectangle");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Star"]
|
||||
.SetFunctionName("drawStar");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Arc"]
|
||||
.SetFunctionName("drawArc");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::BezierCurve"]
|
||||
.SetFunctionName("drawBezierCurve");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::QuadraticCurve"]
|
||||
.SetFunctionName("drawQuadraticCurve");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::BeginFillPath"]
|
||||
.SetFunctionName("beginFillPath");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::EndFillPath"]
|
||||
.SetFunctionName("endFillPath");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathMoveTo"]
|
||||
.SetFunctionName("drawPathMoveTo");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathLineTo"]
|
||||
.SetFunctionName("drawPathLineTo");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathBezierCurveTo"]
|
||||
.SetFunctionName("drawPathBezierCurveTo");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathArc"]
|
||||
.SetFunctionName("drawPathArc");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::PathQuadraticCurveTo"]
|
||||
.SetFunctionName("drawPathQuadraticCurveTo");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::ClosePath"]
|
||||
.SetFunctionName("closePath");
|
||||
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Ellipse"]
|
||||
.SetFunctionName("drawEllipse");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::RoundedRectangle"]
|
||||
.SetFunctionName("drawRoundedRectangle");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Star"]
|
||||
.SetFunctionName("drawStar");
|
||||
// These actions are not exposed yet as the way they work is unsure. See https://github.com/4ian/GDevelop/pull/1256
|
||||
/*GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Arc"]
|
||||
.SetFunctionName("drawArc");
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::ArcTo"]
|
||||
.SetFunctionName("drawArcTo");*/
|
||||
GetAllActionsForObject(
|
||||
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::FillColor"]
|
||||
.SetFunctionName("setFillColor");
|
||||
|
@@ -47,6 +47,101 @@ gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawLine = function(x1, y1,
|
||||
this._graphics.endFill();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawLineV2 = function(x1, y1, x2, y2, thickness) {
|
||||
this._graphics.lineStyle(thickness);
|
||||
this._graphics.moveTo(x1, y1);
|
||||
this._graphics.lineTo(x2,y2);
|
||||
this._graphics.endFill();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawEllipse = function(x1, y1, width, height) {
|
||||
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
|
||||
this._graphics.drawEllipse(x1, y1, width/2, height/2);
|
||||
this._graphics.endFill();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawRoundedRectangle = function(x1, y1, x2, y2, radius) {
|
||||
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
|
||||
this._graphics.drawRoundedRect(x1, y1, x2 - x1, y2 - y1, radius);
|
||||
this._graphics.closePath();
|
||||
this._graphics.endFill();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawStar = function(x1, y1, points, radius, innerRadius, rotation) {
|
||||
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
|
||||
this._graphics.drawStar(x1, y1, points, radius, innerRadius ? innerRadius : radius/2, rotation ? gdjs.toRad(rotation) : 0);
|
||||
this._graphics.closePath();
|
||||
this._graphics.endFill();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawArc = function(x1, y1, radius, startAngle, endAngle, anticlockwise, closePath) {
|
||||
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
|
||||
this._graphics.moveTo( x1 + radius * Math.cos(gdjs.toRad(startAngle)), y1 + radius * Math.sin(gdjs.toRad(startAngle)));
|
||||
this._graphics.arc(x1, y1, radius, gdjs.toRad(startAngle), gdjs.toRad(endAngle), anticlockwise ? true : false);
|
||||
if(closePath){
|
||||
this._graphics.closePath();
|
||||
}
|
||||
this._graphics.endFill();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawBezierCurve = function(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2) {
|
||||
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
|
||||
this._graphics.moveTo(x1, y1);
|
||||
this._graphics.bezierCurveTo(cpX, cpY, cpX2, cpY2, x2, y2);
|
||||
this._graphics.endFill();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawQuadraticCurve = function(x1, y1, cpX, cpY, x2, y2) {
|
||||
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
|
||||
this._graphics.moveTo(x1, y1);
|
||||
this._graphics.quadraticCurveTo(cpX, cpY, x2, y2);
|
||||
this._graphics.endFill();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.beginFillPath = function() {
|
||||
this._graphics.beginFill(this._object._fillColor, this._object._fillOpacity / 255);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.endFillPath = function() {
|
||||
this._graphics.endFill();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathMoveTo = function(x1, y1) {
|
||||
this._graphics.moveTo(x1, y1);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathLineTo = function(x1, y1) {
|
||||
if(this._graphics.graphicsData.length === 0){
|
||||
this._graphics.moveTo(0, 0);
|
||||
}
|
||||
this._graphics.lineTo(x1, y1);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathBezierCurveTo = function(cpX, cpY, cpX2, cpY2, toX, toY) {
|
||||
if(this._graphics.graphicsData.length === 0){
|
||||
this._graphics.moveTo(0, 0);
|
||||
}
|
||||
this._graphics.bezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathArc = function(x1, y1, radius, startAngle, endAngle, anticlockwise) {
|
||||
if(this._graphics.graphicsData.length === 0){
|
||||
this._graphics.moveTo(0, 0);
|
||||
}
|
||||
this._graphics.arc(x1, y1, radius, gdjs.toRad(startAngle), gdjs.toRad(endAngle), anticlockwise ? true : false);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.drawPathQuadraticCurveTo = function(cpX, cpY, toX, toY) {
|
||||
if(this._graphics.graphicsData.length === 0){
|
||||
this._graphics.moveTo(0, 0);
|
||||
}
|
||||
this._graphics.quadraticCurveTo(cpX, cpY, toX, toY);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.closePath = function() {
|
||||
this._graphics.closePath();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObjectPixiRenderer.prototype.updateOutline = function() {
|
||||
this._graphics.lineStyle(
|
||||
this._object._outlineSize,
|
||||
|
@@ -60,6 +60,67 @@ gdjs.ShapePainterRuntimeObject.prototype.drawLine = function(x1, y1, x2, y2, thi
|
||||
this._renderer.drawLine(x1, y1, x2, y2, thickness);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawLineV2 = function(x1, y1, x2, y2, thickness) {
|
||||
this._renderer.drawLineV2(x1, y1, x2, y2, thickness);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawEllipse = function(centerX, centerY, width, height) {
|
||||
this._renderer.drawEllipse(centerX, centerY, width, height);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawRoundedRectangle = function(startX1, startY1, endX2, endY2, radius) {
|
||||
this._renderer.drawRoundedRectangle(startX1, startY1, endX2, endY2, radius);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawStar = function(centerX, centerY, points, radius, innerRadius, rotation) {
|
||||
this._renderer.drawStar(centerX, centerY, points, radius, innerRadius, rotation);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawArc = function(centerX, centerY, radius, startAngle, endAngle, anticlockwise, closePath) {
|
||||
this._renderer.drawArc(centerX, centerY, radius, startAngle, endAngle, anticlockwise, closePath);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawBezierCurve = function(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2) {
|
||||
this._renderer.drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawQuadraticCurve = function(x1, y1, cpX, cpY, x2, y2) {
|
||||
this._renderer.drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.beginFillPath = function(x1, y1) {
|
||||
this._renderer.beginFillPath();
|
||||
this._renderer.drawPathMoveTo(x1, y1);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.endFillPath = function() {
|
||||
this._renderer.endFillPath();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawPathMoveTo = function(x1, y1) {
|
||||
this._renderer.drawPathMoveTo(x1, y1);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawPathLineTo = function(x1, y1, thickness) {
|
||||
this._renderer.drawPathLineTo(x1, y1, thickness);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawPathBezierCurveTo = function(cpX, cpY, cpX2, cpY2, toX, toY) {
|
||||
this._renderer.drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawPathArc = function(cx, cy, radius, startAngle, endAngle, anticlockwise) {
|
||||
this._renderer.drawPathArc(cx, cy, radius, startAngle, endAngle, anticlockwise);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawPathQuadraticCurveTo = function(cpX, cpY, toX, toY) {
|
||||
this._renderer.drawPathQuadraticCurveTo(cpX, cpY, toX, toY);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.closePath = function() {
|
||||
this._renderer.closePath();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.setFillColor = function(rgbColor) {
|
||||
var colors = rgbColor.split(";");
|
||||
if ( colors.length < 3 ) return;
|
||||
|
@@ -42,7 +42,7 @@ void DeclareShopifyExtension(gd::PlatformExtension& extension) {
|
||||
"GetCheckoutUrlForProduct",
|
||||
_("Get the URL for buying a product"),
|
||||
_("Get the URL for buying a product from a shop. The URL will be "
|
||||
"stored in the variable that you specify. You can then use the "
|
||||
"stored in the scene variable that you specify. You can then use the "
|
||||
"action to open an URL to redirect the player to the checkout."),
|
||||
_("Get the URL for product #_PARAM2_ (quantity: _PARAM3_, variant: "
|
||||
"_PARAM4_) from shop _PARAM1_, and store it in _PARAM5_ (or "
|
||||
@@ -60,8 +60,8 @@ void DeclareShopifyExtension(gd::PlatformExtension& extension) {
|
||||
.AddParameter("expression", _("Variant (0 by default)"))
|
||||
.SetDefaultValue("0")
|
||||
.AddParameter("scenevar",
|
||||
_("Variable where the URL for checkout must be stored"))
|
||||
.AddParameter("scenevar", _("Variable containing the error (if any)"));
|
||||
_("Scene variable where the URL for checkout must be stored"))
|
||||
.AddParameter("scenevar", _("Scene variable containing the error (if any)"));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -15,11 +15,25 @@ gdjs.sk.CocosDataLoader.prototype.getData = function(dataName){
|
||||
return cc.loader.getRes("res/"+dataName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the textures from DragonBones data
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @param {Object} objectData
|
||||
*/
|
||||
gdjs.sk.CocosDataLoader.prototype.loadDragonBones = function(runtimeScene, objectData){
|
||||
var textureData = this.getData(objectData.textureDataFilename);
|
||||
var jsonManager = runtimeScene.getGame().getJsonManager();
|
||||
if (!jsonManager.isJsonLoaded(objectData.textureDataFilename)) {
|
||||
console.error("Tried to load DragonBones textures from \"" + objectData.textureDataFilename + "\" but this resource is not loaded.");
|
||||
return;
|
||||
}
|
||||
|
||||
var textureData = jsonManager.getLoadedJson(objectData.textureDataFilename);
|
||||
var texture = runtimeScene.getGame().getImageManager().getTexture(objectData.textureName);
|
||||
if(!textureData || !texture._textureLoaded) return;
|
||||
|
||||
if(!textureData || !texture._textureLoaded) {
|
||||
console.error("Tried to load DragonBones textures from \"" + objectData.textureDataFilename + "\" resource but the texture or the texture data could not be loaded properly.");
|
||||
return;
|
||||
}
|
||||
|
||||
for(var i=0; i<textureData.SubTexture.length; i++){
|
||||
var subTex = textureData.SubTexture[i];
|
||||
var frame = new cc.rect(subTex.x, subTex.y, subTex.width, subTex.height);
|
||||
@@ -29,7 +43,7 @@ gdjs.sk.CocosDataLoader.prototype.loadDragonBones = function(runtimeScene, objec
|
||||
if (subTex.hasOwnProperty("frameHeight")){
|
||||
frame.height = subTex.frameHeight;
|
||||
}
|
||||
|
||||
|
||||
this.textures[subTex.name] = {"texture": texture, "frame": frame};
|
||||
}
|
||||
};
|
||||
@@ -98,7 +112,7 @@ gdjs.sk.SlotCocosRenderer.prototype.loadAsSprite = function(texture){
|
||||
};
|
||||
|
||||
gdjs.sk.SlotCocosRenderer.prototype.loadAsMesh = function(texture, vertices, uvs, triangles){
|
||||
// Meshes not supported, load as sprites
|
||||
// Meshes not supported, load as sprites
|
||||
this.loadAsSprite(texture);
|
||||
};
|
||||
|
||||
|
@@ -11,18 +11,25 @@ gdjs.sk.PixiDataLoader = function()
|
||||
};
|
||||
gdjs.sk.DataLoader = gdjs.sk.PixiDataLoader;
|
||||
|
||||
gdjs.sk.PixiDataLoader.prototype.getData = function(dataName){
|
||||
if(PIXI.loader.resources[dataName]){
|
||||
return PIXI.loader.resources[dataName].data;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the textures from DragonBones data
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @param {Object} objectData
|
||||
*/
|
||||
gdjs.sk.PixiDataLoader.prototype.loadDragonBones = function(runtimeScene, objectData){
|
||||
var textureData = this.getData(objectData.textureDataFilename);
|
||||
var jsonManager = runtimeScene.getGame().getJsonManager();
|
||||
if (!jsonManager.isJsonLoaded(objectData.textureDataFilename)) {
|
||||
console.error("Tried to load DragonBones textures from \"" + objectData.textureDataFilename + "\" but this resource is not loaded.");
|
||||
return;
|
||||
}
|
||||
|
||||
var textureData = jsonManager.getLoadedJson(objectData.textureDataFilename);
|
||||
var texture = runtimeScene.getGame().getImageManager().getPIXITexture(objectData.textureName);
|
||||
if(!textureData || !texture.valid) return;
|
||||
|
||||
if(!textureData || !texture.valid) {
|
||||
console.error("Tried to load DragonBones textures from \"" + objectData.textureDataFilename + "\" resource but the texture or the texture data could not be loaded properly.");
|
||||
return;
|
||||
}
|
||||
|
||||
for(var i=0; i<textureData.SubTexture.length; i++){
|
||||
var subTex = textureData.SubTexture[i];
|
||||
var frame = new PIXI.Rectangle(subTex.x, subTex.y, subTex.width, subTex.height);
|
||||
@@ -37,7 +44,7 @@ gdjs.sk.PixiDataLoader.prototype.loadDragonBones = function(runtimeScene, object
|
||||
if(frame.y > texture.height) frame.y = 0;
|
||||
if(frame.x + frame.width > texture.width) frame.width = texture.width - frame.x;
|
||||
if(frame.y + frame.height > texture.height) frame.height = texture.height - frame.y;
|
||||
|
||||
|
||||
this.textures[subTex.name] = new PIXI.Texture(texture.baseTexture, frame=frame);
|
||||
}
|
||||
};
|
||||
|
@@ -11,18 +11,20 @@ This project is released under the MIT License.
|
||||
#include "SkeletonObject.h"
|
||||
|
||||
void DeclareSkeletonObjectExtension(gd::PlatformExtension& extension) {
|
||||
extension.SetExtensionInformation(
|
||||
"SkeletonObject",
|
||||
_("Skeleton"),
|
||||
_("Enables the use of animated skeleton objects.\nCurrently supported "
|
||||
"formats:\n *DragonBones"),
|
||||
"Franco Maciel",
|
||||
"Open source (MIT License)");
|
||||
extension
|
||||
.SetExtensionInformation("SkeletonObject",
|
||||
_("Skeleton"),
|
||||
_("Enables the use of animated skeleton objects "
|
||||
"made with DragonBones."),
|
||||
"Franco Maciel",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects/skeleton");
|
||||
|
||||
gd::ObjectMetadata& obj = extension.AddObject<SkeletonObject>(
|
||||
"Skeleton",
|
||||
_("Skeleton"),
|
||||
_("Object animated through bones"),
|
||||
_("Object displayed using skeletal animation, powered by DragonBones. "
|
||||
"This object is experimental and searching for a maintainer."),
|
||||
"JsPlatform/Extensions/skeletonicon.png");
|
||||
|
||||
// Object instructions
|
||||
@@ -796,10 +798,10 @@ void DeclareSkeletonObjectExtension(gd::PlatformExtension& extension) {
|
||||
.AddParameter("expression", _("Ray maximum distance (in pixels)"))
|
||||
.AddParameter(
|
||||
"scenevar",
|
||||
_("Variable where to store the X position of the intersection"))
|
||||
_("Scene variable where to store the X position of the intersection"))
|
||||
.AddParameter(
|
||||
"scenevar",
|
||||
_("Variable where to store the Y position of the intersection"))
|
||||
_("Scene variable where to store the Y position of the intersection"))
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.MarkAsAdvanced();
|
||||
}
|
||||
|
@@ -26,14 +26,23 @@ gdjs.SkeletonObjectsManager.prototype.getSkeleton = function(runtimeScene, objec
|
||||
return this.loadedObjects[objectName];
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the Skeleton data
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @param {Object} objectData
|
||||
*/
|
||||
gdjs.SkeletonObjectsManager.prototype.loadSkeleton = function(runtimeScene, objectData){
|
||||
var loader = new gdjs.sk.DataLoader();
|
||||
var skeletalData = loader.getData(objectData.skeletalDataFilename);
|
||||
var skeleton = {"loader": loader,
|
||||
"armatures": [],
|
||||
"rootArmature": 0};
|
||||
|
||||
if(!skeletalData) return skeleton;
|
||||
|
||||
var jsonManager = runtimeScene.getGame().getJsonManager();
|
||||
var skeletalData = jsonManager.getLoadedJson(objectData.skeletalDataFilename);
|
||||
if(!skeletalData) {
|
||||
console.error("Tried to load a Skeleton file from \"" + objectData.skeletalDataFilename + "\" but this resource is not loaded.");
|
||||
return skeleton;
|
||||
}
|
||||
|
||||
if(objectData.apiName === "DragonBones"){
|
||||
// Load sub-textures
|
||||
|
@@ -7,8 +7,8 @@ This project is released under the MIT License.
|
||||
|
||||
#include "SkeletonObject.h"
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCpp/Runtime/CommonTools.h"
|
||||
@@ -52,10 +52,19 @@ void SkeletonObject::DoSerializeTo(gd::SerializerElement& element) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> SkeletonObject::GetProperties(
|
||||
gd::Project& project) const {
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[_("Skeletal data filename")].SetValue(skeletalDataFilename);
|
||||
properties[_("Skeletal data filename")]
|
||||
.SetValue(skeletalDataFilename)
|
||||
.SetType("resource")
|
||||
.AddExtraInfo("json");
|
||||
properties[_("Main armature name")].SetValue(rootArmatureName);
|
||||
properties[_("Texture data filename")].SetValue(textureDataFilename);
|
||||
properties[_("Texture")].SetValue(textureName);
|
||||
properties[_("Texture data filename")]
|
||||
.SetValue(textureDataFilename)
|
||||
.SetType("resource")
|
||||
.AddExtraInfo("json");
|
||||
properties[_("Texture")]
|
||||
.SetValue(textureName)
|
||||
.SetType("resource")
|
||||
.AddExtraInfo("image");
|
||||
properties[_("API")].SetValue(apiName).SetType("Choice").AddExtraInfo(
|
||||
"DragonBones");
|
||||
properties[_("Debug Polygons")]
|
||||
|
@@ -28,6 +28,20 @@ void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
|
||||
|
||||
.SetFunctionName("SystemInfo::IsMobile")
|
||||
.SetIncludeFile("SystemInfo/SystemInfoTools.h");
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"IsWebGLSupported",
|
||||
_("Is WebGL supported"),
|
||||
_("Check if GPU accelerated WebGL is supported on the target device."),
|
||||
_("WebGL is available"),
|
||||
_("System information"),
|
||||
"CppPlatform/Extensions/systeminfoicon24.png",
|
||||
"CppPlatform/Extensions/systeminfoicon16.png")
|
||||
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SystemInfo::IsWebGLSupported")
|
||||
.SetIncludeFile("SystemInfo/SystemInfoTools.h");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -27,6 +27,10 @@ class SystemInfoJsExtension : public gd::PlatformExtension {
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
|
||||
.SetFunctionName("gdjs.evtTools.systemInfo.isMobile");
|
||||
GetAllConditions()["SystemInfo::IsWebGLSupported"]
|
||||
.codeExtraInformation
|
||||
.SetIncludeFile("Extensions/SystemInfo/systeminfotools.js")
|
||||
.SetFunctionName("gdjs.evtTools.systemInfo.isWebGLSupported");
|
||||
|
||||
StripUnimplementedInstructionsAndExpressions();
|
||||
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
|
||||
|
@@ -28,3 +28,12 @@ gdjs.evtTools.systemInfo.isMobile = function() {
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the the device supports WebGL.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @returns {boolean} true if WebGL is supported
|
||||
*/
|
||||
gdjs.evtTools.systemInfo.isWebGLSupported = function(runtimeScene) {
|
||||
return runtimeScene.getGame().getRenderer().isWebGLSupported();
|
||||
};
|
@@ -500,7 +500,9 @@ module.exports = {
|
||||
registerEditorConfigurations: function(objectsEditorService) {
|
||||
objectsEditorService.registerEditorConfiguration(
|
||||
"Video::VideoObject",
|
||||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor()
|
||||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||||
helpPagePath: "/objects/video"
|
||||
})
|
||||
);
|
||||
},
|
||||
/**
|
||||
|
@@ -28,6 +28,8 @@ SceneExtension::SceneExtension() {
|
||||
|
||||
GetAllConditions()["DepartScene"].SetFunctionName(
|
||||
"gdjs.evtTools.runtimeScene.sceneJustBegins");
|
||||
GetAllConditions()["SceneJustResumed"].SetFunctionName(
|
||||
"gdjs.evtTools.runtimeScene.sceneJustResumed");
|
||||
GetAllActions()["SceneBackground"].SetFunctionName(
|
||||
"gdjs.evtTools.runtimeScene.setBackgroundColor");
|
||||
GetAllActions()["Scene"].SetFunctionName(
|
||||
|
@@ -3,6 +3,7 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDJS/IDE/Exporter.h"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
@@ -22,7 +23,6 @@
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
#include "GDJS/Events/CodeGeneration/EventsCodeGenerator.h"
|
||||
#include "GDJS/IDE/Exporter.h"
|
||||
#include "GDJS/IDE/ExporterHelper.h"
|
||||
#undef CopyFile // Disable an annoying macro
|
||||
|
||||
@@ -135,8 +135,7 @@ bool Exporter::ExportWholePixiProject(
|
||||
|
||||
if (!exportProject(exportDir + "/www")) return false;
|
||||
|
||||
if (!helper.ExportCordovaConfigFile(exportedProject, exportDir))
|
||||
return false;
|
||||
if (!helper.ExportCordovaFiles(exportedProject, exportDir)) return false;
|
||||
} else if (exportOptions["exportForElectron"]) {
|
||||
fs.MkDir(exportDir);
|
||||
|
||||
@@ -170,8 +169,7 @@ bool Exporter::ExportWholeCocos2dProject(gd::Project& project,
|
||||
|
||||
// Export the resources (before generating events as some resources filenames
|
||||
// may be updated)
|
||||
helper.ExportResources(
|
||||
fs, exportedProject, exportDir + "/res");
|
||||
helper.ExportResources(fs, exportedProject, exportDir + "/res");
|
||||
|
||||
// Export engine libraries
|
||||
helper.AddLibsInclude(false, true, false, includesFiles);
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
|
@@ -3,6 +3,7 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDJS/IDE/ExporterHelper.h"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
@@ -23,7 +24,6 @@
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
#include "GDJS/Events/CodeGeneration/EventsCodeGenerator.h"
|
||||
#include "GDJS/IDE/ExporterHelper.h"
|
||||
#undef CopyFile // Disable an annoying macro
|
||||
|
||||
namespace gdjs {
|
||||
@@ -200,8 +200,8 @@ bool ExporterHelper::ExportPixiIndexFile(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportCordovaConfigFile(const gd::Project &project,
|
||||
gd::String exportDir) {
|
||||
bool ExporterHelper::ExportCordovaFiles(const gd::Project &project,
|
||||
gd::String exportDir) {
|
||||
auto &platformSpecificAssets = project.GetPlatformSpecificAssets();
|
||||
auto &resourceManager = project.GetResourcesManager();
|
||||
auto getIconFilename = [&resourceManager, &platformSpecificAssets](
|
||||
@@ -281,10 +281,35 @@ bool ExporterHelper::ExportCordovaConfigFile(const gd::Project &project,
|
||||
}
|
||||
|
||||
if (!fs.WriteToFile(exportDir + "/config.xml", str)) {
|
||||
lastError = "Unable to write configuration file.";
|
||||
lastError = "Unable to write Cordova config.xml file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
gd::String jsonName =
|
||||
gd::Serializer::ToJSON(gd::SerializerElement(project.GetName()));
|
||||
gd::String jsonAuthor =
|
||||
gd::Serializer::ToJSON(gd::SerializerElement(project.GetAuthor()));
|
||||
gd::String jsonVersion =
|
||||
gd::Serializer::ToJSON(gd::SerializerElement(project.GetVersion()));
|
||||
gd::String jsonMangledName = gd::Serializer::ToJSON(gd::SerializerElement(
|
||||
gd::SceneNameMangler::GetMangledSceneName(project.GetName())
|
||||
.LowerCase()
|
||||
.FindAndReplace(" ", "-")));
|
||||
|
||||
{
|
||||
gd::String str =
|
||||
fs.ReadFile(gdjsRoot + "/Runtime/Cordova/package.json")
|
||||
.FindAndReplace("\"GDJS_GAME_NAME\"", jsonName)
|
||||
.FindAndReplace("\"GDJS_GAME_AUTHOR\"", jsonAuthor)
|
||||
.FindAndReplace("\"GDJS_GAME_VERSION\"", jsonVersion)
|
||||
.FindAndReplace("\"GDJS_GAME_MANGLED_NAME\"", jsonMangledName);
|
||||
|
||||
if (!fs.WriteToFile(exportDir + "/package.json", str)) {
|
||||
lastError = "Unable to write Cordova package.json file.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -656,10 +681,12 @@ bool ExporterHelper::ExportExternalSourceFiles(
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportIncludesAndLibs(
|
||||
std::vector<gd::String> &includesFiles, gd::String exportDir, bool /*minify*/) {
|
||||
std::vector<gd::String> &includesFiles,
|
||||
gd::String exportDir,
|
||||
bool /*minify*/) {
|
||||
for (std::vector<gd::String>::iterator include = includesFiles.begin();
|
||||
include != includesFiles.end();
|
||||
++include) {
|
||||
include != includesFiles.end();
|
||||
++include) {
|
||||
if (!fs.IsAbsolute(*include)) {
|
||||
gd::String source = gdjsRoot + "/Runtime/" + *include;
|
||||
if (fs.FileExists(source)) {
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
@@ -173,8 +174,7 @@ class ExporterHelper {
|
||||
* \param project The project to be used to generate the configuration file.
|
||||
* \param exportDir The directory where the config.xml must be created.
|
||||
*/
|
||||
bool ExportCordovaConfigFile(const gd::Project &project,
|
||||
gd::String exportDir);
|
||||
bool ExportCordovaFiles(const gd::Project &project, gd::String exportDir);
|
||||
|
||||
/**
|
||||
* \brief Generate the base Cocos2d files.
|
||||
|
14
GDJS/Runtime/Cordova/package.json
Normal file
14
GDJS/Runtime/Cordova/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "GDJS_GAME_MANGLED_NAME",
|
||||
"displayName": "GDJS_GAME_NAME",
|
||||
"version": "GDJS_GAME_VERSION",
|
||||
"description": "GDJS_GAME_NAME",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"ecosystem:cordova"
|
||||
],
|
||||
"author": "GDJS_GAME_AUTHOR"
|
||||
}
|
@@ -119,6 +119,14 @@ gdjs.RuntimeGameCocosRenderer.prototype.getCanvas = function() {
|
||||
return cc.game.canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the device supports WebGL.
|
||||
* @returns {boolean} true if WebGL is supported
|
||||
*/
|
||||
gdjs.RuntimeGameCocosRenderer.prototype.isWebGLSupported = function() {
|
||||
return cc._renderType === cc.game.RENDER_TYPE_WEBGL;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the electron module, if running as a electron renderer process.
|
||||
*/
|
||||
@@ -128,4 +136,4 @@ gdjs.RuntimeGameCocosRenderer.prototype.getElectron = function() {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -48,8 +48,6 @@ gdjs.SpriteRuntimeObjectCocosRenderer.prototype._updateCocosSprite = function()
|
||||
));
|
||||
var xPos = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
|
||||
var yPos = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
|
||||
if ( this._object._flippedX ) xPos += (this._cachedTextureWidth/2-this._object._animationFrame.center.x)*Math.abs(this._object._scaleX)*2;
|
||||
if ( this._object._flippedY ) yPos += (this._cachedTextureHeight/2-this._object._animationFrame.center.y)*Math.abs(this._object._scaleY)*2;
|
||||
this._sprite.setPositionX(xPos);
|
||||
this._sprite.setPositionY(this._convertYPosition(yPos));
|
||||
this._sprite.setRotation(this._object.angle);
|
||||
@@ -87,13 +85,11 @@ gdjs.SpriteRuntimeObjectCocosRenderer.prototype.update = function() {
|
||||
|
||||
gdjs.SpriteRuntimeObjectCocosRenderer.prototype.updateX = function() {
|
||||
var xPos = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
|
||||
if ( this._object._flippedX ) xPos += (this._cachedTextureWidth/2-this._object._animationFrame.center.x)*Math.abs(this._object._scaleX)*2;
|
||||
this._sprite.setPositionX(xPos);
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectCocosRenderer.prototype.updateY = function() {
|
||||
var yPos = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
|
||||
if ( this._object._flippedY ) yPos += (this._cachedTextureHeight/2-this._object._animationFrame.center.y)*Math.abs(this._object._scaleY)*2;
|
||||
this._sprite.setPositionY(this._convertYPosition(yPos));
|
||||
}
|
||||
|
||||
|
@@ -60,7 +60,7 @@ gdjs.evtTools.object.pickOnly = function(objectsLists, runtimeObject) {
|
||||
* + Cost(Testing NbObjList1+NbObjList2 booleans)
|
||||
*
|
||||
*
|
||||
* @param {gdjsTwoListsTestPredicate} predicate The predicate function is called with the two objects to compare, and an optional argument `extraArg`
|
||||
* @param {gdjsTwoListsTestPredicate} predicate The predicate function is called with the two objects to compare, and an optional argument `extraArg`
|
||||
* @param {Hashtable} objectsLists1 e.g. Hashtable.newFrom({ A: objects1 });
|
||||
* @param {Hashtable} objectsLists2 e.g. Hashtable.newFrom({ B: objects2 });
|
||||
* @param {boolean} inverted If `inverted` == true, only the objects of the first table are filtered.
|
||||
@@ -228,8 +228,8 @@ gdjs.evtTools.object.distanceTest = function(objectsLists1, objectsLists2, dista
|
||||
gdjs.evtTools.object._movesToward = function(obj1, obj2, tolerance) {
|
||||
if ( obj1.hasNoForces() ) return false;
|
||||
|
||||
var objAngle = Math.atan2(obj2.getY()+obj2.getCenterY() - (obj1.getY()+obj1.getCenterY()),
|
||||
obj2.getX()+obj2.getCenterX() - (obj1.getX()+obj1.getCenterX()));
|
||||
var objAngle = Math.atan2(obj2.getDrawableY()+obj2.getCenterY() - (obj1.getDrawableY()+obj1.getCenterY()),
|
||||
obj2.getDrawableX()+obj2.getCenterX() - (obj1.getDrawableX()+obj1.getCenterX()));
|
||||
objAngle *= 180/3.14159;
|
||||
|
||||
return Math.abs(gdjs.evtTools.common.angleDifference(obj1.getAverageForce().getAngle(), objAngle)) <= tolerance/2;
|
||||
@@ -241,8 +241,8 @@ gdjs.evtTools.object.movesTowardTest = function(objectsLists1, objectsLists2, to
|
||||
};
|
||||
|
||||
gdjs.evtTools.object._turnedToward = function(obj1, obj2, tolerance) {
|
||||
var objAngle = Math.atan2(obj2.getY()+obj2.getCenterY() - (obj1.getY()+obj1.getCenterY()),
|
||||
obj2.getX()+obj2.getCenterX() - (obj1.getX()+obj1.getCenterX()));
|
||||
var objAngle = Math.atan2(obj2.getDrawableY()+obj2.getCenterY() - (obj1.getDrawableY()+obj1.getCenterY()),
|
||||
obj2.getDrawableX()+obj2.getCenterX() - (obj1.getDrawableX()+obj1.getCenterX()));
|
||||
objAngle *= 180/3.14159;
|
||||
|
||||
return Math.abs(gdjs.evtTools.common.angleDifference(obj1.getAngle(), objAngle)) <= tolerance/2;
|
||||
@@ -276,10 +276,10 @@ gdjs.evtTools.object.pickRandomObject = function(runtimeScene, objectsLists) {
|
||||
objectsCount += list.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (objectsCount === 0)
|
||||
|
||||
if (objectsCount === 0)
|
||||
return false;
|
||||
|
||||
|
||||
// Pick one random object
|
||||
var index = Math.floor(Math.random()*objectsCount);
|
||||
if (index >= objectsCount) index = objectsCount-1; //Should never happen.
|
||||
@@ -299,7 +299,7 @@ gdjs.evtTools.object.pickRandomObject = function(runtimeScene, objectsLists) {
|
||||
startIndex += list.length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gdjs.evtTools.object.pickOnly(objectsLists, theChosenOne);
|
||||
return true;
|
||||
};
|
||||
@@ -355,7 +355,7 @@ gdjs.evtTools.object.raycastObjectToPosition = function(objectsLists, x, y, endX
|
||||
for (var j = 0; j < list.length; j++) {
|
||||
var object = list[j];
|
||||
var result = object.raycastTest(x, y, endX, endY, !inverted);
|
||||
|
||||
|
||||
if( result.collision ) {
|
||||
if ( !inverted && (result.closeSqDist <= testSqDist) ) {
|
||||
testSqDist = result.closeSqDist;
|
||||
|
@@ -17,6 +17,10 @@ gdjs.evtTools.runtimeScene.sceneJustBegins = function(runtimeScene) {
|
||||
return runtimeScene.getTimeManager().isFirstFrame();
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.sceneJustResumed = function(runtimeScene) {
|
||||
return runtimeScene.sceneJustResumed();
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.getSceneName = function(runtimeScene) {
|
||||
return runtimeScene.getName();
|
||||
};
|
||||
|
@@ -18,6 +18,58 @@
|
||||
*/
|
||||
gdjs.JsonManager = function(resources) {
|
||||
this._resources = resources;
|
||||
|
||||
/** @type Object.<string, Object> */
|
||||
this._loadedJsons = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback called when a json is preloaded
|
||||
* @callback JsonManagerOnProgressCallback
|
||||
* @param {number} loaded The number of json files loaded so far
|
||||
* @param {number} total The total number to be loaded
|
||||
* @returns {undefined} Nothing
|
||||
*/
|
||||
|
||||
/**
|
||||
* The callback called when all jsons are preloaded
|
||||
* @callback JsonManagerOnCompleteCallback
|
||||
* @param {number} total The total number to be loaded
|
||||
* @returns {undefined} Nothing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Request all the json resources to be preloaded (unless they are marked as not preloaded).
|
||||
*
|
||||
* @param {JsonManagerOnProgressCallback} onProgress The function called after each json is loaded.
|
||||
* @param {JsonManagerOnCompleteCallback} onComplete The function called when all jsons are loaded.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.preloadJsons = function(onProgress, onComplete) {
|
||||
var resources = this._resources;
|
||||
|
||||
var jsonResources = resources.filter(function(resource) {
|
||||
return resource.kind === 'json' && !resource.disablePreload;
|
||||
});
|
||||
if (jsonResources.length === 0) return onComplete(jsonResources.length);
|
||||
|
||||
var loaded = 0;
|
||||
/** @type JsonManagerRequestCallback */
|
||||
var onLoad = function(error, jsonContent) {
|
||||
if (error) {
|
||||
console.error("Error while preloading a json resource:" + error);
|
||||
}
|
||||
|
||||
loaded++;
|
||||
if (loaded === jsonResources.length) {
|
||||
return onComplete(jsonResources.length);
|
||||
}
|
||||
|
||||
onProgress(loaded, jsonResources.length);
|
||||
};
|
||||
|
||||
for (var i = 0; i < jsonResources.length; ++i) {
|
||||
this.loadJson(jsonResources[i].name, onLoad);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -30,16 +82,16 @@ gdjs.JsonManager = function(resources) {
|
||||
|
||||
/**
|
||||
* Request the json file from the given resource name.
|
||||
* When loaded, the `callback` is called with the error (null if none) and the loaded
|
||||
* json (string).
|
||||
* This method is asynchronous. When loaded, the `callback` is called with the error
|
||||
* (null if none) and the loaded json (a JS Object).
|
||||
*
|
||||
* @param {string} resourceName The resource pointing to the json file to load.
|
||||
* @param {JsonManagerRequestCallback} callback The callback function called when json is loaded (or an error occured).
|
||||
*/
|
||||
gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
|
||||
var resource = this._resources.find(
|
||||
resource => resource.kind === 'json' && resource.name === resourceName
|
||||
);
|
||||
var resource = this._resources.find(function(resource) {
|
||||
return resource.kind === 'json' && resource.name === resourceName;
|
||||
});
|
||||
if (!resource) {
|
||||
callback(
|
||||
new Error(
|
||||
@@ -52,6 +104,7 @@ gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
xhr.open('GET', resource.file);
|
||||
@@ -64,6 +117,9 @@ gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the result
|
||||
that._loadedJsons[resourceName] = xhr.response;
|
||||
|
||||
callback(null, xhr.response);
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
@@ -74,3 +130,23 @@ gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given json resource was loaded (preloaded or loaded with `loadJson`).
|
||||
* @param {string} resourceName The name of the json resource.
|
||||
* @returns {boolean} true if the content of the json resource is loaded. false otherwise.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.isJsonLoaded = function(resourceName) {
|
||||
return !!this._loadedJsons[resourceName];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the object for the given resource that is already loaded (preloaded or loaded with `loadJson`).
|
||||
* If the resource is not loaded, `null` will be returned.
|
||||
*
|
||||
* @param {string} resourceName The name of the json resource.
|
||||
* @returns {?Object} the content of the json resource, if loaded. `null` otherwise.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.getLoadedJson = function(resourceName) {
|
||||
return this._loadedJsons[resourceName] || null;
|
||||
};
|
||||
|
@@ -1,5 +1,11 @@
|
||||
gdjs.LoadingScreenPixiRenderer = function(runtimeGamePixiRenderer, loadingScreenSetup) {
|
||||
this._pixiRenderer = runtimeGamePixiRenderer.getPIXIRenderer();
|
||||
if (!this._pixiRenderer) {
|
||||
// A PIXI Renderer can be missing during tests, when creating a runtime game
|
||||
// without a canvas.
|
||||
return;
|
||||
}
|
||||
|
||||
this._loadingScreen = new PIXI.Container();
|
||||
|
||||
this._progressText = new PIXI.Text(' ', {
|
||||
@@ -42,6 +48,10 @@ gdjs.LoadingScreenPixiRenderer = function(runtimeGamePixiRenderer, loadingScreen
|
||||
gdjs.LoadingScreenRenderer = gdjs.LoadingScreenPixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.LoadingScreenPixiRenderer.prototype.render = function(percent) {
|
||||
if (!this._pixiRenderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
var screenBorder = 10;
|
||||
|
||||
if (this._madeWithText) {
|
||||
|
@@ -426,6 +426,14 @@ gdjs.RuntimeGamePixiRenderer.prototype.getCanvas = function() {
|
||||
return this._pixiRenderer.view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the device supports WebGL.
|
||||
* @returns {boolean} true if WebGL is supported
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.isWebGLSupported = function() {
|
||||
return this._pixiRenderer.type === PIXI.RENDERER_TYPE.WEBGL;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the electron module, if running as a electron renderer process.
|
||||
*/
|
||||
|
@@ -33,8 +33,6 @@ gdjs.SpriteRuntimeObjectPixiRenderer.prototype._updatePIXISprite = function() {
|
||||
this._sprite.anchor.y = this._object._animationFrame.center.y/this._sprite.texture.frame.height;
|
||||
this._sprite.position.x = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
|
||||
this._sprite.position.y = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
|
||||
if ( this._object._flippedX ) this._sprite.position.x += (this._sprite.texture.frame.width/2-this._object._animationFrame.center.x)*Math.abs(this._object._scaleX)*2;
|
||||
if ( this._object._flippedY ) this._sprite.position.y += (this._sprite.texture.frame.height/2-this._object._animationFrame.center.y)*Math.abs(this._object._scaleY)*2;
|
||||
this._sprite.rotation = gdjs.toRad(this._object.angle);
|
||||
this._sprite.visible = !this._object.hidden;
|
||||
this._sprite.blendMode = this._object._blendMode;
|
||||
@@ -74,14 +72,10 @@ gdjs.SpriteRuntimeObjectPixiRenderer.prototype.update = function() {
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateX = function() {
|
||||
this._sprite.position.x = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
|
||||
if ( this._object._flippedX )
|
||||
this._sprite.position.x += (this._sprite.texture.frame.width/2-this._object._animationFrame.center.x)*Math.abs(this._object._scaleX)*2;
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateY = function() {
|
||||
this._sprite.position.y = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
|
||||
if ( this._object._flippedY )
|
||||
this._sprite.position.y += (this._sprite.texture.frame.height/2-this._object._animationFrame.center.y)*Math.abs(this._object._scaleY)*2;
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateAngle = function() {
|
||||
|
@@ -296,24 +296,42 @@ gdjs.RuntimeGame.prototype.loadAllAssets = function(
|
||||
function(texturesTotalCount) {
|
||||
that._soundManager.preloadAudio(
|
||||
function(count, total) {
|
||||
loadingScreen.render(
|
||||
Math.floor(((texturesTotalCount + count) / allAssetsTotal) * 100)
|
||||
var percent = Math.floor(
|
||||
((texturesTotalCount + count) / allAssetsTotal) * 100
|
||||
);
|
||||
loadingScreen.render(percent);
|
||||
if (progressCallback) progressCallback(percent);
|
||||
},
|
||||
function(audioTotalCount) {
|
||||
that._fontManager.loadFonts(
|
||||
function(count, total) {
|
||||
loadingScreen.render(
|
||||
Math.floor(
|
||||
((texturesTotalCount + audioTotalCount + count) /
|
||||
allAssetsTotal) *
|
||||
100
|
||||
)
|
||||
var percent = Math.floor(
|
||||
((texturesTotalCount + audioTotalCount + count) /
|
||||
allAssetsTotal) *
|
||||
100
|
||||
);
|
||||
loadingScreen.render(percent);
|
||||
if (progressCallback) progressCallback(percent);
|
||||
},
|
||||
function() {
|
||||
loadingScreen.unload();
|
||||
callback();
|
||||
function(fontTotalCount) {
|
||||
that._jsonManager.preloadJsons(
|
||||
function(count, total) {
|
||||
var percent = Math.floor(
|
||||
((texturesTotalCount +
|
||||
audioTotalCount +
|
||||
fontTotalCount +
|
||||
count) /
|
||||
allAssetsTotal) *
|
||||
100
|
||||
);
|
||||
loadingScreen.render(percent);
|
||||
if (progressCallback) progressCallback(percent);
|
||||
},
|
||||
function() {
|
||||
loadingScreen.unload();
|
||||
callback();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -274,10 +274,11 @@ gdjs.RuntimeObject.prototype.getY = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the X position of the rendered object.<br>
|
||||
* Get the X position of the rendered object.
|
||||
*
|
||||
* For most objects, this will returns the same value as getX(). But if the object
|
||||
* has an origin this.is not the same as the point (0,0) of the object displayed,
|
||||
* getDrawableX will differs.
|
||||
* has an origin that is not the same as the point (0,0) of the object displayed,
|
||||
* getDrawableX will differ.
|
||||
*
|
||||
* @return {number} The X position of the rendered object.
|
||||
*/
|
||||
@@ -286,10 +287,11 @@ gdjs.RuntimeObject.prototype.getDrawableX = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the Y position of the rendered object.<br>
|
||||
* Get the Y position of the rendered object.
|
||||
*
|
||||
* For most objects, this will returns the same value as getY(). But if the object
|
||||
* has an origin this.is not the same as the point (0,0) of the object displayed,
|
||||
* getDrawableY will differs.
|
||||
* has an origin that is not the same as the point (0,0) of the object displayed,
|
||||
* getDrawableY will differ.
|
||||
*
|
||||
* @return {number} The Y position of the rendered object.
|
||||
*/
|
||||
@@ -530,7 +532,7 @@ gdjs.RuntimeObject.prototype.hasVariable = function(name) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide or show the object
|
||||
* Hide (or show) the object.
|
||||
* @param {boolean} enable Set it to true to hide the object, false to show it.
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.hide = function(enable) {
|
||||
@@ -540,6 +542,11 @@ gdjs.RuntimeObject.prototype.hide = function(enable) {
|
||||
|
||||
/**
|
||||
* Return true if the object is not hidden.
|
||||
*
|
||||
* @note This is unrelated to the actual visibility of the objec on the screen.
|
||||
* For this, see `getVisibilityAABB` to get the bounding boxes of the object as displayed
|
||||
* on the scene.
|
||||
*
|
||||
* @return {boolean} true if the object is not hidden.
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.isVisible = function() {
|
||||
@@ -555,7 +562,7 @@ gdjs.RuntimeObject.prototype.isHidden = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the object
|
||||
* Return the width of the object.
|
||||
* @return {number} The width of the object
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.getWidth = function() {
|
||||
@@ -563,7 +570,7 @@ gdjs.RuntimeObject.prototype.getWidth = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the object
|
||||
* Return the width of the object.
|
||||
* @return {number} The height of the object
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.getHeight = function() {
|
||||
@@ -571,16 +578,16 @@ gdjs.RuntimeObject.prototype.getHeight = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the X position of the object center, relative to the object position.
|
||||
* @return {number} the X position of the object center
|
||||
* Return the X position of the object center, **relative to the object X position** (`getDrawableX`).
|
||||
* @return {number} the X position of the object center, relative to `getDrawableX()`.
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.getCenterX = function() {
|
||||
return this.getWidth() / 2;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the Y position of the object center, relative to the object position.
|
||||
* @return {number} the Y position of the object center
|
||||
* Return the Y position of the object center, **relative to the object position** (`getDrawableY`).
|
||||
* @return {number} the Y position of the object center, relative to `getDrawableY()`.
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.getCenterY = function() {
|
||||
return this.getHeight() / 2;
|
||||
@@ -777,32 +784,28 @@ gdjs.RuntimeObject.prototype.updateHitBoxes = function() {
|
||||
var centerX = this.getCenterX();
|
||||
var centerY = this.getCenterY();
|
||||
|
||||
if (this.getCenterX() === width / 2 && this.getCenterY() === height / 2) {
|
||||
this.hitBoxes[0].vertices[0][0] =-width/2.0;
|
||||
this.hitBoxes[0].vertices[0][1] =-height/2.0;
|
||||
this.hitBoxes[0].vertices[1][0] =+width/2.0;
|
||||
this.hitBoxes[0].vertices[1][1] =-height/2.0;
|
||||
this.hitBoxes[0].vertices[2][0] =+width/2.0;
|
||||
this.hitBoxes[0].vertices[2][1] =+height/2.0;
|
||||
this.hitBoxes[0].vertices[3][0] =-width/2.0;
|
||||
this.hitBoxes[0].vertices[3][1] =+height/2.0;
|
||||
|
||||
this.hitBoxes[0].rotate(this.getAngle()/180*Math.PI);
|
||||
this.hitBoxes[0].move(this.getDrawableX()+this.getCenterX(), this.getDrawableY()+this.getCenterY());
|
||||
if (centerX === width / 2 && centerY === height / 2) {
|
||||
this.hitBoxes[0].vertices[0][0] = - centerX;
|
||||
this.hitBoxes[0].vertices[0][1] = - centerY;
|
||||
this.hitBoxes[0].vertices[1][0] = + centerX;
|
||||
this.hitBoxes[0].vertices[1][1] = - centerY;
|
||||
this.hitBoxes[0].vertices[2][0] = + centerX;
|
||||
this.hitBoxes[0].vertices[2][1] = + centerY;
|
||||
this.hitBoxes[0].vertices[3][0] = - centerX;
|
||||
this.hitBoxes[0].vertices[3][1] = + centerY;
|
||||
} else {
|
||||
this.hitBoxes[0].vertices[0][0] = 0;
|
||||
this.hitBoxes[0].vertices[0][1] = 0;
|
||||
this.hitBoxes[0].vertices[1][0] = width;
|
||||
this.hitBoxes[0].vertices[1][1] = 0;
|
||||
this.hitBoxes[0].vertices[2][0] = width;
|
||||
this.hitBoxes[0].vertices[2][1] = height;
|
||||
this.hitBoxes[0].vertices[3][0] = 0;
|
||||
this.hitBoxes[0].vertices[3][1] = height;
|
||||
|
||||
this.hitBoxes[0].move(-centerX, -centerY);
|
||||
this.hitBoxes[0].rotate(this.getAngle()/180*Math.PI);
|
||||
this.hitBoxes[0].move(this.getDrawableX()+centerX, this.getDrawableY()+centerY);
|
||||
this.hitBoxes[0].vertices[0][0] = 0 - centerX;
|
||||
this.hitBoxes[0].vertices[0][1] = 0 - centerY;
|
||||
this.hitBoxes[0].vertices[1][0] = width - centerX;
|
||||
this.hitBoxes[0].vertices[1][1] = 0 - centerY;
|
||||
this.hitBoxes[0].vertices[2][0] = width - centerX;
|
||||
this.hitBoxes[0].vertices[2][1] = height - centerY;
|
||||
this.hitBoxes[0].vertices[3][0] = 0 - centerX;
|
||||
this.hitBoxes[0].vertices[3][1] = height - centerY;
|
||||
}
|
||||
|
||||
this.hitBoxes[0].rotate(this.getAngle()/180*Math.PI);
|
||||
this.hitBoxes[0].move(this.getDrawableX()+centerX, this.getDrawableY()+centerY);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1288,6 +1291,8 @@ gdjs.RuntimeObject.prototype.separateObjectsWithForces = function(objectsLists,
|
||||
* @return {boolean} true if obj1 and obj2 are in collision
|
||||
*/
|
||||
gdjs.RuntimeObject.collisionTest = function(obj1, obj2, ignoreTouchingEdges) {
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
// is not necessarily in the middle of the object (for sprites for example).
|
||||
//First check if bounding circle are too far.
|
||||
var o1w = obj1.getWidth();
|
||||
var o1h = obj1.getHeight();
|
||||
@@ -1325,6 +1330,8 @@ gdjs.RuntimeObject.collisionTest = function(obj1, obj2, ignoreTouchingEdges) {
|
||||
* @return A raycast result with the contact points and distances
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.raycastTest = function(x, y, endX, endY, closest) {
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
// is not necessarily in the middle of the object (for sprites for example).
|
||||
var objW = this.getWidth();
|
||||
var objH = this.getHeight();
|
||||
var diffX = this.getDrawableX()+this.getCenterX() - x;
|
||||
|
@@ -30,6 +30,7 @@ gdjs.RuntimeScene = function(runtimeGame)
|
||||
this._gameStopRequested = false;
|
||||
this._requestedScene = "";
|
||||
this._isLoaded = false; // True if loadFromScene was called and the scene is being played.
|
||||
this._isJustResumed = false; // True in the first frame after resuming the paused scene
|
||||
|
||||
/** @type gdjs.RuntimeObject[] */
|
||||
this._allInstancesList = []; //An array used to create a list of all instance when necessary ( see _constructListOfAllInstances )
|
||||
@@ -156,6 +157,9 @@ gdjs.RuntimeScene.prototype.onPause = function() {
|
||||
* on screen after having being paused.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.onResume = function() {
|
||||
|
||||
this._isJustResumed = true;
|
||||
|
||||
for(var i = 0;i < gdjs.callbacksRuntimeSceneResumed.length;++i) {
|
||||
gdjs.callbacksRuntimeSceneResumed[i](this);
|
||||
}
|
||||
@@ -281,6 +285,8 @@ gdjs.RuntimeScene.prototype.renderAndStep = function(elapsedTime) {
|
||||
// this.getRenderer().renderDebugDraw(this._allInstancesList, this._layersCameraCoordinates); //TODO
|
||||
// }
|
||||
|
||||
this._isJustResumed = false;
|
||||
|
||||
this.render();
|
||||
if (this._profiler) this._profiler.end("render");
|
||||
|
||||
@@ -756,3 +762,13 @@ gdjs.RuntimeScene.prototype.getAdhocListOfAllInstances = function() {
|
||||
this._constructListOfAllInstances();
|
||||
return this._allInstancesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the scene was just resumed.
|
||||
* This is true during the first frame after the scene has been unpaused.
|
||||
*
|
||||
* @returns {boolean} true if the scene was just resumed
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.sceneJustResumed = function() {
|
||||
return this._isJustResumed;
|
||||
}
|
||||
|
@@ -541,19 +541,19 @@ gdjs.SpriteRuntimeObject.prototype._transformToGlobal = function(x, y, result) {
|
||||
|
||||
//Flipping
|
||||
if ( this._flippedX ) {
|
||||
x = this._renderer.getUnscaledWidth() - x;
|
||||
cx = this._renderer.getUnscaledWidth() - cx;
|
||||
x = x + (cx - x) * 2;
|
||||
}
|
||||
if ( this._flippedY ) {
|
||||
y = this._renderer.getUnscaledHeight() - y;
|
||||
cy = this._renderer.getUnscaledHeight() - cy;
|
||||
y = y + (cy - y) * 2;
|
||||
}
|
||||
|
||||
//Scale
|
||||
x *= Math.abs(this._scaleX);
|
||||
y *= Math.abs(this._scaleY);
|
||||
cx *= Math.abs(this._scaleX);
|
||||
cy *= Math.abs(this._scaleY);
|
||||
var absScaleX = Math.abs(this._scaleX);
|
||||
var absScaleY = Math.abs(this._scaleY);
|
||||
x *= absScaleX;
|
||||
y *= absScaleY;
|
||||
cx *= absScaleX;
|
||||
cy *= absScaleY;
|
||||
|
||||
//Rotation
|
||||
var oldX = x;
|
||||
@@ -566,8 +566,8 @@ gdjs.SpriteRuntimeObject.prototype._transformToGlobal = function(x, y, result) {
|
||||
y = cy + sinValue*(xToCenterXDelta) + cosValue*(yToCenterYDelta);
|
||||
|
||||
result.length = 2;
|
||||
result[0] = x + this.getDrawableX();
|
||||
result[1] = y + this.getDrawableY();
|
||||
result[0] = x + (this.x - this._animationFrame.origin.x*absScaleX);
|
||||
result[1] = y + (this.y - this._animationFrame.origin.y*absScaleY);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -577,7 +577,14 @@ gdjs.SpriteRuntimeObject.prototype._transformToGlobal = function(x, y, result) {
|
||||
gdjs.SpriteRuntimeObject.prototype.getDrawableX = function() {
|
||||
if ( this._animationFrame === null ) return this.x;
|
||||
|
||||
return this.x - this._animationFrame.origin.x*Math.abs(this._scaleX);
|
||||
var absScaleX = Math.abs(this._scaleX);
|
||||
|
||||
if (!this._flippedX) {
|
||||
return this.x - this._animationFrame.origin.x*absScaleX;
|
||||
} else {
|
||||
return this.x + (-this._animationFrame.origin.x
|
||||
- this._renderer.getUnscaledWidth() + 2*this._animationFrame.center.x)*absScaleX;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -587,29 +594,44 @@ gdjs.SpriteRuntimeObject.prototype.getDrawableX = function() {
|
||||
gdjs.SpriteRuntimeObject.prototype.getDrawableY = function() {
|
||||
if ( this._animationFrame === null ) return this.y;
|
||||
|
||||
return this.y - this._animationFrame.origin.y*Math.abs(this._scaleY);
|
||||
var absScaleY = Math.abs(this._scaleY);
|
||||
|
||||
if (!this._flippedY) {
|
||||
return this.y - this._animationFrame.origin.y*absScaleY;
|
||||
} else {
|
||||
return this.y + (-this._animationFrame.origin.y
|
||||
- this._renderer.getUnscaledHeight() + 2*this._animationFrame.center.y)*absScaleY;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the X position of the center of the object, relative to top-left of the object.
|
||||
* @return {number} X position of the center of the object, relative to top-left of the object.
|
||||
* Get the X position of the center of the object, relative to top-left of the texture of the object (`getDrawableX`).
|
||||
* @return {number} X position of the center of the object, relative to `getDrawableX()`.
|
||||
*/
|
||||
gdjs.SpriteRuntimeObject.prototype.getCenterX = function() {
|
||||
if ( this._animationFrame === null ) return 0;
|
||||
|
||||
//Just need to multiply by the scale as it is the center
|
||||
return this._animationFrame.center.x*Math.abs(this._scaleX);
|
||||
if (!this._flippedX) {
|
||||
//Just need to multiply by the scale as it is the center.
|
||||
return this._animationFrame.center.x*Math.abs(this._scaleX);
|
||||
} else {
|
||||
return (this._renderer.getUnscaledWidth() - this._animationFrame.center.x)*Math.abs(this._scaleX);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the Y position of the center of the object, relative to top-left of the object.
|
||||
* @return {number} Y position of the center of the object, relative to top-left of the object.
|
||||
* Get the Y position of the center of the object, relative to top-left of the texture of the object (`getDrawableY`).
|
||||
* @return {number} Y position of the center of the object, relative to `getDrawableY()`.
|
||||
*/
|
||||
gdjs.SpriteRuntimeObject.prototype.getCenterY = function() {
|
||||
if ( this._animationFrame === null ) return 0;
|
||||
|
||||
//Just need to multiply by the scale as it is the center
|
||||
return this._animationFrame.center.y*Math.abs(this._scaleY);
|
||||
if (!this._flippedY) {
|
||||
//Just need to multiply by the scale as it is the center.
|
||||
return this._animationFrame.center.y*Math.abs(this._scaleY);
|
||||
} else {
|
||||
return (this._renderer.getUnscaledHeight() - this._animationFrame.center.y)*Math.abs(this._scaleY);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -743,6 +765,7 @@ gdjs.SpriteRuntimeObject.prototype.flipX = function(enable) {
|
||||
if ( enable !== this._flippedX ) {
|
||||
this._scaleX *= -1;
|
||||
this._flippedX = enable;
|
||||
this.hitBoxesDirty = true;
|
||||
this._renderer.update();
|
||||
}
|
||||
};
|
||||
@@ -751,6 +774,7 @@ gdjs.SpriteRuntimeObject.prototype.flipY = function(enable) {
|
||||
if ( enable !== this._flippedY ) {
|
||||
this._scaleY *= -1;
|
||||
this._flippedY = enable;
|
||||
this.hitBoxesDirty = true;
|
||||
this._renderer.update();
|
||||
}
|
||||
};
|
||||
|
@@ -1,239 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<Project>
|
||||
<GDVersion Major="3" Minor="0" Build="11297" Revision="57008" />
|
||||
<Info winExecutableFilename="" winExecutableIconFile="" linuxExecutableFilename="" macExecutableFilename="" useExternalSourceFiles="false">
|
||||
<Nom value="Projet" />
|
||||
<Auteur value="" />
|
||||
<Extensions>
|
||||
<Extension name="BuiltinObject" />
|
||||
<Extension name="BuiltinAudio" />
|
||||
<Extension name="BuiltinVariables" />
|
||||
<Extension name="BuiltinTime" />
|
||||
<Extension name="BuiltinMouse" />
|
||||
<Extension name="BuiltinKeyboard" />
|
||||
<Extension name="BuiltinJoystick" />
|
||||
<Extension name="BuiltinCamera" />
|
||||
<Extension name="BuiltinWindow" />
|
||||
<Extension name="BuiltinFile" />
|
||||
<Extension name="BuiltinNetwork" />
|
||||
<Extension name="BuiltinScene" />
|
||||
<Extension name="BuiltinAdvanced" />
|
||||
<Extension name="Sprite" />
|
||||
<Extension name="BuiltinCommonInstructions" />
|
||||
<Extension name="BuiltinCommonConversions" />
|
||||
<Extension name="BuiltinStringInstructions" />
|
||||
<Extension name="BuiltinMathematicalTools" />
|
||||
<Extension name="BuiltinExternalLayouts" />
|
||||
</Extensions>
|
||||
<Platforms current="GDevelop JS platform">
|
||||
<Platform name="GDevelop C++ platform" />
|
||||
<Platform name="GDevelop JS platform" />
|
||||
</Platforms>
|
||||
<WindowW value="800" />
|
||||
<WindowH value="600" />
|
||||
<Portable />
|
||||
<LatestCompilationDirectory value="" />
|
||||
<FPSmax value="60" />
|
||||
<FPSmin value="10" />
|
||||
<verticalSync value="false" />
|
||||
</Info>
|
||||
<Resources>
|
||||
<Resources>
|
||||
<Resource kind="image" name="Elisa_standing.png" alwaysLoaded="false" smoothed="true" userAdded="true" file="Elisa_standing.png" />
|
||||
<Resource kind="image" name="Camera.png" alwaysLoaded="false" smoothed="true" userAdded="true" file="Camera.png" />
|
||||
</Resources>
|
||||
<ResourceFolders />
|
||||
</Resources>
|
||||
<Objects />
|
||||
<ObjectGroups />
|
||||
<Variables />
|
||||
<Scenes firstScene="">
|
||||
<Scene nom="Nouvelle sc<73>ne" mangledName="Nouvelle_32sc__4524ne" r="209.000000" v="209.000000" b="209.000000" titre="" oglFOV="90.000000" oglZNear="1.000000" oglZFar="500.000000" standardSortMethod="true" stopSoundsOnStartup="true" disableInputWhenNotFocused="true">
|
||||
<UISettings gridWidth="32.000000" grid="false" snap="true" gridHeight="32.000000" gridR="158.000000" gridG="180.000000" gridB="255.000000" zoomFactor="1.000000" windowMask="true" associatedLayout="" />
|
||||
<GroupesObjets />
|
||||
<Objets>
|
||||
<Objet nom="Obj" type="Sprite">
|
||||
<Variables />
|
||||
<Animations>
|
||||
<Animation typeNormal="false">
|
||||
<Direction boucle="false" tempsEntre="1.000000">
|
||||
<Sprites>
|
||||
<Sprite image="Elisa_standing.png">
|
||||
<Points>
|
||||
<Point nom="Pt" X="29.000000" Y="32.000000" />
|
||||
</Points>
|
||||
<PointOrigine nom="origine" X="1.000000" Y="8.000000" />
|
||||
<PointCentre nom="centre" X="34.000000" Y="44.000000" automatic="false" />
|
||||
<CustomCollisionMask custom="false" />
|
||||
</Sprite>
|
||||
</Sprites>
|
||||
</Direction>
|
||||
</Animation>
|
||||
</Animations>
|
||||
</Objet>
|
||||
<Objet nom="Pt" type="Sprite">
|
||||
<Variables />
|
||||
<Animations>
|
||||
<Animation typeNormal="false">
|
||||
<Direction boucle="false" tempsEntre="1.000000">
|
||||
<Sprites>
|
||||
<Sprite image="Camera.png">
|
||||
<Points />
|
||||
<PointOrigine nom="origine" X="5.000000" Y="5.000000" />
|
||||
<PointCentre nom="centre" X="5.000000" Y="5.000000" automatic="true" />
|
||||
<CustomCollisionMask custom="false" />
|
||||
</Sprite>
|
||||
</Sprites>
|
||||
</Direction>
|
||||
</Animation>
|
||||
</Animations>
|
||||
</Objet>
|
||||
</Objets>
|
||||
<Layers>
|
||||
<Layer Name="" Visibility="true">
|
||||
<Camera DefaultSize="true" Width="0.000000" Height="0.000000" DefaultViewport="true" ViewportLeft="0.000000" ViewportTop="0.000000" ViewportRight="1.000000" ViewportBottom="1.000000" />
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Variables />
|
||||
<BehaviorsSharedDatas />
|
||||
<Positions>
|
||||
<Objet nom="Obj" x="609.026123" y="357.702576" plan="-1" layer="" angle="2.606821" personalizedSize="true" width="100.749054" height="224.566879" locked="false">
|
||||
<floatInfos />
|
||||
<stringInfos />
|
||||
<InitialVariables />
|
||||
</Objet>
|
||||
<Objet nom="Obj" x="111.491089" y="54.868164" plan="-1" layer="" angle="-0.882869" personalizedSize="true" width="100.749054" height="224.566879" locked="false">
|
||||
<floatInfos />
|
||||
<stringInfos />
|
||||
<InitialVariables />
|
||||
</Objet>
|
||||
<Objet nom="Obj" x="109.283447" y="331.300018" plan="-1" layer="" angle="-2.411300" personalizedSize="true" width="100.749054" height="224.566879" locked="false">
|
||||
<floatInfos />
|
||||
<stringInfos />
|
||||
<InitialVariables />
|
||||
</Objet>
|
||||
<Objet nom="Obj" x="594.493713" y="49.738152" plan="-1" layer="" angle="1.661203" personalizedSize="true" width="100.749054" height="224.566879" locked="false">
|
||||
<floatInfos />
|
||||
<stringInfos />
|
||||
<InitialVariables />
|
||||
</Objet>
|
||||
<Objet nom="Obj" x="22.494812" y="8.866817" plan="-1" layer="" angle="0.000000" personalizedSize="false" width="0.000000" height="0.000000" locked="false">
|
||||
<floatInfos />
|
||||
<stringInfos />
|
||||
<InitialVariables />
|
||||
</Objet>
|
||||
</Positions>
|
||||
<Events>
|
||||
<Event disabled="false" folded="false">
|
||||
<Type value="BuiltinCommonInstructions::Standard" />
|
||||
<Conditions />
|
||||
<Actions>
|
||||
<Action>
|
||||
<Type value="Delete" />
|
||||
<Parametre value="Pt" />
|
||||
<Parametre value="" />
|
||||
</Action>
|
||||
</Actions>
|
||||
</Event>
|
||||
<Event disabled="false" folded="false">
|
||||
<Type value="BuiltinCommonInstructions::Standard" />
|
||||
<Conditions>
|
||||
<Condition>
|
||||
<Type value="DepartScene" Contraire="false" />
|
||||
<Parametre value="" />
|
||||
</Condition>
|
||||
<Condition>
|
||||
<Type value="PosX" Contraire="false" />
|
||||
<Parametre value="Obj" />
|
||||
<Parametre value=">" />
|
||||
<Parametre value="400" />
|
||||
</Condition>
|
||||
</Conditions>
|
||||
<Actions>
|
||||
<Action>
|
||||
<Type value="FlipX" />
|
||||
<Parametre value="Obj" />
|
||||
<Parametre value="oui" />
|
||||
</Action>
|
||||
</Actions>
|
||||
</Event>
|
||||
<Event disabled="false" folded="false">
|
||||
<Type value="BuiltinCommonInstructions::Standard" />
|
||||
<Conditions>
|
||||
<Condition>
|
||||
<Type value="DepartScene" Contraire="false" />
|
||||
<Parametre value="" />
|
||||
</Condition>
|
||||
<Condition>
|
||||
<Type value="PosY" Contraire="false" />
|
||||
<Parametre value="Obj" />
|
||||
<Parametre value=">" />
|
||||
<Parametre value="300" />
|
||||
</Condition>
|
||||
</Conditions>
|
||||
<Actions>
|
||||
<Action>
|
||||
<Type value="FlipY" />
|
||||
<Parametre value="Obj" />
|
||||
<Parametre value="oui" />
|
||||
</Action>
|
||||
</Actions>
|
||||
</Event>
|
||||
<Event disabled="false" folded="false">
|
||||
<Type value="BuiltinCommonInstructions::Standard" />
|
||||
<Conditions />
|
||||
<Actions>
|
||||
<Action>
|
||||
<Type value="ChangeDirection" />
|
||||
<Parametre value="Obj" />
|
||||
<Parametre value="+" />
|
||||
<Parametre value="TimeDelta()*10" />
|
||||
</Action>
|
||||
</Actions>
|
||||
</Event>
|
||||
<Event disabled="false" folded="false">
|
||||
<Type value="BuiltinCommonInstructions::ForEach" />
|
||||
<Object value="Obj" />
|
||||
<Conditions />
|
||||
<Actions>
|
||||
<Action>
|
||||
<Type value="Create" />
|
||||
<Parametre value="" />
|
||||
<Parametre value="Pt" />
|
||||
<Parametre value="Obj.PointX(Pt)" />
|
||||
<Parametre value="Obj.PointY(Pt)" />
|
||||
<Parametre value="" />
|
||||
</Action>
|
||||
<Action>
|
||||
<Type value="Create" />
|
||||
<Parametre value="" />
|
||||
<Parametre value="Pt" />
|
||||
<Parametre value="Obj.PointX(Centre)" />
|
||||
<Parametre value="Obj.PointY(Centre)" />
|
||||
<Parametre value="" />
|
||||
</Action>
|
||||
<Action>
|
||||
<Type value="Create" />
|
||||
<Parametre value="" />
|
||||
<Parametre value="Pt" />
|
||||
<Parametre value="Obj.X()" />
|
||||
<Parametre value="Obj.Y()" />
|
||||
<Parametre value="" />
|
||||
</Action>
|
||||
<Action>
|
||||
<Type value="Create" />
|
||||
<Parametre value="" />
|
||||
<Parametre value="Pt" />
|
||||
<Parametre value="Obj.PointX(Origin)" />
|
||||
<Parametre value="Obj.PointY(Origin)" />
|
||||
<Parametre value="" />
|
||||
</Action>
|
||||
</Actions>
|
||||
</Event>
|
||||
</Events>
|
||||
</Scene>
|
||||
</Scenes>
|
||||
<ExternalEvents />
|
||||
<ExternalLayouts />
|
||||
<ExternalSourceFiles />
|
||||
</Project>
|
729
GDJS/tests/games/Sprite flipping and resizing.json
Normal file
729
GDJS/tests/games/Sprite flipping and resizing.json
Normal file
@@ -0,0 +1,729 @@
|
||||
{
|
||||
"firstLayout": "",
|
||||
"gdVersion": {
|
||||
"build": 98,
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"revision": 0
|
||||
},
|
||||
"properties": {
|
||||
"adMobAppId": "",
|
||||
"folderProject": false,
|
||||
"linuxExecutableFilename": "",
|
||||
"macExecutableFilename": "",
|
||||
"orientation": "default",
|
||||
"packageName": "",
|
||||
"projectFile": "/Users/florian/Projects/F/GD/GDJS/tests/games/Sprite flipping and resizing.json",
|
||||
"scaleMode": "linear",
|
||||
"sizeOnStartupMode": "",
|
||||
"useExternalSourceFiles": false,
|
||||
"version": "1.0.0",
|
||||
"winExecutableFilename": "",
|
||||
"winExecutableIconFile": "",
|
||||
"name": "Projet",
|
||||
"author": "",
|
||||
"windowWidth": 800,
|
||||
"windowHeight": 600,
|
||||
"latestCompilationDirectory": "",
|
||||
"maxFPS": 60,
|
||||
"minFPS": 10,
|
||||
"verticalSync": false,
|
||||
"platformSpecificAssets": {},
|
||||
"loadingScreen": {
|
||||
"showGDevelopSplash": true
|
||||
},
|
||||
"extensions": [
|
||||
{
|
||||
"name": "BuiltinObject"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinAudio"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinVariables"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinTime"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinMouse"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinKeyboard"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinJoystick"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinCamera"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinWindow"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinFile"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinNetwork"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinScene"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinAdvanced"
|
||||
},
|
||||
{
|
||||
"name": "Sprite"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinCommonInstructions"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinCommonConversions"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinStringInstructions"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinMathematicalTools"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinExternalLayouts"
|
||||
}
|
||||
],
|
||||
"platforms": [
|
||||
{
|
||||
"name": "GDevelop JS platform"
|
||||
}
|
||||
],
|
||||
"currentPlatform": "GDevelop JS platform"
|
||||
},
|
||||
"resources": {
|
||||
"resources": [
|
||||
{
|
||||
"alwaysLoaded": false,
|
||||
"file": "Elisa_standing.png",
|
||||
"kind": "image",
|
||||
"metadata": "",
|
||||
"name": "Elisa_standing.png",
|
||||
"smoothed": true,
|
||||
"userAdded": true
|
||||
},
|
||||
{
|
||||
"alwaysLoaded": false,
|
||||
"file": "Camera.png",
|
||||
"kind": "image",
|
||||
"metadata": "",
|
||||
"name": "Camera.png",
|
||||
"smoothed": true,
|
||||
"userAdded": true
|
||||
}
|
||||
],
|
||||
"resourceFolders": []
|
||||
},
|
||||
"objects": [],
|
||||
"objectsGroups": [],
|
||||
"variables": [],
|
||||
"layouts": [
|
||||
{
|
||||
"b": 209,
|
||||
"disableInputWhenNotFocused": true,
|
||||
"mangledName": "Nouvelle_32sc_232ne",
|
||||
"name": "Nouvelle scène",
|
||||
"oglFOV": 90,
|
||||
"oglZFar": 500,
|
||||
"oglZNear": 1,
|
||||
"r": 209,
|
||||
"standardSortMethod": true,
|
||||
"stopSoundsOnStartup": true,
|
||||
"title": "",
|
||||
"v": 209,
|
||||
"uiSettings": {
|
||||
"grid": false,
|
||||
"gridB": 255,
|
||||
"gridG": 180,
|
||||
"gridHeight": 32,
|
||||
"gridOffsetX": 0,
|
||||
"gridOffsetY": 0,
|
||||
"gridR": 158,
|
||||
"gridWidth": 32,
|
||||
"snap": true,
|
||||
"windowMask": true,
|
||||
"zoomFactor": 0.72
|
||||
},
|
||||
"objectsGroups": [],
|
||||
"variables": [],
|
||||
"instances": [
|
||||
{
|
||||
"angle": 2.60682,
|
||||
"customSize": true,
|
||||
"height": 179,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 80,
|
||||
"x": 472,
|
||||
"y": 282,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": [
|
||||
{
|
||||
"name": "rotate",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "flipX",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "flipY",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"angle": 0.5887,
|
||||
"customSize": true,
|
||||
"height": 198,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 89,
|
||||
"x": 112,
|
||||
"y": 260,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": [
|
||||
{
|
||||
"name": "rotate",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "flipY",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"angle": 1.6612,
|
||||
"customSize": true,
|
||||
"height": 167,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 75,
|
||||
"x": 453,
|
||||
"y": 51,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": [
|
||||
{
|
||||
"name": "rotate",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "flipX",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": false,
|
||||
"height": 0,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 0,
|
||||
"x": 732,
|
||||
"y": 497,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": [
|
||||
{
|
||||
"name": "flipX",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "flipY",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": false,
|
||||
"height": 0,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 0,
|
||||
"x": 14,
|
||||
"y": 492,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": [
|
||||
{
|
||||
"name": "flipY",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": false,
|
||||
"height": 0,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 0,
|
||||
"x": 360,
|
||||
"y": 11,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": [
|
||||
{
|
||||
"name": "flipX",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": false,
|
||||
"height": 0,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 0,
|
||||
"x": 359,
|
||||
"y": 9,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": []
|
||||
},
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": false,
|
||||
"height": 0,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 0,
|
||||
"x": 723,
|
||||
"y": 13,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": [
|
||||
{
|
||||
"name": "flipX",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": false,
|
||||
"height": 0,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 0,
|
||||
"x": 22.4948,
|
||||
"y": 8.86682,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": []
|
||||
},
|
||||
{
|
||||
"angle": -0.882869,
|
||||
"customSize": true,
|
||||
"height": 168,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Obj",
|
||||
"width": 75,
|
||||
"x": 111.491,
|
||||
"y": 54.8682,
|
||||
"zOrder": -1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": [
|
||||
{
|
||||
"name": "rotate",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"objects": [
|
||||
{
|
||||
"name": "Obj",
|
||||
"tags": "",
|
||||
"type": "Sprite",
|
||||
"updateIfNotVisible": true,
|
||||
"variables": [],
|
||||
"behaviors": [],
|
||||
"animations": [
|
||||
{
|
||||
"name": "",
|
||||
"useMultipleDirections": false,
|
||||
"directions": [
|
||||
{
|
||||
"looping": false,
|
||||
"timeBetweenFrames": 1,
|
||||
"sprites": [
|
||||
{
|
||||
"hasCustomCollisionMask": false,
|
||||
"image": "Elisa_standing.png",
|
||||
"points": [
|
||||
{
|
||||
"name": "Pt",
|
||||
"x": 29,
|
||||
"y": 32
|
||||
}
|
||||
],
|
||||
"originPoint": {
|
||||
"name": "origine",
|
||||
"x": 1,
|
||||
"y": 8
|
||||
},
|
||||
"centerPoint": {
|
||||
"automatic": false,
|
||||
"name": "centre",
|
||||
"x": 34,
|
||||
"y": 44
|
||||
},
|
||||
"customCollisionMask": [
|
||||
[
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Pt",
|
||||
"tags": "",
|
||||
"type": "Sprite",
|
||||
"updateIfNotVisible": true,
|
||||
"variables": [],
|
||||
"behaviors": [],
|
||||
"animations": [
|
||||
{
|
||||
"name": "",
|
||||
"useMultipleDirections": false,
|
||||
"directions": [
|
||||
{
|
||||
"looping": false,
|
||||
"timeBetweenFrames": 1,
|
||||
"sprites": [
|
||||
{
|
||||
"hasCustomCollisionMask": false,
|
||||
"image": "Camera.png",
|
||||
"points": [],
|
||||
"originPoint": {
|
||||
"name": "origine",
|
||||
"x": 5,
|
||||
"y": 5
|
||||
},
|
||||
"centerPoint": {
|
||||
"automatic": true,
|
||||
"name": "centre",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"customCollisionMask": [
|
||||
[
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "Delete"
|
||||
},
|
||||
"parameters": [
|
||||
"Pt",
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "DepartScene"
|
||||
},
|
||||
"parameters": [
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "VarObjet"
|
||||
},
|
||||
"parameters": [
|
||||
"Obj",
|
||||
"flipX",
|
||||
"=",
|
||||
"1"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "FlipX"
|
||||
},
|
||||
"parameters": [
|
||||
"Obj",
|
||||
"oui"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "DepartScene"
|
||||
},
|
||||
"parameters": [
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "VarObjet"
|
||||
},
|
||||
"parameters": [
|
||||
"Obj",
|
||||
"flipY",
|
||||
"=",
|
||||
"1"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "FlipY"
|
||||
},
|
||||
"parameters": [
|
||||
"Obj",
|
||||
"oui"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "VarObjet"
|
||||
},
|
||||
"parameters": [
|
||||
"Obj",
|
||||
"rotate",
|
||||
"=",
|
||||
"1"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "ChangeDirection"
|
||||
},
|
||||
"parameters": [
|
||||
"Obj",
|
||||
"+",
|
||||
"TimeDelta()*10"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::ForEach",
|
||||
"object": "Obj",
|
||||
"conditions": [],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "Create"
|
||||
},
|
||||
"parameters": [
|
||||
"",
|
||||
"Pt",
|
||||
"Obj.PointX(\"Pt\")",
|
||||
"Obj.PointY(\"Pt\")",
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "Create"
|
||||
},
|
||||
"parameters": [
|
||||
"",
|
||||
"Pt",
|
||||
"Obj.PointX(\"Centre\")",
|
||||
"Obj.PointY(\"Centre\")",
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "Create"
|
||||
},
|
||||
"parameters": [
|
||||
"",
|
||||
"Pt",
|
||||
"Obj.X()",
|
||||
"Obj.Y()",
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "Create"
|
||||
},
|
||||
"parameters": [
|
||||
"",
|
||||
"Pt",
|
||||
"Obj.PointX(\"Origin\")",
|
||||
"Obj.PointY(\"Origin\")",
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
}
|
||||
],
|
||||
"layers": [
|
||||
{
|
||||
"name": "",
|
||||
"visibility": true,
|
||||
"cameras": [
|
||||
{
|
||||
"defaultSize": true,
|
||||
"defaultViewport": true,
|
||||
"height": 0,
|
||||
"viewportBottom": 1,
|
||||
"viewportLeft": 0,
|
||||
"viewportRight": 1,
|
||||
"viewportTop": 0,
|
||||
"width": 0
|
||||
}
|
||||
],
|
||||
"effects": []
|
||||
}
|
||||
],
|
||||
"behaviorsSharedData": []
|
||||
}
|
||||
],
|
||||
"externalEvents": [],
|
||||
"eventsFunctionsExtensions": [],
|
||||
"externalLayouts": [],
|
||||
"externalSourceFiles": []
|
||||
}
|
BIN
GDJS/tests/games/rotate-flip-around-center-point/NewObject-1.png
Normal file
BIN
GDJS/tests/games/rotate-flip-around-center-point/NewObject-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 186 B |
BIN
GDJS/tests/games/rotate-flip-around-center-point/Ship-1-0.png
Normal file
BIN
GDJS/tests/games/rotate-flip-around-center-point/Ship-1-0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 726 B |
BIN
GDJS/tests/games/rotate-flip-around-center-point/Ship-1.png
Normal file
BIN
GDJS/tests/games/rotate-flip-around-center-point/Ship-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 438 B |
@@ -0,0 +1,581 @@
|
||||
{
|
||||
"firstLayout": "",
|
||||
"gdVersion": {
|
||||
"build": 98,
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"revision": 0
|
||||
},
|
||||
"properties": {
|
||||
"adMobAppId": "",
|
||||
"folderProject": false,
|
||||
"linuxExecutableFilename": "",
|
||||
"macExecutableFilename": "",
|
||||
"orientation": "landscape",
|
||||
"packageName": "com.example.gamename",
|
||||
"projectFile": "/Users/florian/Projects/F/GD/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json",
|
||||
"scaleMode": "linear",
|
||||
"sizeOnStartupMode": "adaptWidth",
|
||||
"useExternalSourceFiles": false,
|
||||
"version": "1.0.0",
|
||||
"winExecutableFilename": "",
|
||||
"winExecutableIconFile": "",
|
||||
"name": "Project",
|
||||
"author": "",
|
||||
"windowWidth": 800,
|
||||
"windowHeight": 600,
|
||||
"latestCompilationDirectory": "",
|
||||
"maxFPS": 60,
|
||||
"minFPS": 20,
|
||||
"verticalSync": false,
|
||||
"platformSpecificAssets": {},
|
||||
"loadingScreen": {
|
||||
"showGDevelopSplash": true
|
||||
},
|
||||
"extensions": [
|
||||
{
|
||||
"name": "BuiltinObject"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinAudio"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinVariables"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinTime"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinMouse"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinKeyboard"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinJoystick"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinCamera"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinWindow"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinFile"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinNetwork"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinScene"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinAdvanced"
|
||||
},
|
||||
{
|
||||
"name": "Sprite"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinCommonInstructions"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinCommonConversions"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinStringInstructions"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinMathematicalTools"
|
||||
},
|
||||
{
|
||||
"name": "BuiltinExternalLayouts"
|
||||
}
|
||||
],
|
||||
"platforms": [
|
||||
{
|
||||
"name": "GDevelop JS platform"
|
||||
}
|
||||
],
|
||||
"currentPlatform": "GDevelop JS platform"
|
||||
},
|
||||
"resources": {
|
||||
"resources": [
|
||||
{
|
||||
"alwaysLoaded": false,
|
||||
"file": "Ship-1.png",
|
||||
"kind": "image",
|
||||
"metadata": "",
|
||||
"name": "Ship-1.png",
|
||||
"smoothed": true,
|
||||
"userAdded": false
|
||||
},
|
||||
{
|
||||
"alwaysLoaded": false,
|
||||
"file": "Ship-1-0.png",
|
||||
"kind": "image",
|
||||
"metadata": "",
|
||||
"name": "Ship-1-0.png",
|
||||
"smoothed": true,
|
||||
"userAdded": true
|
||||
},
|
||||
{
|
||||
"alwaysLoaded": false,
|
||||
"file": "NewObject-1.png",
|
||||
"kind": "image",
|
||||
"metadata": "",
|
||||
"name": "NewObject-1.png",
|
||||
"smoothed": true,
|
||||
"userAdded": false
|
||||
}
|
||||
],
|
||||
"resourceFolders": []
|
||||
},
|
||||
"objects": [],
|
||||
"objectsGroups": [],
|
||||
"variables": [],
|
||||
"layouts": [
|
||||
{
|
||||
"b": 209,
|
||||
"disableInputWhenNotFocused": true,
|
||||
"mangledName": "NewScene",
|
||||
"name": "NewScene",
|
||||
"oglFOV": 90,
|
||||
"oglZFar": 500,
|
||||
"oglZNear": 1,
|
||||
"r": 209,
|
||||
"standardSortMethod": true,
|
||||
"stopSoundsOnStartup": true,
|
||||
"title": "",
|
||||
"v": 209,
|
||||
"uiSettings": {
|
||||
"grid": false,
|
||||
"gridB": 255,
|
||||
"gridG": 180,
|
||||
"gridHeight": 32,
|
||||
"gridOffsetX": 0,
|
||||
"gridOffsetY": 0,
|
||||
"gridR": 158,
|
||||
"gridWidth": 32,
|
||||
"snap": true,
|
||||
"windowMask": false,
|
||||
"zoomFactor": 1
|
||||
},
|
||||
"objectsGroups": [],
|
||||
"variables": [],
|
||||
"instances": [
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": false,
|
||||
"height": 0,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Ship",
|
||||
"width": 0,
|
||||
"x": 365,
|
||||
"y": 276,
|
||||
"zOrder": 1,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": []
|
||||
},
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": true,
|
||||
"height": 36,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Collider",
|
||||
"width": 31,
|
||||
"x": 505,
|
||||
"y": 385,
|
||||
"zOrder": 2,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": []
|
||||
},
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": false,
|
||||
"height": 0,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Collider",
|
||||
"width": 0,
|
||||
"x": 472,
|
||||
"y": 168,
|
||||
"zOrder": 2,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": []
|
||||
},
|
||||
{
|
||||
"angle": 0,
|
||||
"customSize": true,
|
||||
"height": 36,
|
||||
"layer": "",
|
||||
"locked": false,
|
||||
"name": "Collider",
|
||||
"width": 31,
|
||||
"x": 149,
|
||||
"y": 179,
|
||||
"zOrder": 2,
|
||||
"numberProperties": [],
|
||||
"stringProperties": [],
|
||||
"initialVariables": []
|
||||
}
|
||||
],
|
||||
"objects": [
|
||||
{
|
||||
"name": "Ship",
|
||||
"tags": "",
|
||||
"type": "Sprite",
|
||||
"updateIfNotVisible": false,
|
||||
"variables": [],
|
||||
"behaviors": [],
|
||||
"animations": [
|
||||
{
|
||||
"name": "Ship",
|
||||
"useMultipleDirections": false,
|
||||
"directions": [
|
||||
{
|
||||
"looping": false,
|
||||
"timeBetweenFrames": 0.08,
|
||||
"sprites": [
|
||||
{
|
||||
"hasCustomCollisionMask": true,
|
||||
"image": "Ship-1-0.png",
|
||||
"points": [],
|
||||
"originPoint": {
|
||||
"name": "origine",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"centerPoint": {
|
||||
"automatic": false,
|
||||
"name": "centre",
|
||||
"x": 24,
|
||||
"y": 29
|
||||
},
|
||||
"customCollisionMask": [
|
||||
[
|
||||
{
|
||||
"x": 66.832,
|
||||
"y": 15.2109
|
||||
},
|
||||
{
|
||||
"x": 82.5742,
|
||||
"y": 12.3047
|
||||
},
|
||||
{
|
||||
"x": 81.5078,
|
||||
"y": 45.1836
|
||||
},
|
||||
{
|
||||
"x": 66.1602,
|
||||
"y": 41.5391
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Collider",
|
||||
"tags": "",
|
||||
"type": "Sprite",
|
||||
"updateIfNotVisible": false,
|
||||
"variables": [],
|
||||
"behaviors": [],
|
||||
"animations": [
|
||||
{
|
||||
"name": "NewObject",
|
||||
"useMultipleDirections": false,
|
||||
"directions": [
|
||||
{
|
||||
"looping": false,
|
||||
"timeBetweenFrames": 0.08,
|
||||
"sprites": [
|
||||
{
|
||||
"hasCustomCollisionMask": false,
|
||||
"image": "NewObject-1.png",
|
||||
"points": [],
|
||||
"originPoint": {
|
||||
"name": "origine",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"centerPoint": {
|
||||
"automatic": true,
|
||||
"name": "centre",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"customCollisionMask": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Comment",
|
||||
"color": {
|
||||
"b": 109,
|
||||
"g": 230,
|
||||
"r": 255,
|
||||
"textB": 0,
|
||||
"textG": 0,
|
||||
"textR": 0
|
||||
},
|
||||
"comment": "The spaceship center point should always stay at the same position when the ship is flipped. In other words, there should be no \"jump\" when changing direction or rotating.",
|
||||
"comment2": ""
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "KeyPressed"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"Up"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "AddForceAL"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"Ship.Direction()-90",
|
||||
"100",
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "KeyPressed"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"Down"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"actions": [],
|
||||
"events": []
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "KeyPressed"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"Left"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "FlipX"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"no"
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "Rotate"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"-90",
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "KeyPressed"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"Right"
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "FlipX"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"yes"
|
||||
],
|
||||
"subInstructions": []
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "Rotate"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"90",
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Comment",
|
||||
"color": {
|
||||
"b": 109,
|
||||
"g": 230,
|
||||
"r": 255,
|
||||
"textB": 0,
|
||||
"textG": 0,
|
||||
"textR": 0
|
||||
},
|
||||
"comment": "Only the arm of the ship should trigger a collision with \"Collider\" objects",
|
||||
"comment2": ""
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "SceneBackground"
|
||||
},
|
||||
"parameters": [
|
||||
"",
|
||||
"\"147;147;147\""
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
"folded": false,
|
||||
"type": "BuiltinCommonInstructions::Standard",
|
||||
"conditions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "CollisionNP"
|
||||
},
|
||||
"parameters": [
|
||||
"Ship",
|
||||
"Collider",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": {
|
||||
"inverted": false,
|
||||
"value": "SceneBackground"
|
||||
},
|
||||
"parameters": [
|
||||
"",
|
||||
"\"0;193;1\""
|
||||
],
|
||||
"subInstructions": []
|
||||
}
|
||||
],
|
||||
"events": []
|
||||
}
|
||||
],
|
||||
"layers": [
|
||||
{
|
||||
"name": "",
|
||||
"visibility": true,
|
||||
"cameras": [
|
||||
{
|
||||
"defaultSize": true,
|
||||
"defaultViewport": true,
|
||||
"height": 0,
|
||||
"viewportBottom": 1,
|
||||
"viewportLeft": 0,
|
||||
"viewportRight": 1,
|
||||
"viewportTop": 0,
|
||||
"width": 0
|
||||
}
|
||||
],
|
||||
"effects": []
|
||||
}
|
||||
],
|
||||
"behaviorsSharedData": []
|
||||
}
|
||||
],
|
||||
"externalEvents": [],
|
||||
"eventsFunctionsExtensions": [],
|
||||
"externalLayouts": [],
|
||||
"externalSourceFiles": []
|
||||
}
|
@@ -32,9 +32,7 @@ module.exports = function(config) {
|
||||
'../Runtime/variable.js',
|
||||
'../Runtime/variablescontainer.js',
|
||||
'../Runtime/oncetriggers.js',
|
||||
'../Runtime/runtimescene.js',
|
||||
'../Runtime/runtimebehavior.js',
|
||||
'../Runtime/runtimeobject.js',
|
||||
'../Runtime/spriteruntimeobject.js',
|
||||
'../Runtime/events-tools/commontools.js',
|
||||
'../Runtime/events-tools/runtimescenetools.js',
|
||||
@@ -59,13 +57,17 @@ module.exports = function(config) {
|
||||
'./tests/Extensions/**.js',
|
||||
|
||||
//All tests files:
|
||||
'./tests/init.js',
|
||||
'./tests-utils/init.gdjs.js',
|
||||
'./tests-utils/init.pixiruntimegamewithassets.js',
|
||||
'../../Extensions/**/tests/**.spec.js',
|
||||
'./tests/**/*.js',
|
||||
|
||||
//All benchmark files:
|
||||
'./benchmarks/init.js',
|
||||
'./benchmarks/**/*.js'
|
||||
'./benchmarks/**/*.js',
|
||||
|
||||
// Assets
|
||||
{pattern: './tests-utils/assets/*.jpg', watched: false, included: false, served: true, nocache: false}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
BIN
GDJS/tests/tests-utils/assets/64x64.jpg
Normal file
BIN
GDJS/tests/tests-utils/assets/64x64.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
39
GDJS/tests/tests-utils/init.pixiruntimegamewithassets.js
Normal file
39
GDJS/tests/tests-utils/init.pixiruntimegamewithassets.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Create and return a game with a few assets loaded, to be used in tests
|
||||
* needing real images.
|
||||
*/
|
||||
gdjs.getPixiRuntimeGameWithAssets = () => {
|
||||
if (gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets) {
|
||||
return Promise.resolve(gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets);
|
||||
}
|
||||
|
||||
var runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
resources: {
|
||||
resources: [
|
||||
{
|
||||
kind: 'image',
|
||||
name: 'base/tests-utils/assets/64x64.jpg',
|
||||
metadata: '',
|
||||
file: 'base/tests-utils/assets/64x64.jpg',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
return new Promise(resolve => {
|
||||
runtimeGame.loadAllAssets(
|
||||
() => {
|
||||
console.info('Done loading assets for test game');
|
||||
|
||||
gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets = runtimeGame;
|
||||
resolve(gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets);
|
||||
},
|
||||
() => {/* Ignore progress */}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/** @type gdjs.RuntimeGame */
|
||||
gdjs.getPixiRuntimeGameWithAssets.pixiRuntimeGameWithAssets = null;
|
@@ -1,140 +1,74 @@
|
||||
/**
|
||||
* Common tests for gdjs game engine.
|
||||
* See README.md for more information.
|
||||
* Basic tests for gdjs.SpriteRuntimeObject
|
||||
*/
|
||||
|
||||
const makeSpriteRuntimeObjectWithCustomHitBox = (runtimeScene) => new gdjs.SpriteRuntimeObject(runtimeScene, {
|
||||
"name": "obj1",
|
||||
"type": "Sprite",
|
||||
"updateIfNotVisible": false,
|
||||
"variables": [],
|
||||
"behaviors": [],
|
||||
"animations": [
|
||||
{
|
||||
"name": "NewObject2",
|
||||
"useMultipleDirections": false,
|
||||
"directions": [
|
||||
{
|
||||
"looping": false,
|
||||
"timeBetweenFrames": 1,
|
||||
"sprites": [
|
||||
{
|
||||
"hasCustomCollisionMask": true,
|
||||
"image": "NewObject2-2.png",
|
||||
"points": [],
|
||||
"originPoint": {
|
||||
"name": "origine",
|
||||
"x": 32,
|
||||
"y": 16
|
||||
},
|
||||
"centerPoint": {
|
||||
"automatic": false,
|
||||
"name": "centre",
|
||||
"x": 64,
|
||||
"y": 31
|
||||
},
|
||||
"customCollisionMask": [
|
||||
[
|
||||
{
|
||||
"x": 12.5,
|
||||
"y": 1
|
||||
},
|
||||
{
|
||||
"x": 41.5,
|
||||
"y": 2
|
||||
},
|
||||
{
|
||||
"x": 55.5,
|
||||
"y": 31
|
||||
},
|
||||
{
|
||||
"x": 24.5,
|
||||
"y": 30
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
describe('gdjs.SpriteRuntimeObject', function() {
|
||||
var runtimeGame = new gdjs.RuntimeGame({variables: [], properties: {windowWidth: 800, windowHeight: 600}});
|
||||
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
var runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
});
|
||||
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
it('should handle scaling properly', function(){
|
||||
var object = new gdjs.SpriteRuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], animations: []});
|
||||
it('should handle scaling properly', function() {
|
||||
var object = new gdjs.SpriteRuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
type: '',
|
||||
behaviors: [],
|
||||
animations: [],
|
||||
});
|
||||
|
||||
expect(object.getScaleX()).to.be(1);
|
||||
object.flipX(true);
|
||||
expect(object.getScaleX()).to.be(1);
|
||||
object.setScaleX(0.42);
|
||||
expect(object.getScaleX()).to.be(0.42);
|
||||
expect(object.isFlippedX()).to.be(true);
|
||||
object.flipX(false);
|
||||
expect(object.isFlippedX()).to.be(false);
|
||||
expect(object.getScaleX()).to.be(0.42);
|
||||
});
|
||||
expect(object.getScaleX()).to.be(1);
|
||||
object.flipX(true);
|
||||
expect(object.getScaleX()).to.be(1);
|
||||
object.setScaleX(0.42);
|
||||
expect(object.getScaleX()).to.be(0.42);
|
||||
expect(object.isFlippedX()).to.be(true);
|
||||
object.flipX(false);
|
||||
expect(object.isFlippedX()).to.be(false);
|
||||
expect(object.getScaleX()).to.be(0.42);
|
||||
});
|
||||
|
||||
describe('Animations', function() {
|
||||
var object = new gdjs.SpriteRuntimeObject(runtimeScene, {
|
||||
name: "obj1",
|
||||
type: "",
|
||||
behaviors: [],
|
||||
animations: [{
|
||||
name: 'firstAnimation',
|
||||
directions: []
|
||||
}, {
|
||||
name: 'secondAnimation',
|
||||
directions: []
|
||||
}, {
|
||||
name: '',
|
||||
directions: []
|
||||
}],
|
||||
});
|
||||
describe('Animations', function() {
|
||||
var object = new gdjs.SpriteRuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
type: '',
|
||||
behaviors: [],
|
||||
animations: [
|
||||
{
|
||||
name: 'firstAnimation',
|
||||
directions: [],
|
||||
},
|
||||
{
|
||||
name: 'secondAnimation',
|
||||
directions: [],
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
directions: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
it('can change animation using animation name', function() {
|
||||
expect(object.getAnimationName()).to.be('firstAnimation');
|
||||
object.setAnimationName('secondAnimation');
|
||||
expect(object.getAnimationName()).to.be('secondAnimation');
|
||||
expect(object.getAnimation()).to.be(1);
|
||||
expect(object.isCurrentAnimationName('secondAnimation')).to.be(true);
|
||||
expect(object.isCurrentAnimationName('firstAnimation')).to.be(false);
|
||||
});
|
||||
it('can change animation using animation name', function() {
|
||||
expect(object.getAnimationName()).to.be('firstAnimation');
|
||||
object.setAnimationName('secondAnimation');
|
||||
expect(object.getAnimationName()).to.be('secondAnimation');
|
||||
expect(object.getAnimation()).to.be(1);
|
||||
expect(object.isCurrentAnimationName('secondAnimation')).to.be(true);
|
||||
expect(object.isCurrentAnimationName('firstAnimation')).to.be(false);
|
||||
});
|
||||
|
||||
it('keeps the same animation when using an invalid/empty name', function() {
|
||||
object.setAnimationName('unexisting animation');
|
||||
expect(object.getAnimation()).to.be(1);
|
||||
object.setAnimationName('');
|
||||
expect(object.getAnimation()).to.be(1);
|
||||
});
|
||||
it('keeps the same animation when using an invalid/empty name', function() {
|
||||
object.setAnimationName('unexisting animation');
|
||||
expect(object.getAnimation()).to.be(1);
|
||||
object.setAnimationName('');
|
||||
expect(object.getAnimation()).to.be(1);
|
||||
});
|
||||
|
||||
it('can change animation using animation index', function(){
|
||||
object.setAnimation(2);
|
||||
expect(object.getAnimationName()).to.be('');
|
||||
object.setAnimation(0);
|
||||
expect(object.getAnimationName()).to.be('firstAnimation');
|
||||
});
|
||||
});
|
||||
|
||||
it('should properly compute hitboxes', function(){
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// Check the hitboxes without any rotation (only the non default origin
|
||||
// which is at 32;16 is to be used).
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - 32, 1 - 16]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - 32, 2 - 16]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - 32, 31 - 16]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - 32, 30 - 16]);
|
||||
|
||||
object.setAngle(90);
|
||||
expect(object.getHitBoxes()[0].vertices[0][0]).to.be.within(61.9999, 62.0001);
|
||||
expect(object.getHitBoxes()[0].vertices[0][1]).to.be.within(-36.5001, -36.49999);
|
||||
expect(object.getHitBoxes()[0].vertices[2][0]).to.be.within(31.999, 32.0001);
|
||||
expect(object.getHitBoxes()[0].vertices[2][1]).to.be.within(6.4999, 6.5001);
|
||||
});
|
||||
it('can change animation using animation index', function() {
|
||||
object.setAnimation(2);
|
||||
expect(object.getAnimationName()).to.be('');
|
||||
object.setAnimation(0);
|
||||
expect(object.getAnimationName()).to.be('firstAnimation');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -0,0 +1,390 @@
|
||||
/**
|
||||
* Tests for gdjs.SpriteRuntimeObject using a "real" PIXI RuntimeGame with assets.
|
||||
*
|
||||
* See also these test games:
|
||||
* * GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json
|
||||
* * GDJS/tests/games/rotated-objects-hitboxes/game.json
|
||||
*/
|
||||
describe('gdjs.SpriteRuntimeObject (using a PIXI RuntimeGame with assets)', function() {
|
||||
const textureWidth = 64;
|
||||
const textureHeight = 64;
|
||||
const centerPointX = 64;
|
||||
const centerPointY = 31;
|
||||
const originPointX = 32;
|
||||
const originPointY = 16;
|
||||
|
||||
/**
|
||||
* Create a SpriteRuntimeObject using a 64x64 image with custom origin,
|
||||
* center and a custom collision mask.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
*/
|
||||
const makeSpriteRuntimeObjectWithCustomHitBox = runtimeScene =>
|
||||
new gdjs.SpriteRuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
type: 'Sprite',
|
||||
updateIfNotVisible: false,
|
||||
variables: [],
|
||||
behaviors: [],
|
||||
animations: [
|
||||
{
|
||||
name: 'NewObject2',
|
||||
useMultipleDirections: false,
|
||||
directions: [
|
||||
{
|
||||
looping: false,
|
||||
timeBetweenFrames: 1,
|
||||
sprites: [
|
||||
{
|
||||
hasCustomCollisionMask: true,
|
||||
image: 'base/tests-utils/assets/64x64.jpg',
|
||||
points: [],
|
||||
originPoint: {
|
||||
name: 'origine',
|
||||
x: originPointX,
|
||||
y: originPointY,
|
||||
},
|
||||
centerPoint: {
|
||||
automatic: false,
|
||||
name: 'centre',
|
||||
x: centerPointX,
|
||||
y: centerPointY,
|
||||
},
|
||||
customCollisionMask: [
|
||||
[
|
||||
{
|
||||
x: 12.5,
|
||||
y: 1,
|
||||
},
|
||||
{
|
||||
x: 41.5,
|
||||
y: 2,
|
||||
},
|
||||
{
|
||||
x: 55.5,
|
||||
y: 31,
|
||||
},
|
||||
{
|
||||
x: 24.5,
|
||||
y: 30,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
it('returns the size of the object from the texture', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
expect(object.getWidth()).to.be(64);
|
||||
expect(object.getHeight()).to.be(64);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the object drawable X/Y', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// Texture is shown on screen at -32;-16 because of the custom origin
|
||||
expect(object.getDrawableX()).to.be(-32);
|
||||
expect(object.getDrawableY()).to.be(-16);
|
||||
|
||||
// Flipping is done relative to the center, so texture is shown on screen at 32;-16
|
||||
// after vertical flip, as the center X position is 64 (on the very right of the texture):
|
||||
object.flipX(true);
|
||||
object.flipY(false);
|
||||
expect(object.getDrawableX()).to.be(32);
|
||||
expect(object.getDrawableY()).to.be(-16);
|
||||
|
||||
// Flipping is done relative to the center, so texture is shown on screen at 32;-18
|
||||
// after vertical flip, as the center Y position is 31 (so new Y position is 2 pixels away from -16)
|
||||
object.flipX(false);
|
||||
object.flipY(true);
|
||||
expect(object.getDrawableX()).to.be(-32);
|
||||
expect(object.getDrawableY()).to.be(-18);
|
||||
|
||||
// Sanity check when flipping on both axes:
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(object.getDrawableX()).to.be(32);
|
||||
expect(object.getDrawableY()).to.be(-18);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the object center X/Y', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// getDrawableX/Y is checked in another test:
|
||||
expect(object.getDrawableX()).to.be(-32);
|
||||
expect(object.getDrawableY()).to.be(-16);
|
||||
|
||||
// Check that the center X and Y is returned relative to getDrawableX/Y:
|
||||
expect(object.getCenterX()).to.be(64);
|
||||
expect(object.getCenterY()).to.be(31);
|
||||
|
||||
// Sanity test that center position in the scene coordinates is right.
|
||||
// It's a common pattern in the game engine:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
// Check that the center X and Y is always returned relative to the object texture origin.
|
||||
object.flipX(true);
|
||||
object.flipY(false);
|
||||
expect(object.getCenterX()).to.be(0); // Center is at the very right of the texture, so after flipping is on the very left.
|
||||
expect(object.getCenterY()).to.be(31);
|
||||
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
// Check that the center X and Y is always returned relative to the object texture origin.
|
||||
object.flipX(false);
|
||||
object.flipY(true);
|
||||
expect(object.getCenterX()).to.be(64);
|
||||
expect(object.getCenterY()).to.be(33); // Center point was 1 pixel above the texture center, so is now 1 pixel below
|
||||
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
// Check that the center X and Y is always returned relative to the object texture origin.
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(object.getCenterX()).to.be(0); // Center is at the very right of the texture, so after flipping is on the very left.
|
||||
expect(object.getCenterY()).to.be(33); // Center point was 1 pixel above the texture center, so is now 1 pixel below
|
||||
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
object.setAngle(12.92);
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
object.setAngle(-12345.67);
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
});
|
||||
});
|
||||
|
||||
it('properly computes hitboxes and point positions (after flipping or rotation)', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// Check the hitboxes without any rotation (only the non default origin
|
||||
// which is at 32;16 is to be used).
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - originPointX, 30 - originPointY]);
|
||||
|
||||
// Sanity check the center position
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Sanity check the origin position
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
|
||||
// Hitbox with rotation
|
||||
object.setAngle(90);
|
||||
expect(object.getHitBoxes()[0].vertices[0][0]).to.be.within(
|
||||
61.9999,
|
||||
62.0001
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[0][1]).to.be.within(
|
||||
-36.5001,
|
||||
-36.49999
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[2][0]).to.be.within(
|
||||
31.999,
|
||||
32.0001
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[2][1]).to.be.within(
|
||||
6.4999,
|
||||
6.5001
|
||||
);
|
||||
|
||||
// Center is unchanged with rotation
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Hitbox with flipping (X axis)
|
||||
//
|
||||
// On the X axis, points are like this (P = the first vertex of the hitboxes, O = origin, C = center):
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (12.5) O (32) C (64)
|
||||
//
|
||||
// Object X position is 0, so the origin is at 0 in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (-19.5) O (0) C (32)
|
||||
//
|
||||
// Object is flipped on X axis, relative to the center, so points are like this now in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// C (32) O (64) P (83.5)
|
||||
//
|
||||
object.setAngle(0);
|
||||
object.flipX(true);
|
||||
expect(centerPointX - 12.5 + centerPointX - originPointX).to.be(83.5); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([centerPointX - 12.5 + centerPointX - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([centerPointX - 41.5 + centerPointX - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([centerPointX - 55.5 + centerPointX - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([centerPointX - 24.5 + centerPointX - originPointX, 30 - originPointY]);
|
||||
|
||||
// Center is unchanged with flipping
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Origin *point* is flipped
|
||||
expect(object.getPointX("Origin")).to.be(64);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
|
||||
// Hitbox with flipping (X and Y axis)
|
||||
//
|
||||
// Same calculations as before for the point Y positions.
|
||||
object.setAngle(0);
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(centerPointY - 1 + centerPointY - originPointY).to.be(45); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([centerPointX - 12.5 + centerPointX - originPointX, centerPointY - 1 + centerPointY - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([centerPointX - 41.5 + centerPointX - originPointX, centerPointY - 2 + centerPointY - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([centerPointX - 55.5 + centerPointX - originPointX, centerPointY - 31 + centerPointY - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([centerPointX - 24.5 + centerPointX - originPointX, centerPointY - 30 + centerPointY - originPointY]);
|
||||
|
||||
// Center is unchanged with flipping
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Origin *point* is flipped
|
||||
expect(object.getPointX("Origin")).to.be(64);
|
||||
expect(object.getPointY("Origin")).to.be(30);
|
||||
|
||||
// Hitbox with flipping (X and Y axis) and rotation
|
||||
object.setAngle(-90);
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([62, -36.5]);
|
||||
expect(object.getHitBoxes()[0].vertices[1][0]).to.be(
|
||||
61
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[1][1]).to.be.within(
|
||||
-7.5,
|
||||
-7.49
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([32, 6.5]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([33, -24.5]);
|
||||
|
||||
// Center is unchanged with flipping and rotation
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Origin *point* is flipped and rotated
|
||||
expect(object.getPointX("Origin")).to.be(47);
|
||||
expect(object.getPointY("Origin")).to.be(-17);
|
||||
});
|
||||
});
|
||||
|
||||
it('properly computes hitboxes and point positions after scaling', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// Check the hitboxes without any rotation (only the non default origin
|
||||
// which is at 32;16 is to be used).
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - originPointX, 30 - originPointY]);
|
||||
|
||||
// Sanity check the center position
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Sanity check the origin position
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
|
||||
// Hitbox with 0.5 scaling (X and Y axis)
|
||||
//
|
||||
// On the X axis, points are like this (P = the first vertex of the hitboxes, O = origin, C = center):
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (12.5) O (32) C (64)
|
||||
//
|
||||
// Object X position is 0, so the origin is at 0 in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (-19.5) O (0) C (32)
|
||||
//
|
||||
// Object is scaled, relative to the origin, so points are like this now in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (-9.75) O (0) C (16)
|
||||
//
|
||||
object.setAngle(0);
|
||||
object.setScale(0.5);
|
||||
object.flipX(false);
|
||||
object.flipY(false);
|
||||
expect((12.5 - originPointX)/2).to.be(-9.75); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([(12.5 - originPointX)/2, (1 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([(41.5 - originPointX)/2, (2 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([(55.5 - originPointX)/2, (31 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([(24.5 - originPointX)/2, (30 - originPointY)/2]);
|
||||
|
||||
// Center is moved after scaling
|
||||
expect(object.getPointX("Center")).to.be(16);
|
||||
expect(object.getPointY("Center")).to.be(7.5);
|
||||
|
||||
// Origin is unchanged after scaling
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
|
||||
// Hitbox with 0.5 scaling (X and Y axis) and flipping (X axis)
|
||||
|
||||
// Object is scaled, relative to the origin, and flipped on X axis so points are like this now in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// C (16) O (32) P (41.75)
|
||||
//
|
||||
object.setAngle(0);
|
||||
object.setScale(0.5);
|
||||
object.flipX(true);
|
||||
object.flipY(false);
|
||||
expect((centerPointX - 12.5 + centerPointX - originPointX)/2).to.be(41.75); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([(centerPointX - 12.5 + centerPointX - originPointX)/2, (1 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([(centerPointX - 41.5 + centerPointX - originPointX)/2, (2 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([(centerPointX - 55.5 + centerPointX - originPointX)/2, (31 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([(centerPointX - 24.5 + centerPointX - originPointX)/2, (30 - originPointY)/2]);
|
||||
|
||||
// Center is unchanged with flipping
|
||||
expect(object.getPointX("Center")).to.be(16);
|
||||
expect(object.getPointY("Center")).to.be(7.5);
|
||||
|
||||
// Origin *point* is flipped
|
||||
expect(object.getPointX("Origin")).to.be(32);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
});
|
||||
});
|
||||
});
|
1
GDevelop.js/.gitignore
vendored
1
GDevelop.js/.gitignore
vendored
@@ -9,3 +9,4 @@ WebIDLGrammar.pkl
|
||||
examples/demo-generated-code.js
|
||||
examples/demo-generated-game.json
|
||||
**/.DS_Store
|
||||
emsdk
|
||||
|
@@ -1514,10 +1514,12 @@ interface WholeProjectRefactorer {
|
||||
[Const, Ref] EventsFunctionsExtension eventsFunctionsExtension,
|
||||
[Const] DOMString oldName,
|
||||
[Const] DOMString newName);
|
||||
void STATIC_ObjectRenamedInLayout([Ref] Project project, [Ref] Layout layout, [Const] DOMString oldName, [Const] DOMString newName);
|
||||
void STATIC_ObjectRemovedInLayout([Ref] Project project, [Ref] Layout layout, [Const] DOMString objectName, boolean removeEventsAndGroups);
|
||||
void STATIC_GlobalObjectRenamed([Ref] Project project, [Const] DOMString oldName, [Const] DOMString newName);
|
||||
void STATIC_GlobalObjectRemoved([Ref] Project project, [Const] DOMString objectName, boolean removeEventsAndGroups);
|
||||
void STATIC_ObjectOrGroupRenamedInLayout([Ref] Project project, [Ref] Layout layout, [Const] DOMString oldName, [Const] DOMString newName, boolean isObjectGroup);
|
||||
void STATIC_ObjectOrGroupRemovedInLayout([Ref] Project project, [Ref] Layout layout, [Const] DOMString objectName, boolean isObjectGroup, boolean removeEventsAndGroups);
|
||||
void STATIC_ObjectOrGroupRenamedInEventsFunction([Ref] Project project, [Ref] EventsFunction eventsFunction, [Ref] ObjectsContainer globalObjectsContainer, [Ref] ObjectsContainer objectsContainer, [Const] DOMString oldName, [Const] DOMString newName, boolean isObjectGroup);
|
||||
void STATIC_ObjectOrGroupRemovedInEventsFunction([Ref] Project project, [Ref] EventsFunction eventsFunction, [Ref] ObjectsContainer globalObjectsContainer, [Ref] ObjectsContainer objectsContainer, [Const] DOMString objectName, boolean isObjectGroup, boolean removeEventsAndGroups);
|
||||
void STATIC_GlobalObjectOrGroupRenamed([Ref] Project project, [Const] DOMString oldName, [Const] DOMString newName, boolean isObjectGroup);
|
||||
void STATIC_GlobalObjectOrGroupRemoved([Ref] Project project, [Const] DOMString objectName, boolean isObjectGroup, boolean removeEventsAndGroups);
|
||||
[Value] SetString STATIC_GetAllObjectTypesUsingEventsBasedBehavior([Const, Ref] Project project, [Const, Ref] EventsFunctionsExtension eventsFunctionsExtension, [Const, Ref] EventsBasedBehavior eventsBasedBehavior);
|
||||
void STATIC_EnsureBehaviorEventsFunctionsProperParameters([Const, Ref] EventsFunctionsExtension eventsFunctionsExtension, [Const, Ref] EventsBasedBehavior eventsBasedBehavior);
|
||||
};
|
||||
@@ -1681,6 +1683,7 @@ interface EventsFunctionsContainer {
|
||||
void RemoveEventsFunction([Const] DOMString name);
|
||||
void MoveEventsFunction(unsigned long oldIndex, unsigned long newIndex);
|
||||
unsigned long GetEventsFunctionsCount();
|
||||
unsigned long GetEventsFunctionPosition([Const, Ref] EventsFunction eventsFunction);
|
||||
};
|
||||
|
||||
interface EventsBasedBehavior {
|
||||
@@ -1715,6 +1718,7 @@ interface EventsBasedBehaviorsList {
|
||||
void Remove([Const] DOMString name);
|
||||
void Move(unsigned long oldIndex, unsigned long newIndex);
|
||||
unsigned long GetCount();
|
||||
unsigned long GetPosition([Const, Ref] EventsBasedBehavior item);
|
||||
|
||||
unsigned long size();
|
||||
[Ref] EventsBasedBehavior at(unsigned long index);
|
||||
@@ -1770,6 +1774,7 @@ interface EventsFunctionsExtension {
|
||||
void RemoveEventsFunction([Const] DOMString name);
|
||||
void MoveEventsFunction(unsigned long oldIndex, unsigned long newIndex);
|
||||
unsigned long GetEventsFunctionsCount();
|
||||
unsigned long GetEventsFunctionPosition([Const, Ref] EventsFunction eventsFunction);
|
||||
};
|
||||
|
||||
interface AbstractFileSystem {
|
||||
@@ -2124,6 +2129,37 @@ interface TextEntryObject {
|
||||
};
|
||||
TextEntryObject implements gdObject;
|
||||
|
||||
interface SkeletonObject {
|
||||
void SkeletonObject([Const] DOMString name);
|
||||
|
||||
//Inherited from gdObject:
|
||||
void SetName([Const] DOMString name);
|
||||
[Const, Ref] DOMString GetName();
|
||||
void SetType([Const] DOMString type);
|
||||
[Const, Ref] DOMString GetType();
|
||||
void SetTags([Const] DOMString tags);
|
||||
[Const, Ref] DOMString GetTags();
|
||||
|
||||
[Value] MapStringPropertyDescriptor GetProperties([Ref] Project project);
|
||||
boolean UpdateProperty([Const] DOMString name, [Const] DOMString value, [Ref] Project project);
|
||||
|
||||
[Value] MapStringPropertyDescriptor GetInitialInstanceProperties([Const, Ref] InitialInstance instance, [Ref] Project project, [Ref] Layout scene);
|
||||
boolean UpdateInitialInstanceProperty([Ref] InitialInstance instance, [Const] DOMString name, [Const] DOMString value, [Ref] Project project, [Ref] Layout scene);
|
||||
|
||||
void ExposeResources([Ref] ArbitraryResourceWorker worker);
|
||||
|
||||
[Ref] VariablesContainer GetVariables();
|
||||
[Value] VectorString GetAllBehaviorNames();
|
||||
boolean HasBehaviorNamed([Const] DOMString name);
|
||||
BehaviorContent AddNewBehavior([Ref] Project project, [Const] DOMString type, [Const] DOMString name);
|
||||
[Ref] BehaviorContent GetBehavior([Const] DOMString name);
|
||||
void RemoveBehavior([Const] DOMString name);
|
||||
boolean RenameBehavior([Const] DOMString oldBame, [Const] DOMString name);
|
||||
|
||||
void SerializeTo([Ref] SerializerElement element);
|
||||
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
|
||||
};
|
||||
|
||||
enum ParticleEmitterObject_RendererType {
|
||||
"ParticleEmitterObject::Point",
|
||||
"ParticleEmitterObject::Line",
|
||||
|
@@ -71,6 +71,7 @@
|
||||
#include "../../Extensions/TextEntryObject/TextEntryObject.h"
|
||||
#include "../../Extensions/TextObject/TextObject.h"
|
||||
#include "../../Extensions/TiledSpriteObject/TiledSpriteObject.h"
|
||||
#include "../../Extensions/SkeletonObject/SkeletonObject.h"
|
||||
|
||||
#include <GDJS/Events/Builtin/JsCodeEvent.h>
|
||||
#include <GDJS/Events/CodeGeneration/EventsCodeGenerator.h>
|
||||
@@ -520,10 +521,12 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
|
||||
#define STATIC_Year Year
|
||||
#define STATIC_Month Month
|
||||
#define STATIC_Date Date
|
||||
#define STATIC_ObjectRenamedInLayout ObjectRenamedInLayout
|
||||
#define STATIC_ObjectRemovedInLayout ObjectRemovedInLayout
|
||||
#define STATIC_GlobalObjectRenamed GlobalObjectRenamed
|
||||
#define STATIC_GlobalObjectRemoved GlobalObjectRemoved
|
||||
#define STATIC_ObjectOrGroupRenamedInLayout ObjectOrGroupRenamedInLayout
|
||||
#define STATIC_ObjectOrGroupRemovedInLayout ObjectOrGroupRemovedInLayout
|
||||
#define STATIC_ObjectOrGroupRemovedInEventsFunction ObjectOrGroupRemovedInEventsFunction
|
||||
#define STATIC_ObjectOrGroupRenamedInEventsFunction ObjectOrGroupRenamedInEventsFunction
|
||||
#define STATIC_GlobalObjectOrGroupRenamed GlobalObjectOrGroupRenamed
|
||||
#define STATIC_GlobalObjectOrGroupRemoved GlobalObjectOrGroupRemoved
|
||||
#define STATIC_GetAllObjectTypesUsingEventsBasedBehavior GetAllObjectTypesUsingEventsBasedBehavior
|
||||
#define STATIC_EnsureBehaviorEventsFunctionsProperParameters EnsureBehaviorEventsFunctionsProperParameters
|
||||
#define STATIC_CreateRectangle CreateRectangle
|
||||
|
@@ -1,26 +1,51 @@
|
||||
module.exports = function(grunt) {
|
||||
var emscriptenPath = process.env.EMSCRIPTEN;
|
||||
var emscriptenMemoryProfiler = emscriptenPath + '/src/memoryprofiler.js';
|
||||
var cmakeToolchainpath =
|
||||
const fs = require('fs');
|
||||
const isWin = /^win/.test(process.platform);
|
||||
|
||||
const buildOutputPath = '../Binaries/Output/libGD.js/Release/';
|
||||
const buildPath = '../Binaries/embuild';
|
||||
|
||||
const emscriptenPath = process.env.EMSCRIPTEN;
|
||||
const emscriptenMemoryProfiler = emscriptenPath + '/src/memoryprofiler.js';
|
||||
const cmakeToolchainpath =
|
||||
emscriptenPath + '/cmake/Modules/Platform/Emscripten.cmake';
|
||||
var buildOutputPath = '../Binaries/Output/libGD.js/Release/';
|
||||
var buildPath = '../Binaries/embuild';
|
||||
|
||||
var isWin = /^win/.test(process.platform);
|
||||
var cmakeBinary = isWin
|
||||
? '"C:\\Program Files (x86)\\CMake\\bin\\cmake"'
|
||||
: 'emconfigure cmake';
|
||||
var cmakeArgs = isWin ? '-G "MinGW Makefiles"' : '';
|
||||
let cmakeBinary = 'emconfigure cmake';
|
||||
let makeBinary = 'emmake make';
|
||||
let cmakeArgs = '';
|
||||
|
||||
var makeBinary = isWin ? 'mingw32-make' : 'emmake make';
|
||||
// Use more specific paths on Windows
|
||||
if (isWin) {
|
||||
// Use make from MinGW
|
||||
if (!fs.existsSync('C:\\MinGW\\bin\\mingw32-make.exe')) {
|
||||
console.error(
|
||||
"🔴 Can't find mingw32-make in C:\\MinGW. Make sure MinGW is installed."
|
||||
);
|
||||
return;
|
||||
}
|
||||
makeBinary = 'emmake "C:\\MinGW\\bin\\mingw32-make"';
|
||||
|
||||
// Find CMake in usual folders or fallback to PATH.
|
||||
if (fs.existsSync('C:\\Program Files\\CMake\\bin\\cmake.exe')) {
|
||||
cmakeBinary = 'emconfigure "C:\\Program Files\\CMake\\bin\\cmake"';
|
||||
} else if (fs.existsSync('C:\\Program Files (x86)\\CMake\\bin\\cmake.exe')) {
|
||||
cmakeBinary = 'emconfigure "C:\\Program Files (x86)\\CMake\\bin\\cmake"';
|
||||
} else {
|
||||
console.log(
|
||||
"⚠️ Can't find CMake in its usual Program Files folder. Make sure you have cmake in your PATH instead."
|
||||
);
|
||||
}
|
||||
|
||||
cmakeArgs = '-G "MinGW Makefiles"';
|
||||
}
|
||||
|
||||
//Sanity checks
|
||||
var fs = require('fs');
|
||||
if (!process.env.EMSCRIPTEN) {
|
||||
console.error('🔴 EMSCRIPTEN env. variable is not set');
|
||||
console.log(
|
||||
'⚠️ Please set Emscripten environment by launching `emsdk_env` script'
|
||||
'⚠️ Please set Emscripten environment by launching `emsdk_env` script (or `emsdk_env.bat` on Windows).'
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!fs.existsSync(emscriptenMemoryProfiler)) {
|
||||
console.error(
|
||||
@@ -68,10 +93,7 @@ module.exports = function(grunt) {
|
||||
cmake: {
|
||||
src: [buildPath + '/CMakeCache.txt', 'CMakeLists.txt'],
|
||||
command:
|
||||
cmakeBinary +
|
||||
' ' +
|
||||
cmakeArgs +
|
||||
' ../.. -DFULL_VERSION_NUMBER=FALSE',
|
||||
cmakeBinary + ' ' + cmakeArgs + ' ../.. -DFULL_VERSION_NUMBER=FALSE',
|
||||
options: {
|
||||
execOptions: {
|
||||
cwd: buildPath,
|
||||
@@ -109,7 +131,7 @@ module.exports = function(grunt) {
|
||||
clean: {
|
||||
options: { force: true },
|
||||
build: {
|
||||
src: [buildOutputPath + 'libGD.js', buildOutputPath + 'libGD.min.js'],
|
||||
src: [buildPath, buildOutputPath + 'libGD.js', buildOutputPath + 'libGD.min.js'],
|
||||
},
|
||||
},
|
||||
compress: {
|
||||
|
@@ -1,31 +1,29 @@
|
||||
# GDevelop.js
|
||||
|
||||
This is a port of some parts of **GDevelop** to Javascript using **[Emscripten]**.
|
||||
This is the port of GDevelop core classes to JavaScript. This allow [GDevelop Core libraries](https://github.com/4ian/GDevelop) to run in a browser or on Node.js.
|
||||
|
||||
GDevelop is a full featured, cross-platform, open-source game creator software requiring no programming skills. Download it on [the official website](https://gdevelop-app.com).
|
||||
> 🎮 GDevelop is a full featured, cross-platform, open-source game development software requiring no programming skills. Download it on [the official website](https://gdevelop-app.com).
|
||||
|
||||
## How to build
|
||||
|
||||
- Make sure you have [CMake 3.5+](http://www.cmake.org/)
|
||||
> 👋 Usually if you're working on GDevelop editor or extensions in JavaScript, you don't need rebuilding GDevelop.js. If you want to make changes in C++ extensions or classes, read this section.
|
||||
|
||||
- Make sure you have [CMake 3.5+](http://www.cmake.org/) and [Node.js](nodejs.org/) installed.
|
||||
|
||||
- On Windows, install [MinGW](https://osdn.net/projects/mingw/releases/) (only `mingw32-base-bin` package is required).
|
||||
|
||||
- Install [Emscripten](https://github.com/kripken/emscripten), as explained on the [Emscripten installation instructions](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html):
|
||||
|
||||
```shell
|
||||
git clone https://github.com/juj/emsdk.git
|
||||
cd emsdk
|
||||
./emsdk update
|
||||
./emsdk install sdk-1.37.37-64bit
|
||||
./emsdk activate sdk-1.37.37-64bit
|
||||
source ./emsdk_env.sh
|
||||
```
|
||||
| Linux/macOS | Windows |
|
||||
|-------------|---------|
|
||||
| `git clone https://github.com/juj/emsdk.git` | `git clone https://github.com/juj/emsdk.git` |
|
||||
| `cd emsdk` | `cd emsdk`|
|
||||
| `./emsdk update` | `emsdk update` |
|
||||
| `./emsdk install sdk-1.37.37-64bit` | `emsdk install sdk-1.37.37-64bit` |
|
||||
| `./emsdk activate sdk-1.37.37-64bit` | `emsdk activate sdk-1.37.37-64bit` |
|
||||
| `source ./emsdk_env.sh` | `emsdk_env.bat` |
|
||||
|
||||
(on Windows run `emsdk` instead of `./emsdk`, and `emsdk_env.bat` instead of `source ./emsdk_env.sh`. For up-to-date information, check again [Emscripten installation instructions](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html)).
|
||||
|
||||
- Make sure you have Node.js installed and grunt:
|
||||
|
||||
```shell
|
||||
npm install -g grunt-cli
|
||||
```
|
||||
> For up-to-date information, check again [Emscripten installation instructions](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
|
||||
|
||||
- Launch the build from GDevelop.js folder:
|
||||
|
||||
@@ -37,7 +35,7 @@ source ./emsdk_env.sh
|
||||
|
||||
Output is created in _/path/to/GD/Binaries/Output/libGD.js/_.
|
||||
|
||||
- You can then launch GDevelop 5 that will use your build of Gdevelop.js:
|
||||
- You can then launch GDevelop 5 that will use your build of GDevelop.js:
|
||||
|
||||
```shell
|
||||
cd ..
|
||||
@@ -59,7 +57,6 @@ npm test
|
||||
The grunt _build_ task:
|
||||
|
||||
- create `Binaries/embuild` directory,
|
||||
- patch SFML `Config.hpp` file to make Emscripten recognized as a linux target,
|
||||
- launch CMake inside to compile GDevelop with _Emscripten toolchain file_,
|
||||
- update the glue.cpp and glue.js from Bindings.idl using _Emscripten WebIDL Binder_,
|
||||
- launch the compilation with _make_ and wrap the generated `libGD.js.raw` into the final `libGD.js` file.
|
||||
@@ -68,7 +65,5 @@ It also create a compressed `libGD.js.gz` file which is handy for distributing t
|
||||
|
||||
## Documentation
|
||||
|
||||
- The file [Bindings.idl](https://github.com/4ian/GDevelop.js/blob/master/Bindings/Bindings.idl) describes all the classes available in GDevelop.js.
|
||||
- The file [Bindings.idl](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Bindings.idl) describes all the classes available in GDevelop.js.
|
||||
- Refer to [GDevelop documentation](http://4ian.github.io/GD-Documentation/GDCore%20Documentation/) for detailed documentation of the original C++ classes.
|
||||
|
||||
[emscripten]: https://github.com/kripken/emscripten
|
||||
|
@@ -177,9 +177,9 @@ describe('libGD.js', function() {
|
||||
it('can have a name and visibility', function() {
|
||||
const layer = new gd.Layer();
|
||||
|
||||
layer.setName("GUI");
|
||||
layer.setName('GUI');
|
||||
layer.setVisibility(false);
|
||||
expect(layer.getName()).toBe("GUI");
|
||||
expect(layer.getName()).toBe('GUI');
|
||||
expect(layer.getVisibility()).toBe(false);
|
||||
|
||||
layer.delete();
|
||||
@@ -187,27 +187,27 @@ describe('libGD.js', function() {
|
||||
it('can have effects', function() {
|
||||
const layer = new gd.Layer();
|
||||
|
||||
expect(layer.hasEffectNamed("EffectThatDoesNotExist")).toBe(false);
|
||||
expect(layer.hasEffectNamed('EffectThatDoesNotExist')).toBe(false);
|
||||
expect(layer.getEffectsCount()).toBe(0);
|
||||
|
||||
layer.insertNewEffect("MyEffect", 0);
|
||||
expect(layer.hasEffectNamed("EffectThatDoesNotExist")).toBe(false);
|
||||
expect(layer.hasEffectNamed("MyEffect")).toBe(true);
|
||||
layer.insertNewEffect('MyEffect', 0);
|
||||
expect(layer.hasEffectNamed('EffectThatDoesNotExist')).toBe(false);
|
||||
expect(layer.hasEffectNamed('MyEffect')).toBe(true);
|
||||
expect(layer.getEffectsCount()).toBe(1);
|
||||
expect(layer.getEffectPosition("MyEffect")).toBe(0);
|
||||
expect(layer.getEffectPosition('MyEffect')).toBe(0);
|
||||
|
||||
const effect2 = new gd.Effect();
|
||||
effect2.setName("MyEffect2");
|
||||
effect2.setName('MyEffect2');
|
||||
|
||||
layer.insertEffect(effect2, 1);
|
||||
expect(layer.hasEffectNamed("MyEffect2")).toBe(true);
|
||||
expect(layer.hasEffectNamed('MyEffect2')).toBe(true);
|
||||
expect(layer.getEffectsCount()).toBe(2);
|
||||
expect(layer.getEffectPosition("MyEffect")).toBe(0);
|
||||
expect(layer.getEffectPosition("MyEffect2")).toBe(1);
|
||||
expect(layer.getEffectPosition('MyEffect')).toBe(0);
|
||||
expect(layer.getEffectPosition('MyEffect2')).toBe(1);
|
||||
|
||||
layer.swapEffects(0, 1);
|
||||
expect(layer.getEffectPosition("MyEffect2")).toBe(0);
|
||||
expect(layer.getEffectPosition("MyEffect")).toBe(1);
|
||||
expect(layer.getEffectPosition('MyEffect2')).toBe(0);
|
||||
expect(layer.getEffectPosition('MyEffect')).toBe(1);
|
||||
|
||||
layer.delete();
|
||||
});
|
||||
@@ -217,18 +217,23 @@ describe('libGD.js', function() {
|
||||
it('can have a name, effect name and parameters', function() {
|
||||
const effect = new gd.Effect();
|
||||
|
||||
effect.setName("MyEffect");
|
||||
effect.setEffectName("Sepia");
|
||||
expect(effect.getName()).toBe("MyEffect");
|
||||
expect(effect.getEffectName()).toBe("Sepia");
|
||||
effect.setName('MyEffect');
|
||||
effect.setEffectName('Sepia');
|
||||
expect(effect.getName()).toBe('MyEffect');
|
||||
expect(effect.getEffectName()).toBe('Sepia');
|
||||
|
||||
effect.setParameter("Brightness", 1);
|
||||
effect.setParameter("Darkness", 0.3);
|
||||
effect.setParameter("Param3", 6);
|
||||
expect(effect.getAllParameters().keys().size()).toBe(3);
|
||||
expect(effect.getParameter("Brightness")).toBe(1);
|
||||
expect(effect.getParameter("Darkness")).toBe(0.3);
|
||||
expect(effect.getParameter("Param3")).toBe(6);
|
||||
effect.setParameter('Brightness', 1);
|
||||
effect.setParameter('Darkness', 0.3);
|
||||
effect.setParameter('Param3', 6);
|
||||
expect(
|
||||
effect
|
||||
.getAllParameters()
|
||||
.keys()
|
||||
.size()
|
||||
).toBe(3);
|
||||
expect(effect.getParameter('Brightness')).toBe(1);
|
||||
expect(effect.getParameter('Darkness')).toBe(0.3);
|
||||
expect(effect.getParameter('Param3')).toBe(6);
|
||||
|
||||
effect.delete();
|
||||
});
|
||||
@@ -307,9 +312,9 @@ describe('libGD.js', function() {
|
||||
// Prepare two containers, one with 3 objects and one empty
|
||||
const objectsContainer1 = new gd.ObjectsContainer();
|
||||
const objectsContainer2 = new gd.ObjectsContainer();
|
||||
const mySpriteObject = new gd.SpriteObject("MySprite");
|
||||
const mySprite2Object = new gd.SpriteObject("MySprite2");
|
||||
const mySprite3Object = new gd.SpriteObject("MySprite3");
|
||||
const mySpriteObject = new gd.SpriteObject('MySprite');
|
||||
const mySprite2Object = new gd.SpriteObject('MySprite2');
|
||||
const mySprite3Object = new gd.SpriteObject('MySprite3');
|
||||
objectsContainer1.insertObject(mySpriteObject, 0);
|
||||
objectsContainer1.insertObject(mySprite2Object, 1);
|
||||
objectsContainer1.insertObject(mySprite3Object, 2);
|
||||
@@ -323,40 +328,68 @@ describe('libGD.js', function() {
|
||||
expect(objectsContainer1.getObjectsCount()).toBe(3);
|
||||
expect(objectsContainer2.getObjectsCount()).toBe(0);
|
||||
const mySpriteObjectPtr = gd.getPointer(objectsContainer1.getObjectAt(0));
|
||||
const mySprite2ObjectPtr = gd.getPointer(objectsContainer1.getObjectAt(1));
|
||||
const mySprite3ObjectPtr = gd.getPointer(objectsContainer1.getObjectAt(2));
|
||||
const mySprite2ObjectPtr = gd.getPointer(
|
||||
objectsContainer1.getObjectAt(1)
|
||||
);
|
||||
const mySprite3ObjectPtr = gd.getPointer(
|
||||
objectsContainer1.getObjectAt(2)
|
||||
);
|
||||
|
||||
// Move objects between containers
|
||||
objectsContainer1.moveObjectToAnotherContainer("MySprite2", objectsContainer2, 0);
|
||||
objectsContainer1.moveObjectToAnotherContainer(
|
||||
'MySprite2',
|
||||
objectsContainer2,
|
||||
0
|
||||
);
|
||||
expect(objectsContainer1.getObjectsCount()).toBe(2);
|
||||
expect(objectsContainer1.getObjectAt(0).getName()).toBe("MySprite");
|
||||
expect(objectsContainer1.getObjectAt(1).getName()).toBe("MySprite3");
|
||||
expect(objectsContainer1.getObjectAt(0).getName()).toBe('MySprite');
|
||||
expect(objectsContainer1.getObjectAt(1).getName()).toBe('MySprite3');
|
||||
expect(objectsContainer2.getObjectsCount()).toBe(1);
|
||||
expect(objectsContainer2.getObjectAt(0).getName()).toBe("MySprite2");
|
||||
expect(objectsContainer2.getObjectAt(0).getName()).toBe('MySprite2');
|
||||
|
||||
objectsContainer1.moveObjectToAnotherContainer("MySprite3", objectsContainer2, 1);
|
||||
objectsContainer1.moveObjectToAnotherContainer(
|
||||
'MySprite3',
|
||||
objectsContainer2,
|
||||
1
|
||||
);
|
||||
expect(objectsContainer1.getObjectsCount()).toBe(1);
|
||||
expect(objectsContainer1.getObjectAt(0).getName()).toBe("MySprite");
|
||||
expect(objectsContainer1.getObjectAt(0).getName()).toBe('MySprite');
|
||||
expect(objectsContainer2.getObjectsCount()).toBe(2);
|
||||
expect(objectsContainer2.getObjectAt(0).getName()).toBe("MySprite2");
|
||||
expect(objectsContainer2.getObjectAt(1).getName()).toBe("MySprite3");
|
||||
expect(objectsContainer2.getObjectAt(0).getName()).toBe('MySprite2');
|
||||
expect(objectsContainer2.getObjectAt(1).getName()).toBe('MySprite3');
|
||||
|
||||
// Check that the object in memory are the same, even if moved to another container
|
||||
expect(gd.getPointer(objectsContainer1.getObjectAt(0))).toBe(mySpriteObjectPtr);
|
||||
expect(gd.getPointer(objectsContainer2.getObjectAt(0))).toBe(mySprite2ObjectPtr);
|
||||
expect(gd.getPointer(objectsContainer2.getObjectAt(1))).toBe(mySprite3ObjectPtr);
|
||||
expect(gd.getPointer(objectsContainer1.getObjectAt(0))).toBe(
|
||||
mySpriteObjectPtr
|
||||
);
|
||||
expect(gd.getPointer(objectsContainer2.getObjectAt(0))).toBe(
|
||||
mySprite2ObjectPtr
|
||||
);
|
||||
expect(gd.getPointer(objectsContainer2.getObjectAt(1))).toBe(
|
||||
mySprite3ObjectPtr
|
||||
);
|
||||
|
||||
objectsContainer2.moveObjectToAnotherContainer("MySprite2", objectsContainer1, 0);
|
||||
objectsContainer2.moveObjectToAnotherContainer(
|
||||
'MySprite2',
|
||||
objectsContainer1,
|
||||
0
|
||||
);
|
||||
expect(objectsContainer1.getObjectsCount()).toBe(2);
|
||||
expect(objectsContainer1.getObjectAt(0).getName()).toBe("MySprite2");
|
||||
expect(objectsContainer1.getObjectAt(1).getName()).toBe("MySprite");
|
||||
expect(objectsContainer1.getObjectAt(0).getName()).toBe('MySprite2');
|
||||
expect(objectsContainer1.getObjectAt(1).getName()).toBe('MySprite');
|
||||
expect(objectsContainer2.getObjectsCount()).toBe(1);
|
||||
expect(objectsContainer2.getObjectAt(0).getName()).toBe("MySprite3");
|
||||
expect(objectsContainer2.getObjectAt(0).getName()).toBe('MySprite3');
|
||||
|
||||
// Check again that the object in memory are the same, even if moved to another container
|
||||
expect(gd.getPointer(objectsContainer1.getObjectAt(0))).toBe(mySprite2ObjectPtr);
|
||||
expect(gd.getPointer(objectsContainer1.getObjectAt(1))).toBe(mySpriteObjectPtr);
|
||||
expect(gd.getPointer(objectsContainer2.getObjectAt(0))).toBe(mySprite3ObjectPtr);
|
||||
expect(gd.getPointer(objectsContainer1.getObjectAt(0))).toBe(
|
||||
mySprite2ObjectPtr
|
||||
);
|
||||
expect(gd.getPointer(objectsContainer1.getObjectAt(1))).toBe(
|
||||
mySpriteObjectPtr
|
||||
);
|
||||
expect(gd.getPointer(objectsContainer2.getObjectAt(0))).toBe(
|
||||
mySprite3ObjectPtr
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -827,12 +860,29 @@ describe('libGD.js', function() {
|
||||
resource.delete();
|
||||
});
|
||||
it('can have metadata', function() {
|
||||
const resource = new gd.VideoResource();
|
||||
const resource = new gd.JsonResource();
|
||||
expect(resource.getMetadata()).toBe('');
|
||||
resource.setMetadata(JSON.stringify({ hello: 'world' }));
|
||||
expect(resource.getMetadata()).toBe('{"hello":"world"}');
|
||||
resource.delete();
|
||||
});
|
||||
|
||||
it('has disablePreload custom properties', function() {
|
||||
const project = gd.ProjectHelper.createNewGDJSProject();
|
||||
const resource = new gd.JsonResource();
|
||||
|
||||
const properties = resource.getProperties();
|
||||
expect(properties.get('disablePreload').getValue()).toBe('false');
|
||||
|
||||
// Note: updateProperty expect the booleans in an usual "0" or "1" format.
|
||||
resource.updateProperty('disablePreload', '1', project);
|
||||
|
||||
const updatedProperties = resource.getProperties();
|
||||
expect(updatedProperties.get('disablePreload').getValue()).toBe('true');
|
||||
|
||||
resource.delete();
|
||||
project.delete();
|
||||
});
|
||||
});
|
||||
|
||||
describe('gd.ResourcesManager', function() {
|
||||
@@ -1116,32 +1166,29 @@ describe('libGD.js', function() {
|
||||
describe('gd.NamedPropertyDescriptor', function() {
|
||||
const makeNewProperty = () => {
|
||||
const property = new gd.NamedPropertyDescriptor();
|
||||
property.setName("Property1")
|
||||
.setLabel("The first property")
|
||||
.setValue("Hello world")
|
||||
.setType("string")
|
||||
.addExtraInfo('Info1')
|
||||
.addExtraInfo('Info2');
|
||||
property
|
||||
.setName('Property1')
|
||||
.setLabel('The first property')
|
||||
.setValue('Hello world')
|
||||
.setType('string')
|
||||
.addExtraInfo('Info1')
|
||||
.addExtraInfo('Info2');
|
||||
|
||||
return property;
|
||||
}
|
||||
};
|
||||
|
||||
it('can be created and manipulated', function (){
|
||||
it('can be created and manipulated', function() {
|
||||
const property = makeNewProperty();
|
||||
expect(property.getName()).toBe('Property1');
|
||||
expect(property.getLabel()).toBe('The first property');
|
||||
expect(property.getValue()).toBe('Hello world');
|
||||
expect(property.getType()).toBe('string');
|
||||
expect(property
|
||||
.getExtraInfo()
|
||||
.toJSArray()).toContain("Info1");
|
||||
expect(property
|
||||
.getExtraInfo()
|
||||
.toJSArray()).toContain("Info2");
|
||||
expect(property.getExtraInfo().toJSArray()).toContain('Info1');
|
||||
expect(property.getExtraInfo().toJSArray()).toContain('Info2');
|
||||
|
||||
property.delete();
|
||||
});
|
||||
it('can be serialized', function (){
|
||||
it('can be serialized', function() {
|
||||
const property = makeNewProperty();
|
||||
|
||||
var serializerElement = new gd.SerializerElement();
|
||||
@@ -1156,12 +1203,8 @@ describe('libGD.js', function() {
|
||||
expect(property2.getLabel()).toBe('The first property');
|
||||
expect(property2.getValue()).toBe('Hello world');
|
||||
expect(property2.getType()).toBe('string');
|
||||
expect(property2
|
||||
.getExtraInfo()
|
||||
.toJSArray()).toContain("Info1");
|
||||
expect(property2
|
||||
.getExtraInfo()
|
||||
.toJSArray()).toContain("Info2");
|
||||
expect(property2.getExtraInfo().toJSArray()).toContain('Info1');
|
||||
expect(property2.getExtraInfo().toJSArray()).toContain('Info2');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1169,17 +1212,17 @@ describe('libGD.js', function() {
|
||||
it('can be used to store named properties', function() {
|
||||
const list = new gd.NamedPropertyDescriptorsList();
|
||||
|
||||
const property1 = list.insertNew("Property1", 0);
|
||||
expect(list.has("Property1")).toBe(true);
|
||||
const property1 = list.insertNew('Property1', 0);
|
||||
expect(list.has('Property1')).toBe(true);
|
||||
expect(list.getCount()).toBe(1);
|
||||
|
||||
expect(property1.getName()).toBe("Property1");
|
||||
expect(list.getAt(0).getName()).toBe("Property1");
|
||||
expect(property1.getName()).toBe('Property1');
|
||||
expect(list.getAt(0).getName()).toBe('Property1');
|
||||
|
||||
property1.setLabel("Property 1");
|
||||
property1.setValue("123");
|
||||
expect(list.getAt(0).getLabel()).toBe("Property 1");
|
||||
expect(list.getAt(0).getValue()).toBe("123");
|
||||
property1.setLabel('Property 1');
|
||||
property1.setValue('123');
|
||||
expect(list.getAt(0).getLabel()).toBe('Property 1');
|
||||
expect(list.getAt(0).getValue()).toBe('123');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2287,7 +2330,7 @@ describe('libGD.js', function() {
|
||||
it('should export files of the project', function() {
|
||||
// Create a project with a mix of resources, stored in /my/project folder.
|
||||
const project = new gd.ProjectHelper.createNewGDJSProject();
|
||||
project.setProjectFile("/my/project/project.json");
|
||||
project.setProjectFile('/my/project/project.json');
|
||||
const layout = project.insertNewLayout('Scene', 0);
|
||||
const resource = new gd.ImageResource();
|
||||
const resource2 = new gd.ImageResource();
|
||||
@@ -2330,10 +2373,10 @@ describe('libGD.js', function() {
|
||||
};
|
||||
fs.isAbsolute = function(fullPath) {
|
||||
return path.isAbsolute(fullPath);
|
||||
}
|
||||
};
|
||||
fs.dirExists = function(directoryPath) {
|
||||
return true; // Fake that all directory required exist.
|
||||
}
|
||||
};
|
||||
|
||||
// In particular, create a mock copyFile, that we can track to verify
|
||||
// files are properly copied.
|
||||
@@ -2347,50 +2390,135 @@ describe('libGD.js', function() {
|
||||
// * including absolute files.
|
||||
// * preserving relative file structures
|
||||
fs.copyFile.mockClear();
|
||||
gd.ProjectResourcesCopier.copyAllResourcesTo(project, fs, '/my/new/folder', false, false, true);
|
||||
gd.ProjectResourcesCopier.copyAllResourcesTo(
|
||||
project,
|
||||
fs,
|
||||
'/my/new/folder',
|
||||
false,
|
||||
false,
|
||||
true
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledTimes(5); // All 5 resources are copied
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/MyResource.png', '/my/new/folder/MyResource.png');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/MyResource.wav', '/my/new/folder/MyResource.wav');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/absolute/path/MyResource2.png', '/my/new/folder/MyResource2.png');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/test/MyResourceWithoutExtension', '/my/new/folder/test/MyResourceWithoutExtension');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/test/sub/folder/MyResourceWithoutExtension', '/my/new/folder/test/sub/folder/MyResourceWithoutExtension');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/MyResource.png',
|
||||
'/my/new/folder/MyResource.png'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/MyResource.wav',
|
||||
'/my/new/folder/MyResource.wav'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/absolute/path/MyResource2.png',
|
||||
'/my/new/folder/MyResource2.png'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/test/MyResourceWithoutExtension',
|
||||
'/my/new/folder/test/MyResourceWithoutExtension'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/test/sub/folder/MyResourceWithoutExtension',
|
||||
'/my/new/folder/test/sub/folder/MyResourceWithoutExtension'
|
||||
);
|
||||
|
||||
// Check that resources can be copied to another folder:
|
||||
// * including absolute files.
|
||||
// * NOT preserving relative file structures
|
||||
// Check that filename collisions are avoided.
|
||||
fs.copyFile.mockClear();
|
||||
gd.ProjectResourcesCopier.copyAllResourcesTo(project, fs, '/my/new/folder', false, false, false);
|
||||
gd.ProjectResourcesCopier.copyAllResourcesTo(
|
||||
project,
|
||||
fs,
|
||||
'/my/new/folder',
|
||||
false,
|
||||
false,
|
||||
false
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledTimes(5); // All 5 resources are copied
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/MyResource.png', '/my/new/folder/MyResource.png');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/MyResource.wav', '/my/new/folder/MyResource.wav');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/absolute/path/MyResource2.png', '/my/new/folder/MyResource2.png');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/test/MyResourceWithoutExtension', '/my/new/folder/MyResourceWithoutExtension');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/test/sub/folder/MyResourceWithoutExtension', '/my/new/folder/MyResourceWithoutExtension2');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/MyResource.png',
|
||||
'/my/new/folder/MyResource.png'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/MyResource.wav',
|
||||
'/my/new/folder/MyResource.wav'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/absolute/path/MyResource2.png',
|
||||
'/my/new/folder/MyResource2.png'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/test/MyResourceWithoutExtension',
|
||||
'/my/new/folder/MyResourceWithoutExtension'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/test/sub/folder/MyResourceWithoutExtension',
|
||||
'/my/new/folder/MyResourceWithoutExtension2'
|
||||
);
|
||||
|
||||
// Check that resources can be copied to another folder:
|
||||
// * without touching absolute files.
|
||||
// * preserving relative file structures
|
||||
fs.copyFile.mockClear();
|
||||
gd.ProjectResourcesCopier.copyAllResourcesTo(project, fs, '/my/new/folder', false, true, true);
|
||||
gd.ProjectResourcesCopier.copyAllResourcesTo(
|
||||
project,
|
||||
fs,
|
||||
'/my/new/folder',
|
||||
false,
|
||||
true,
|
||||
true
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledTimes(4); // Only the 4 relative resources are copied
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/MyResource.png', '/my/new/folder/MyResource.png');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/MyResource.wav', '/my/new/folder/MyResource.wav');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/test/MyResourceWithoutExtension', '/my/new/folder/test/MyResourceWithoutExtension');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/test/sub/folder/MyResourceWithoutExtension', '/my/new/folder/test/sub/folder/MyResourceWithoutExtension');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/MyResource.png',
|
||||
'/my/new/folder/MyResource.png'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/MyResource.wav',
|
||||
'/my/new/folder/MyResource.wav'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/test/MyResourceWithoutExtension',
|
||||
'/my/new/folder/test/MyResourceWithoutExtension'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/test/sub/folder/MyResourceWithoutExtension',
|
||||
'/my/new/folder/test/sub/folder/MyResourceWithoutExtension'
|
||||
);
|
||||
|
||||
// Check that resources can be copied to another folder:
|
||||
// * without touching absolute files.
|
||||
// * NOT preserving relative file structures
|
||||
// Check that filename collisions are avoided.
|
||||
fs.copyFile.mockClear();
|
||||
gd.ProjectResourcesCopier.copyAllResourcesTo(project, fs, '/my/new/folder', false, true, false);
|
||||
gd.ProjectResourcesCopier.copyAllResourcesTo(
|
||||
project,
|
||||
fs,
|
||||
'/my/new/folder',
|
||||
false,
|
||||
true,
|
||||
false
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledTimes(5); // All 5 resources are copied
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/MyResource.png', '/my/new/folder/MyResource.png');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/MyResource.wav', '/my/new/folder/MyResource.wav');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/absolute/path/MyResource2.png', '/my/new/folder/MyResource2.png');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/test/MyResourceWithoutExtension', '/my/new/folder/MyResourceWithoutExtension');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith('/my/project/test/sub/folder/MyResourceWithoutExtension', '/my/new/folder/MyResourceWithoutExtension2');
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/MyResource.png',
|
||||
'/my/new/folder/MyResource.png'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/MyResource.wav',
|
||||
'/my/new/folder/MyResource.wav'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/absolute/path/MyResource2.png',
|
||||
'/my/new/folder/MyResource2.png'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/test/MyResourceWithoutExtension',
|
||||
'/my/new/folder/MyResourceWithoutExtension'
|
||||
);
|
||||
expect(fs.copyFile).toHaveBeenCalledWith(
|
||||
'/my/project/test/sub/folder/MyResourceWithoutExtension',
|
||||
'/my/new/folder/MyResourceWithoutExtension2'
|
||||
);
|
||||
|
||||
project.delete();
|
||||
});
|
||||
@@ -2450,11 +2578,12 @@ describe('libGD.js', function() {
|
||||
instance1.setObjectName('Object1');
|
||||
instance2.setObjectName('Object2');
|
||||
|
||||
gd.WholeProjectRefactorer.objectRenamedInLayout(
|
||||
gd.WholeProjectRefactorer.objectOrGroupRenamedInLayout(
|
||||
project,
|
||||
layout,
|
||||
'Object1',
|
||||
'Object3'
|
||||
'Object3',
|
||||
/* isObjectGroup=*/ false
|
||||
);
|
||||
expect(layout.getInitialInstances().hasInstancesOfObject('Object1')).toBe(
|
||||
false
|
||||
@@ -2466,10 +2595,11 @@ describe('libGD.js', function() {
|
||||
true
|
||||
);
|
||||
|
||||
gd.WholeProjectRefactorer.objectRemovedInLayout(
|
||||
gd.WholeProjectRefactorer.objectOrGroupRemovedInLayout(
|
||||
project,
|
||||
layout,
|
||||
'Object3',
|
||||
/* isObjectGroup=*/ false,
|
||||
true
|
||||
);
|
||||
expect(layout.getInitialInstances().hasInstancesOfObject('Object1')).toBe(
|
||||
|
@@ -14,6 +14,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "grunt build",
|
||||
"clean": "grunt clean",
|
||||
"test": "jest"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
@@ -23,10 +23,10 @@ Overview of the architecture
|
||||
| `GDCpp` | GDevelop C++ game engine, used to **build native games**. |
|
||||
| `GDJS` | GDevelop JS game engine, used to build **HTML5 games**. |
|
||||
| `GDevelop.js` | Bindings of Core/GDCpp/GDJS and Extensions to JavaScript (used by the IDE). |
|
||||
| `newIDE` | The game editor, written in Javascript with React, Electron and Pixi.js. |
|
||||
| `newIDE` | The game editor, written in JavaScript with React, Electron and Pixi.js. |
|
||||
| `Extensions` | Extensions for C++ or JS game engines, providing objects, events and new features. |
|
||||
|
||||
### Documentation
|
||||
To learn more about GDevelop Architecture, read the [architecture overview here](Core/GDevelop-Architecture-Overview.md).
|
||||
|
||||
A pre-generated documentation of the Core library, C++ and JS game engines is [available here](http://4ian.github.io/GD-Documentation).
|
||||
|
||||
|
@@ -126,7 +126,9 @@ To add an editor to your object, implement the function `registerEditorConfigura
|
||||
registerEditorConfigurations: function(objectsEditorService) {
|
||||
objectsEditorService.registerEditorConfiguration(
|
||||
"MyDummyExtension::DummyObject", // Replace by your extension and object type names.
|
||||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor()
|
||||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||||
helpPagePath : "/extensions/extend-gdevelop" // The link to the help page for your object, in GDevelop wiki.
|
||||
})
|
||||
);
|
||||
}
|
||||
```
|
||||
|
@@ -3,7 +3,7 @@
|
||||
This is the GDevelop 5 editor. It is based on [React](https://facebook.github.io/react/), [Material-UI](http://www.material-ui.com), [Pixi.js](https://github.com/pixijs/pixi.js) and [Electron](https://electron.atom.io/).
|
||||
It uses GDevelop [core C++ classes compiled to Javascript](https://github.com/4ian/GDevelop.js) to work with GDevelop games.
|
||||
|
||||

|
||||

|
||||
|
||||
## 1) Installation 💻
|
||||
|
||||
@@ -27,7 +27,7 @@ Images resources, GDJS Runtime, extensions will be copied in resources, and [lib
|
||||
[build libGD.js by yourself](https://github.com/4ian/GDevelop.js) (useful if you modified GDevelop native code like extensions).
|
||||
|
||||
> Note for Linux: If you get an error message that looks like this:
|
||||
`Error: watch GD/newIDE/app/some/file ENOSPC` then follow the instructions [here](https://stackoverflow.com/questions/22475849/node-js-error-enospc) to fix.
|
||||
> `Error: watch GD/newIDE/app/some/file ENOSPC` then follow the instructions [here](https://stackoverflow.com/questions/22475849/node-js-error-enospc) to fix.
|
||||
|
||||
### Development of the standalone app
|
||||
|
||||
@@ -51,8 +51,7 @@ npm run electron-linux
|
||||
|
||||
There is a script file that automates cloning this repository, building the newIde and running it
|
||||
|
||||
* For Windows: You can download the batch script [here](https://raw.githubusercontent.com/4ian/GDevelop/master/scripts/gitCloneAndBuildGD.bat) and save it to where you want GD to be cloned to, then simply run it.
|
||||
|
||||
- For Windows: You can download the batch script [here](https://raw.githubusercontent.com/4ian/GDevelop/master/scripts/gitCloneAndBuildGD.bat) and save it to where you want GD to be cloned to, then simply run it.
|
||||
|
||||
### Development of UI components
|
||||
|
||||
@@ -82,9 +81,9 @@ It's possible to create new themes for the UI. See [this file](https://github.co
|
||||
|
||||
Make sure to have the standalone app running with Electron.
|
||||
|
||||
* If you want create/modify *a extensions*, check the [README about extensions](./README-extensions.md) for a step-by-step explanations to get started in 5 minutes.
|
||||
- If you want create/modify _a extensions_, check the [README about extensions](./README-extensions.md) for a step-by-step explanations to get started in 5 minutes.
|
||||
|
||||
* The *game engine core* ([GDJS](https://github.com/4ian/GDevelop/tree/master/GDJS)) is in [GDJS/Runtime folder](https://github.com/4ian/GDevelop/tree/master/GDJS/Runtime). If you modify anything, run the `import-GDJS-Runtime.js` script:
|
||||
- The _game engine core_ ([GDJS](https://github.com/4ian/GDevelop/tree/master/GDJS)) is in [GDJS/Runtime folder](https://github.com/4ian/GDevelop/tree/master/GDJS/Runtime). If you modify anything, run the `import-GDJS-Runtime.js` script:
|
||||
|
||||
```bash
|
||||
cd newIDE/app
|
||||
@@ -96,10 +95,19 @@ Make sure to have the standalone app running with Electron.
|
||||
|
||||
### Recommended tools for development
|
||||
|
||||
Any text editor is fine, but it's a good idea to have one with *Prettier* (code formatting), *ESLint* (code linting) and *Flow* (type checking) integration. [Modern JavaScript is used for the editor](https://github.com/4ian/GDevelop/blob/master/newIDE/docs/Supported-JavaScript-features-and-coding-style.md).
|
||||
Any text editor is fine, but it's a good idea to have one with _Prettier_ (code formatting), _ESLint_ (code linting) and _Flow_ (type checking) integration. [Modern JavaScript is used for the editor](https://github.com/4ian/GDevelop/blob/master/newIDE/docs/Supported-JavaScript-features-and-coding-style.md).
|
||||
|
||||
👉 You can use [Visual Studio Code](https://code.visualstudio.com) with these extensions: [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode), [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [Flow Language Support](https://github.com/flowtype/flow-for-vscode).
|
||||
|
||||
### Testing and developing with the cloud storage providers (Google Drive, Dropbox, OneDrive, etc...)
|
||||
|
||||
Cloud storage providers are set up with development keys when you're running GDevelop in development mode. For these, to work, you must execute the web-app not from the traditional `http://localhost:3000` origin, but from `http://gdevelop-app-local:3000`:
|
||||
|
||||
- Set up a [redirection in your hosts file](https://www.howtogeek.com/howto/27350/beginner-geek-how-to-edit-your-hosts-file/), that should look like: `127.0.0.1 gdevelop-app-local.com`.
|
||||
- Launch then the webapp from `http://gdevelop-app-local:3000`.
|
||||
|
||||
> This is only necessary if you want to have cloud storage providers working in development. If not done, GDevelop will simply display an error while trying to use them.
|
||||
|
||||
## (Optional) Building and deploying the standalone app 📦
|
||||
|
||||
> 🖐 This section is only for maintainers that want to deploy the "official app" on the GDevelop website. If you're working on contributions for GDevelop, you won't need it. You can download ["Nightly Builds" of GDevelop here too](./docs/Nightly-Builds-and-continuous-deployment.md).
|
||||
@@ -133,6 +141,7 @@ yarn deploy # or npm run deploy
|
||||
### (Optional) Updating translations
|
||||
|
||||
Extract translations from the editor, as well as GDevelop Core and extensions:
|
||||
|
||||
```bash
|
||||
cd newIDE/app
|
||||
yarn extract-all-translations # or npm run extract-all-translations
|
||||
@@ -150,12 +159,12 @@ yarn compile-translations # or npm run compile-translations
|
||||
|
||||
The editor, the game engine and extensions are always in development. Your contribution is welcome!
|
||||
|
||||
* Check the [the **roadmap** for ideas and features planned](https://trello.com/b/qf0lM7k8/gdevelop-roadmap).
|
||||
- Check the [the **roadmap** for ideas and features planned](https://trello.com/b/qf0lM7k8/gdevelop-roadmap).
|
||||
|
||||
You can contribute by picking anything here or anything that you think is missing or could be improved in GD5! If you don't know how to start, it's a good idea to play a bit with the editor and see if there is something that is unavailable and that you can add or fix.
|
||||
|
||||
* Follow the [Development](https://github.com/4ian/GDevelop/tree/master/newIDE#development) section of the README to set up GDevelop and start modifying either **the editor** or **[the game engine/extensions](https://github.com/4ian/GDevelop/tree/master/newIDE#development-of-the-game-engine-or-extensions)**.
|
||||
- Follow the [Development](https://github.com/4ian/GDevelop/tree/master/newIDE#development) section of the README to set up GDevelop and start modifying either **the editor** or **[the game engine/extensions](https://github.com/4ian/GDevelop/tree/master/newIDE#development-of-the-game-engine-or-extensions)**.
|
||||
|
||||
* To submit your changes, you have first to create a Fork on GitHub (use the Fork button on the top right), then [create a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
||||
- To submit your changes, you have first to create a Fork on GitHub (use the Fork button on the top right), then [create a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
||||
|
||||
* Finally, make sure that the tests pass (refer to this README and to the [game engine README](https://github.com/4ian/GDevelop/tree/master/GDJS)) for learning how to run tests.
|
||||
- Finally, make sure that the tests pass (refer to this README and to the [game engine README](https://github.com/4ian/GDevelop/tree/master/GDJS)) for learning how to run tests.
|
||||
|
5
newIDE/app/.gitignore
vendored
5
newIDE/app/.gitignore
vendored
@@ -22,8 +22,9 @@ public/libGD.js
|
||||
# Third party editors
|
||||
public/external/piskel/piskel-editor
|
||||
public/external/piskel/piskel-editor.zip
|
||||
public/external/monaco-editor-min
|
||||
public/external/jfxr/jfxr-editor/
|
||||
public/external/monaco-editor-min/*
|
||||
public/external/jfxr/jfxr-editor/*
|
||||
public/external/yarn/yarn-editor/*
|
||||
|
||||
# Resources
|
||||
resources/GDJS
|
||||
|
@@ -1,7 +1,10 @@
|
||||
import { configure } from '@storybook/react';
|
||||
import { configure, addDecorator } from '@storybook/react';
|
||||
import i18nProviderDecorator from '../src/stories/I18nProviderDecorator';
|
||||
|
||||
function loadStories() {
|
||||
require('../src/stories');
|
||||
}
|
||||
|
||||
addDecorator(i18nProviderDecorator);
|
||||
|
||||
configure(loadStories, module);
|
||||
|
38
newIDE/app/flow-typed/npm/flat_vx.x.x.js
vendored
38
newIDE/app/flow-typed/npm/flat_vx.x.x.js
vendored
@@ -1,38 +0,0 @@
|
||||
// flow-typed signature: c08181a4b3d833ea6a7fac4d3b51da8c
|
||||
// flow-typed version: <<STUB>>/flat_v2.0.1/flow_v0.78.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* 'flat'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module 'flat' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module 'flat/test/test' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module 'flat/index' {
|
||||
declare module.exports: $Exports<'flat'>;
|
||||
}
|
||||
declare module 'flat/index.js' {
|
||||
declare module.exports: $Exports<'flat'>;
|
||||
}
|
||||
declare module 'flat/test/test.js' {
|
||||
declare module.exports: $Exports<'flat/test/test'>;
|
||||
}
|
8053
newIDE/app/flow-typed/npm/material-ui_vx.x.x.js
vendored
8053
newIDE/app/flow-typed/npm/material-ui_vx.x.x.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
// flow-typed signature: 2991c1bc398f4e5a314e188fa39d27f2
|
||||
// flow-typed version: 1ad2a03c70/why-did-you-update_v0.x.x/flow_>=v0.41.x
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
declare module "why-did-you-update" {
|
||||
declare export function whyDidYouUpdate(
|
||||
React: {
|
||||
Component: typeof React$Component
|
||||
},
|
||||
options?: Object
|
||||
): void;
|
||||
}
|
7972
newIDE/app/package-lock.json
generated
7972
newIDE/app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -29,22 +29,22 @@
|
||||
"@blueprintjs/core": "file:src/Utils/BlueprintJsPlaceholder",
|
||||
"@blueprintjs/icons": "file:src/Utils/BlueprintJsPlaceholder",
|
||||
"@lingui/react": "git://github.com/4ian/lingui-react.git#master",
|
||||
"algoliasearch": "^3.30.0",
|
||||
"@material-ui/core": "4.3.2",
|
||||
"@material-ui/icons": "4.2.1",
|
||||
"algoliasearch": "3.33.0",
|
||||
"aws-sdk": "^2.100.0",
|
||||
"axios": "^0.16.1",
|
||||
"blueimp-md5": "^2.10.0",
|
||||
"classnames": "2.2.5",
|
||||
"create-react-context": "0.1.6",
|
||||
"date-fns": "^1.29.0",
|
||||
"downshift": "^3.2.12",
|
||||
"element-closest": "2.0.2",
|
||||
"firebase": "^6.1.0",
|
||||
"flat": "2.0.1",
|
||||
"fontfaceobserver": "2.0.13",
|
||||
"keen-tracking": "1.1.3",
|
||||
"lodash": "4.17.4",
|
||||
"material-ui": "0.20",
|
||||
"node-require-function": "^1.2.0",
|
||||
"pixi-simple-gesture": "git://github.com/4ian/pixi-simple-gesture#v0.3.2",
|
||||
"pixi-simple-gesture": "git://github.com/4ian/pixi-simple-gesture#v0.3.3",
|
||||
"pixi.js": "4.8.6",
|
||||
"prop-types": "^15.5.10",
|
||||
"randomcolor": "^0.5.3",
|
||||
@@ -53,6 +53,8 @@
|
||||
"react-color": "2.13.8",
|
||||
"react-dnd": "7.7.0",
|
||||
"react-dnd-html5-backend": "7.7.0",
|
||||
"react-dnd-multi-backend": "3.2.2",
|
||||
"react-dnd-touch-backend": "0.8.3",
|
||||
"react-dom": "16.8.6",
|
||||
"react-error-boundary": "^1.2.0",
|
||||
"react-json-view": "^1.16.1",
|
||||
@@ -65,13 +67,13 @@
|
||||
"react-test-renderer": "16.8.6",
|
||||
"react-virtualized": "9.21.1",
|
||||
"slugs": "0.1.3",
|
||||
"source-map-explorer": "^1.4.0",
|
||||
"source-map-explorer": "^2.0.1",
|
||||
"url-search-params": "^1.0.2",
|
||||
"wait-promise": "0.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "npm run import-resources",
|
||||
"import-resources": "cd scripts && node import-libGD.js && node import-GDJS-Runtime.js && node import-monaco-editor.js && node import-zipped-editor.js piskel 5.0.0-beta56 a2c36775109a1c1181d0b9ab9b7c903365c4ecb340118f1237b66236f23e20dd && node import-zipped-editor.js jfxr 5.0.0-beta55 8ac12b557c2ddba958c6f0d3e0c5df8cf3369a65262dcb90cf5c8a7a7d20bdf6",
|
||||
"import-resources": "cd scripts && node import-libGD.js && node import-GDJS-Runtime.js && node import-monaco-editor.js && node import-zipped-editor.js piskel 5.0.0-beta56 a2c36775109a1c1181d0b9ab9b7c903365c4ecb340118f1237b66236f23e20dd && node import-zipped-editor.js jfxr 5.0.0-beta55 8ac12b557c2ddba958c6f0d3e0c5df8cf3369a65262dcb90cf5c8a7a7d20bdf6 && node import-zipped-editor.js yarn 5.0.0-beta80 5930c686ccf2b6fd3433b1c9539483473a6aedb8b6a73dd13c07030c95efecb9",
|
||||
"start": "npm run import-resources && react-scripts start",
|
||||
"electron-win": "cd ../electron-app && node node_modules/electron/cli.js app",
|
||||
"electron-linux": "cd ../electron-app && ./node_modules/electron/dist/electron app",
|
||||
@@ -96,6 +98,10 @@
|
||||
"includeGlob": [
|
||||
"src/**/*.js"
|
||||
],
|
||||
"excludeGlob": [
|
||||
"node_modules/**",
|
||||
"src/locales/**"
|
||||
],
|
||||
"type": [
|
||||
"text",
|
||||
"html",
|
||||
|
91
newIDE/app/public/external/jfxr/jfxr-main.js
vendored
91
newIDE/app/public/external/jfxr/jfxr-main.js
vendored
@@ -1,53 +1,61 @@
|
||||
import { createPathEditorHeader } from '../utils/path-editor.js'
|
||||
import { createPathEditorHeader } from '../utils/path-editor.js';
|
||||
|
||||
const electron = require('electron')
|
||||
const ipcRenderer = electron.ipcRenderer
|
||||
const fs = require('fs')
|
||||
const remote = electron.remote
|
||||
const electron = require('electron');
|
||||
const electronWindow = electron.remote.getCurrentWindow();
|
||||
const ipcRenderer = electron.ipcRenderer;
|
||||
const fs = require('fs');
|
||||
const remote = electron.remote;
|
||||
|
||||
let jfxr = null
|
||||
let jfxr = null;
|
||||
|
||||
const closeWindow = () => {
|
||||
remote.getCurrentWindow().close()
|
||||
}
|
||||
remote.getCurrentWindow().close();
|
||||
};
|
||||
|
||||
const loadMetaData = externalEditorData => {
|
||||
if ('jfxr' in externalEditorData) {
|
||||
jfxr.getSound().parse(externalEditorData.jfxr.data);
|
||||
} else {
|
||||
jfxr.applyPreset(jfxr.presets[1])
|
||||
jfxr.applyPreset(jfxr.presets[1]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const saveSoundEffect = pathEditor => {
|
||||
const externalEditorData = {
|
||||
data: jfxr.getSound().serialize(),
|
||||
name: pathEditor.state.name
|
||||
}
|
||||
name: pathEditor.state.name,
|
||||
};
|
||||
|
||||
jfxr.synth.run().then(data => {
|
||||
var blob = new Blob([data.toWavBytes()], {
|
||||
type: 'audio/wav'
|
||||
})
|
||||
var fileReader = new FileReader()
|
||||
fileReader.onload = function () {
|
||||
fs.writeFileSync(pathEditor.state.fullPath, Buffer(new Uint8Array(this.result)))
|
||||
ipcRenderer.send('jfxr-changes-saved', pathEditor.state.fullPath, externalEditorData)
|
||||
closeWindow()
|
||||
}
|
||||
fileReader.readAsArrayBuffer(blob)
|
||||
})
|
||||
}
|
||||
type: 'audio/wav',
|
||||
});
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function() {
|
||||
fs.writeFileSync(
|
||||
pathEditor.state.fullPath,
|
||||
Buffer(new Uint8Array(this.result))
|
||||
);
|
||||
ipcRenderer.send(
|
||||
'jfxr-changes-saved',
|
||||
pathEditor.state.fullPath,
|
||||
externalEditorData
|
||||
);
|
||||
closeWindow();
|
||||
};
|
||||
fileReader.readAsArrayBuffer(blob);
|
||||
});
|
||||
};
|
||||
|
||||
// Gain access to JFXR controller by using the signal that the JFXR author kindly provided.
|
||||
// It gets fired upon loading of jfxr in the iframe element
|
||||
const editorFrameEl = document.getElementById('jfxr-frame')
|
||||
window.addEventListener('jfxrReady', (e) => {
|
||||
const editorFrameEl = document.getElementById('jfxr-frame');
|
||||
window.addEventListener('jfxrReady', e => {
|
||||
jfxr = e.mainCtrl;
|
||||
ipcRenderer.send('jfxr-ready');
|
||||
});
|
||||
// Trigger the load of Jfxr manually, to ensure the event listener "jfxrReady" is registered already
|
||||
editorFrameEl.src = 'jfxr-editor/index.html'
|
||||
editorFrameEl.src = 'jfxr-editor/index.html';
|
||||
|
||||
// Called to load a sound. Should be called after the window is fully loaded.
|
||||
ipcRenderer.on('jfxr-open', (event, receivedOptions) => {
|
||||
@@ -55,14 +63,6 @@ ipcRenderer.on('jfxr-open', (event, receivedOptions) => {
|
||||
|
||||
// Load a custom save file(s) header
|
||||
const pathEditorHeaderDiv = document.getElementById('path-editor-header');
|
||||
const headerStyle = {
|
||||
saveFolderLabel: 'float: left;margin-left: 2px; font-size:15px;margin-top: 10px;color:aqua',
|
||||
nameInput: 'font-family:"Courier New";height:27px;width:90px;float:left;margin-left: 2px;padding:4px;margin-top: 4px;font-size:15px;border: 2px solid #e5cd50;border-radius: 3px;background-color:black; color: #e5cd50;',
|
||||
saveButton: 'float:right;margin-left:2px;margin-right:4px;border: 2px solid white;border-radius: 1px;margin-top: 5px;background-color:white;',
|
||||
cancelButton: 'float:right;margin-right:2px;border: 2px solid white;border-radius: 1px;margin-top: 5px;background-color:white;',
|
||||
setFolderButton: 'float:right;margin-left:2px;margin-right:4px;border: 2px solid white;border-radius: 1px;margin-top: 5px;background-color:white;',
|
||||
fileExistsLabel: 'height:27px;color:blue;float: left;margin-left: 2px;margin-top: 10px; font-size:15px;'
|
||||
};
|
||||
createPathEditorHeader({
|
||||
parentElement: pathEditorHeaderDiv,
|
||||
editorContentDocument: document,
|
||||
@@ -71,18 +71,29 @@ ipcRenderer.on('jfxr-open', (event, receivedOptions) => {
|
||||
projectPath: receivedOptions.projectPath,
|
||||
initialResourcePath: receivedOptions.resourcePath,
|
||||
extension: '.wav',
|
||||
headerStyle
|
||||
});
|
||||
|
||||
electronWindow.setTitle(
|
||||
'GDevelop Sound Effects Editor (Jfxr) - ' +
|
||||
(receivedOptions.resourcePath
|
||||
? receivedOptions.resourcePath
|
||||
: receivedOptions.projectPath)
|
||||
);
|
||||
|
||||
// Disable google analytics from collecting personal information
|
||||
editorFrameEl.contentWindow.ga('set', 'allowAdFeatures', false);
|
||||
// Alter the interface of the external editor
|
||||
const editorContentDocument = editorFrameEl.contentDocument;
|
||||
editorContentDocument.getElementsByClassName('github')[0].remove();
|
||||
// Disable inside iframe links - they break the embedding
|
||||
editorContentDocument.getElementsByClassName('titlepane column-left')[0].childNodes[0].onclick = () => {
|
||||
return false
|
||||
editorContentDocument.getElementsByClassName(
|
||||
'titlepane column-left'
|
||||
)[0].childNodes[0].onclick = () => {
|
||||
return false;
|
||||
};
|
||||
editorContentDocument.getElementsByClassName('titlepane column-left')[0].childNodes[1].onclick = () => {
|
||||
return false
|
||||
editorContentDocument.getElementsByClassName(
|
||||
'titlepane column-left'
|
||||
)[0].childNodes[1].onclick = () => {
|
||||
return false;
|
||||
};
|
||||
})
|
||||
});
|
||||
|
53
newIDE/app/public/external/jfxr/jfxr-style.css
vendored
53
newIDE/app/public/external/jfxr/jfxr-style.css
vendored
@@ -7,7 +7,7 @@ body {
|
||||
font-family: Helvetica;
|
||||
margin: 0;
|
||||
overflow-y: hidden;
|
||||
background-color: black;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
#jfxr-frame {
|
||||
@@ -16,3 +16,54 @@ body {
|
||||
border: none;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#path-editor-header .leftSide {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#path-editor-header > span > * {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
#path-editor-header > span > label {
|
||||
height: 27px;
|
||||
color: aqua;
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#path-editor-header .rightButtons {
|
||||
display: flex;
|
||||
padding-bottom: 4px;
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
#path-editor-header .rightButtons > button {
|
||||
right: 0;
|
||||
margin-right: 4px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#path-editor-header > button,
|
||||
#path-editor-header > span > button {
|
||||
background-color: whitesmoke;
|
||||
border: 2px solid lightblue;
|
||||
border-radius: 3px;
|
||||
max-height: 30px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#path-editor-header > span > input {
|
||||
font-family: 'Courier New';
|
||||
height: 27px;
|
||||
width: 90px;
|
||||
float: left;
|
||||
padding: 4px;
|
||||
margin-top: 4px;
|
||||
font-size: 15px;
|
||||
border: 2px solid #e5cd50;
|
||||
border-radius: 3px;
|
||||
background-color: black;
|
||||
color: #e5cd50;
|
||||
}
|
||||
|
45
newIDE/app/public/external/piskel/piskel-main.js
vendored
45
newIDE/app/public/external/piskel/piskel-main.js
vendored
@@ -1,5 +1,6 @@
|
||||
import { createPathEditorHeader } from '../utils/path-editor.js';
|
||||
const electron = require('electron');
|
||||
const electronWindow = electron.remote.getCurrentWindow();
|
||||
const ipcRenderer = electron.ipcRenderer;
|
||||
const fs = require('fs');
|
||||
const async = require('async');
|
||||
@@ -14,8 +15,7 @@ const updateFrameElements = () => {
|
||||
if (piskelOptions.singleFrame) {
|
||||
editorContentDocument.getElementsByClassName(
|
||||
'preview-list-wrapper'
|
||||
)[0].style.display =
|
||||
'none';
|
||||
)[0].style.display = 'none';
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -46,8 +46,8 @@ const readBase64ImageFile = file => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the content to the specified file
|
||||
*/
|
||||
* Save the content to the specified file
|
||||
*/
|
||||
const saveToFile = (content, filePath, callback) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
@@ -306,7 +306,6 @@ const loadPiskelDataFromGd = () => {
|
||||
const layer = piskelController.getLayers()[0];
|
||||
const removeFrameIndex = layer.getFrames().indexOf(frameToDelete);
|
||||
if (removeFrameIndex !== -1) {
|
||||
|
||||
// Always keep the frame count at 1 or above by inserting an empty frame if we're reaching 0 - as Piskel does not support having no frames
|
||||
if (piskelController.getFrameCount() === 1) {
|
||||
piskelController.setCurrentFrameIndex(
|
||||
@@ -344,7 +343,7 @@ const loadPiskelDataFromGd = () => {
|
||||
* Inject custom buttons in Piskel's header,
|
||||
* get rid of the new file button,
|
||||
* make animation name and path editable
|
||||
*/
|
||||
*/
|
||||
ipcRenderer.on('piskel-load-animation', (event, receivedOptions) => {
|
||||
piskelOptions = receivedOptions;
|
||||
|
||||
@@ -365,31 +364,18 @@ ipcRenderer.on('piskel-load-animation', (event, receivedOptions) => {
|
||||
|
||||
// Load a custom save file(s) header
|
||||
const pathEditorHeaderDiv = document.getElementById('path-editor-header');
|
||||
const headerStyle = {
|
||||
saveFolderLabel:
|
||||
'float: left;margin-left: 2px; font-size:15px;margin-top: 10px;color:aqua',
|
||||
nameInput:
|
||||
'font-family:"Courier New";height:27px;width:90px;float:left;margin-left: 2px;padding:4px;margin-top: 4px;font-size:15px;border: 2px solid #e5cd50;border-radius: 3px;background-color:black; color: #e5cd50;',
|
||||
saveButton:
|
||||
'float:right;margin-left:2px;margin-right:4px;border: 2px solid white;border-radius: 1px;margin-top: 5px;background-color:white;',
|
||||
cancelButton:
|
||||
'float:right;margin-left:2px;margin-right:2px;border: 2px solid white;border-radius: 1px;margin-top: 5px;background-color:white;',
|
||||
setFolderButton:
|
||||
'float:right;margin-left:2px;margin-right:2px;border: 2px solid white;border-radius: 1px;margin-top: 5px;background-color:white;',
|
||||
fileExistsLabel:
|
||||
'height:27px;color:aqua;float: left;margin-left: 2px;margin-top: 10px; font-size:15px;',
|
||||
};
|
||||
const initialResourcePath =
|
||||
receivedOptions.resources[0] === undefined
|
||||
? ''
|
||||
: receivedOptions.resources[0].resourcePath;
|
||||
|
||||
const savePathEditor = createPathEditorHeader({
|
||||
parentElement: pathEditorHeaderDiv,
|
||||
editorContentDocument: document,
|
||||
onSaveToGd: saveToGD,
|
||||
onCancelChanges: closeWindow,
|
||||
projectPath: receivedOptions.projectPath,
|
||||
initialResourcePath:
|
||||
receivedOptions.resources[0] === undefined
|
||||
? ''
|
||||
: receivedOptions.resources[0].resourcePath,
|
||||
headerStyle,
|
||||
initialResourcePath,
|
||||
name: receivedOptions.name,
|
||||
extension: piskelOptions.singleFrame ? '.png' : undefined,
|
||||
});
|
||||
@@ -403,6 +389,15 @@ ipcRenderer.on('piskel-load-animation', (event, receivedOptions) => {
|
||||
piskelOptions.singleFrame
|
||||
);
|
||||
|
||||
electronWindow.setTitle(
|
||||
'GDevelop Pixel Editor (Piskel) - ' +
|
||||
path.normalize(
|
||||
!piskelOptions.singleFrame
|
||||
? receivedOptions.projectPath + '/' + receivedOptions.name
|
||||
: initialResourcePath
|
||||
)
|
||||
);
|
||||
|
||||
// If there were no resources sent by GD, create an empty piskel document
|
||||
if (receivedOptions.resources.length === 0) {
|
||||
piskelCreateAnimation();
|
||||
|
@@ -2,15 +2,67 @@
|
||||
* Additional styles for the embedded Piskel editor
|
||||
*/
|
||||
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
font-family: Helvetica;
|
||||
margin: 0;
|
||||
overflow-y:hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#piskel-frame {
|
||||
width:100%;
|
||||
height:100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
overflow-y:hidden;
|
||||
}
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#path-editor-header .leftSide {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#path-editor-header > span > * {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
#path-editor-header > span > label {
|
||||
height: 27px;
|
||||
color: aqua;
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#path-editor-header .rightButtons {
|
||||
display: flex;
|
||||
padding-bottom: 4px;
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
#path-editor-header .rightButtons > button {
|
||||
right: 0;
|
||||
margin-right: 4px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#path-editor-header > button,
|
||||
#path-editor-header > span > button {
|
||||
background-color: whitesmoke;
|
||||
border: 2px solid lightblue;
|
||||
border-radius: 3px;
|
||||
max-height: 30px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#path-editor-header > span > input {
|
||||
font-family: 'Courier New';
|
||||
height: 27px;
|
||||
width: 90px;
|
||||
float: left;
|
||||
padding: 4px;
|
||||
margin-top: 4px;
|
||||
font-size: 15px;
|
||||
border: 2px solid #e5cd50;
|
||||
border-radius: 3px;
|
||||
background-color: black;
|
||||
color: #e5cd50;
|
||||
}
|
||||
|
73
newIDE/app/public/external/utils/path-editor.js
vendored
73
newIDE/app/public/external/utils/path-editor.js
vendored
@@ -12,7 +12,6 @@ export const createPathEditorHeader = ({
|
||||
projectPath,
|
||||
initialResourcePath,
|
||||
extension,
|
||||
headerStyle,
|
||||
name,
|
||||
}) => {
|
||||
if (fs.existsSync(initialResourcePath)) {
|
||||
@@ -32,46 +31,52 @@ export const createPathEditorHeader = ({
|
||||
: path.basename(name, path.extname(name)),
|
||||
extension: !extension ? undefined : path.extname(initialResourcePath),
|
||||
projectBasePath: path.normalize(projectPath),
|
||||
visible: true,
|
||||
},
|
||||
};
|
||||
|
||||
const state = headerObject.state;
|
||||
headerObject.root = editorContentDocument.createElement('span');
|
||||
headerObject.root.className = 'leftSide';
|
||||
parentElement.appendChild(headerObject.root);
|
||||
|
||||
headerObject.rightButtons = editorContentDocument.createElement('span');
|
||||
parentElement.appendChild(headerObject.rightButtons);
|
||||
headerObject.rightButtons.className = 'rightButtons';
|
||||
|
||||
// create the dom elements of the ui
|
||||
headerObject.saveFolderLabel = editorContentDocument.createElement('label');
|
||||
headerObject.saveFolderLabel.style = headerStyle.saveFolderLabel;
|
||||
headerObject.saveFolderLabel.textContent = state.folderPath;
|
||||
parentElement.appendChild(headerObject.saveFolderLabel);
|
||||
headerObject.root.appendChild(headerObject.saveFolderLabel);
|
||||
|
||||
headerObject.nameInput = editorContentDocument.createElement('input');
|
||||
headerObject.nameInput.type = 'text';
|
||||
headerObject.nameInput.style = headerStyle.nameInput;
|
||||
headerObject.nameInput.value = state.name;
|
||||
parentElement.appendChild(headerObject.nameInput);
|
||||
headerObject.root.appendChild(headerObject.nameInput);
|
||||
|
||||
headerObject.fileExistsLabel = editorContentDocument.createElement('label');
|
||||
headerObject.fileExistsLabel.style = headerStyle.fileExistsLabel;
|
||||
headerObject.fileExistsLabel.textContent = '-';
|
||||
parentElement.appendChild(headerObject.fileExistsLabel);
|
||||
headerObject.root.appendChild(headerObject.fileExistsLabel);
|
||||
|
||||
headerObject.hideButton = editorContentDocument.createElement('button');
|
||||
headerObject.hideButton.textContent = headerObject.state.visible ? '>' : '<';
|
||||
parentElement.appendChild(headerObject.hideButton);
|
||||
|
||||
headerObject.saveButton = editorContentDocument.createElement('button');
|
||||
headerObject.saveButton.textContent = 'Save';
|
||||
headerObject.saveButton.style = headerStyle.saveButton;
|
||||
parentElement.appendChild(headerObject.saveButton);
|
||||
headerObject.rightButtons.appendChild(headerObject.saveButton);
|
||||
|
||||
headerObject.cancelButton = editorContentDocument.createElement('button');
|
||||
headerObject.cancelButton.textContent = 'Cancel';
|
||||
headerObject.cancelButton.style = headerStyle.cancelButton;
|
||||
parentElement.appendChild(headerObject.cancelButton);
|
||||
headerObject.rightButtons.appendChild(headerObject.cancelButton);
|
||||
|
||||
headerObject.openFolderButton = editorContentDocument.createElement('button');
|
||||
headerObject.openFolderButton.textContent = 'Locate';
|
||||
headerObject.openFolderButton.style = headerStyle.cancelButton;
|
||||
parentElement.appendChild(headerObject.openFolderButton);
|
||||
headerObject.rightButtons.appendChild(headerObject.openFolderButton);
|
||||
|
||||
headerObject.setFolderButton = editorContentDocument.createElement('button');
|
||||
headerObject.setFolderButton.textContent = 'Set Folder';
|
||||
headerObject.setFolderButton.style = headerStyle.setFolderButton;
|
||||
parentElement.appendChild(headerObject.setFolderButton);
|
||||
headerObject.rightButtons.appendChild(headerObject.setFolderButton);
|
||||
|
||||
// From here on we hook the dom with the imported or local methods via event listeners
|
||||
headerObject.nameInput.addEventListener('input', () => {
|
||||
@@ -80,6 +85,17 @@ export const createPathEditorHeader = ({
|
||||
headerObject.saveButton.addEventListener('click', () => {
|
||||
onSaveToGd(headerObject);
|
||||
});
|
||||
/**
|
||||
* Toggles the path editor
|
||||
*/
|
||||
headerObject.toggle = () => {
|
||||
headerObject.state.visible = !headerObject.state.visible;
|
||||
render(headerObject);
|
||||
};
|
||||
headerObject.hideButton.addEventListener('click', () => {
|
||||
headerObject.toggle();
|
||||
render(headerObject);
|
||||
});
|
||||
headerObject.cancelButton.addEventListener('click', onCancelChanges);
|
||||
const selectFolderPath = () => {
|
||||
selectBaseFolderPath(headerObject);
|
||||
@@ -93,8 +109,8 @@ export const createPathEditorHeader = ({
|
||||
headerObject.openFolderButton.addEventListener('click', openFolderPath);
|
||||
|
||||
/**
|
||||
* Disables the path editor
|
||||
*/
|
||||
* Disables the path editor
|
||||
*/
|
||||
headerObject.disableSavePathControls = () => {
|
||||
headerObject.saveFolderLabel.removeEventListener('click', selectFolderPath);
|
||||
headerObject.nameInput.style.color = '#8bb0b2';
|
||||
@@ -108,9 +124,9 @@ export const createPathEditorHeader = ({
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a path for a file that does not exist yet.
|
||||
* Used to avoid unwanted file overwriting.
|
||||
*/
|
||||
* Returns a path for a file that does not exist yet.
|
||||
* Used to avoid unwanted file overwriting.
|
||||
*/
|
||||
headerObject.makeFileNameUnique = (filePath, missingExtension) => {
|
||||
if (!fileExists(filePath)) {
|
||||
return filePath;
|
||||
@@ -134,6 +150,21 @@ export const createPathEditorHeader = ({
|
||||
};
|
||||
|
||||
const render = headerObject => {
|
||||
const pathEditorVisibility = headerObject.state.visible
|
||||
? 'visibility:visible'
|
||||
: 'visibility:hidden;';
|
||||
headerObject.rightButtons.style = pathEditorVisibility;
|
||||
headerObject.root.style = pathEditorVisibility;
|
||||
|
||||
headerObject.hideButton.textContent = headerObject.state.visible ? '>' : '<';
|
||||
headerObject.hideButton.style = headerObject.state.visible
|
||||
? 'opacity: 1'
|
||||
: 'opacity:0.5';
|
||||
|
||||
headerObject.root.parentElement.style = headerObject.state.visible
|
||||
? 'position: relative; display:flex; width:100%'
|
||||
: 'position: absolute; display:flex; width:100%';
|
||||
|
||||
headerObject.nameInput.value = headerObject.nameInput.value.replace(
|
||||
/[^a-zA-Z0-9_-]/g,
|
||||
''
|
||||
@@ -163,7 +194,7 @@ const render = headerObject => {
|
||||
}
|
||||
};
|
||||
|
||||
const fileExists = path => {
|
||||
export const fileExists = path => {
|
||||
try {
|
||||
return fs.statSync(path).isFile();
|
||||
} catch (e) {
|
||||
|
7
newIDE/app/public/external/yarn/README.md
vendored
Normal file
7
newIDE/app/public/external/yarn/README.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
This folder contains sources to embed Yarn editor (https://github.com/InfiniteAmmoInc/Yarn) so that it can
|
||||
be used directly from GDevelop to edit/create Dialogue Trees.
|
||||
|
||||
Yarn sources are downloaded by `import-zipped-editor.js` script. They are raw, unchanged sources
|
||||
of the Yarn editor build. Sources will be stored in `yarn-editor` folder.
|
||||
See `yarn-main.js` for the code running the editor.
|
||||
See `ModalWindow.js` and `LocalYarnBridge.js` files for the bridge that open the Window and pass data from GDevelop to yarn.
|
12
newIDE/app/public/external/yarn/yarn-index.html
vendored
Normal file
12
newIDE/app/public/external/yarn/yarn-index.html
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>GDevelop Dialogue Tree Editor (Yarn)</title>
|
||||
<link rel="stylesheet" type="text/css" href="yarn-style.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="path-editor-header"></div>
|
||||
<iframe id="yarn-frame"></iframe>
|
||||
<script type="module" src="gdide://external/yarn/yarn-main.js"></script>
|
||||
</body>
|
||||
</html>
|
75
newIDE/app/public/external/yarn/yarn-main.js
vendored
Normal file
75
newIDE/app/public/external/yarn/yarn-main.js
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
import { createPathEditorHeader, fileExists } from '../utils/path-editor.js';
|
||||
|
||||
const electron = require('electron');
|
||||
const electronWindow = electron.remote.getCurrentWindow();
|
||||
const ipcRenderer = electron.ipcRenderer;
|
||||
const fs = require('fs');
|
||||
const remote = electron.remote;
|
||||
|
||||
let yarn = null;
|
||||
|
||||
const saveAndClose = pathEditorHeader => {
|
||||
const savePath = pathEditorHeader.state.fullPath;
|
||||
yarn.data.saveTo(savePath, yarn.data.getSaveData('json'), () => {
|
||||
ipcRenderer.send('yarn-changes-saved', savePath);
|
||||
remote.getCurrentWindow().close();
|
||||
});
|
||||
};
|
||||
|
||||
const closeWindow = () => {
|
||||
remote.getCurrentWindow().close();
|
||||
};
|
||||
|
||||
const editorFrameEl = document.getElementById('yarn-frame');
|
||||
window.addEventListener('yarnReady', e => {
|
||||
yarn = e;
|
||||
yarn.app.fs = fs;
|
||||
yarn.app.electron = electron;
|
||||
ipcRenderer.send('yarn-ready');
|
||||
});
|
||||
editorFrameEl.src = 'yarn-editor/index.html';
|
||||
|
||||
// Called to load yarn data. Should be called after the window is fully loaded.
|
||||
ipcRenderer.on('yarn-open', (event, receivedData) => {
|
||||
// Make the header.
|
||||
const pathEditorHeaderDiv = document.getElementById('path-editor-header');
|
||||
const pathEditorHeader = createPathEditorHeader({
|
||||
parentElement: pathEditorHeaderDiv,
|
||||
editorContentDocument: document,
|
||||
onSaveToGd: saveAndClose,
|
||||
onCancelChanges: closeWindow,
|
||||
projectPath: receivedData.projectPath,
|
||||
initialResourcePath: receivedData.resourcePath,
|
||||
extension: '.json',
|
||||
});
|
||||
|
||||
// Inject custom Apply button.
|
||||
const saveToGdButton = yarn.document
|
||||
.getElementsByClassName('menu')[0]
|
||||
.cloneNode(true);
|
||||
saveToGdButton.onclick = () => saveAndClose(pathEditorHeader);
|
||||
yarn.document
|
||||
.getElementsByClassName('app-menu')[0]
|
||||
.appendChild(saveToGdButton);
|
||||
saveToGdButton.childNodes[0].firstChild.data = 'Apply';
|
||||
|
||||
// Process the json file to open, if any.
|
||||
if (fileExists(receivedData.resourcePath)) {
|
||||
receivedData.externalEditorData = fs
|
||||
.readFileSync(receivedData.resourcePath, 'utf8')
|
||||
.toString();
|
||||
|
||||
yarn.data.loadData(receivedData.externalEditorData, 'json', true);
|
||||
electronWindow.setTitle(
|
||||
'GDevelop Dialogue Tree Editor (Yarn) - ' + receivedData.resourcePath
|
||||
);
|
||||
|
||||
pathEditorHeader.toggle();
|
||||
} else {
|
||||
// Set up a new path for the JSON to be saved if none was passed.
|
||||
receivedData.resourcePath = receivedData.projectPath + '/NewFile.json';
|
||||
}
|
||||
|
||||
yarn.data.editingPath(receivedData.resourcePath);
|
||||
yarn.data.editingType('json');
|
||||
});
|
68
newIDE/app/public/external/yarn/yarn-style.css
vendored
Normal file
68
newIDE/app/public/external/yarn/yarn-style.css
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Additional styles for the embedded Yarn editor
|
||||
*/
|
||||
html,
|
||||
body {
|
||||
font-family: Helvetica;
|
||||
margin: 0;
|
||||
overflow-y: hidden;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#yarn-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#path-editor-header .leftSide {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#path-editor-header .rightButtons {
|
||||
display: flex;
|
||||
padding-bottom: 4px;
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
#path-editor-header .rightButtons > button {
|
||||
right: 0;
|
||||
margin-right: 4px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#path-editor-header > button,
|
||||
#path-editor-header > span > button {
|
||||
background-color: white;
|
||||
border: 2px solid lightblue;
|
||||
border-radius: 3px;
|
||||
max-height: 30px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#path-editor-header > span > input {
|
||||
font-family: 'Courier New';
|
||||
height: 27px;
|
||||
width: 90px;
|
||||
float: left;
|
||||
padding: 4px;
|
||||
margin-top: 4px;
|
||||
font-size: 15px;
|
||||
border: 2px solid cyan;
|
||||
border-radius: 3px;
|
||||
background-color: white;
|
||||
color: darkslategrey;
|
||||
}
|
||||
|
||||
#path-editor-header > span > label {
|
||||
height: 27px;
|
||||
color: darkcyan;
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#path-editor-header > span > * {
|
||||
padding-left: 5px;
|
||||
}
|
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-eval' 'unsafe-inline' https://api.gdevelop-app.com https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js https://api.keen.io https://fullstory.com/s/fs.js https://rs.fullstory.com">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-eval' 'unsafe-inline' https://api.gdevelop-app.com https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js https://api.keen.io https://fullstory.com/s/fs.js https://rs.fullstory.com https://apis.google.com">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
<title>GDevelop 5</title>
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
<!-- Root div used for React `App` component rendering-->
|
||||
<div id="root"></div>
|
||||
|
||||
<script>
|
||||
window["GD_STARTUP_TIMES"] = [["indexHtmlFirstScriptStarted", performance.now()]];
|
||||
</script>
|
||||
|
||||
<!-- GDevelop.js core -->
|
||||
<script>
|
||||
//Ensure module is not defined to avoid GDevelop.js to think
|
||||
@@ -40,6 +44,11 @@
|
||||
</script>
|
||||
<script src="%PUBLIC_URL%/libGD.js"></script>
|
||||
|
||||
<script>
|
||||
window["GD_STARTUP_TIMES"] = window["GD_STARTUP_TIMES"] || [];
|
||||
window["GD_STARTUP_TIMES"].push(["libGDLoadedTime", performance.now()]);
|
||||
</script>
|
||||
|
||||
<!-- Monaco Editor support -->
|
||||
<script>
|
||||
// Monaco editor is using amd require. Save the Node.js require if it exists (Electron)
|
||||
@@ -65,5 +74,10 @@
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
window["GD_STARTUP_TIMES"] = window["GD_STARTUP_TIMES"] || [];
|
||||
window["GD_STARTUP_TIMES"].push(["monacoEditorLoaderLoaded", performance.now()]);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
BIN
newIDE/app/public/res/actions/Star24.png
Normal file
BIN
newIDE/app/public/res/actions/Star24.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user