Compare commits

...

33 Commits

Author SHA1 Message Date
Clément Pasteau
e4fc065dc1 Bump newIDE Version 2021-08-12 17:27:30 +02:00
Florian Rival
d1f4d26e49 Fix color of grids in scene editor not properly persisted
* Also make the update real time (any change in the settings are shown directly in the editor)

Don't show the rest in the changelog:

* Add various Flow typings where missing (SetupGridDialog and various InstancesEditor files)
* Serialize the settings into a generic "EditorSetting" (so that the Project is agnostic of any editor related stuff)
* Rename uiSettings/options/LayoutEditorCanvasOptions to InstancesEditorSettings
  * Handle everything inside the IDE (so that the Project remains agnostic of any editor related stuff)

Note that in the future, this kind of EditorSetting that is stored inside the project could be moved to its own structure (living outside of the project file).
2021-08-12 17:05:32 +02:00
ClementPasteau
a1b840a4fa Add Release Ladder action to Platformer Object Behavior (#2892)
Allow a Platformer Object to simulate a "Release Ladder" action
2021-08-12 15:54:19 +02:00
Florian Rival
465629b688 Add a way to set the "extra info" of a parameter without automatic namespace addition for objects/behaviors
Don't show in changelog
2021-08-12 10:28:01 +02:00
ClementPasteau
38758c62c6 Improve Actions and Conditions search allowing typing mistakes (fuzzy) (#2891) 2021-08-11 17:40:20 +02:00
Aurélien Vivet
26f6e95e46 Show previews of image resources properly pixelated when smoothing is deactivated (#2882) 2021-08-09 17:09:58 +02:00
Aurélien Vivet
34abf8290e Bump newIDE version (#2880) 2021-08-07 14:33:54 +02:00
Aurélien Vivet
52677f5512 Show pixel art assets in the asset store as pixelated and larger (#2838) 2021-08-05 20:27:47 +02:00
ClementPasteau
df2b51bb1b Increase the NewIDE tests jest timeout (#2878)
Don't show in changelog
2021-08-05 15:12:52 +02:00
ClementPasteau
6b0ff984f2 Fix Panel Sprite initial opacity not being correctly applied (#2873)
Fix Panel Sprite initial opacity not being correctly applied
2021-08-05 14:25:43 +02:00
D8H
f4512242e4 Add an action to clear the shapes of a Shape Painter object on demand (#2868) 2021-08-04 13:10:29 +02:00
D8H
8beabbadef Add an action to forbid to jump again while in the air to the Platformer behavior. (#2872)
* This is useful if you want to allow to jump again in the air for a bit of time, and then forbid to jump again later (for example, to implement a "Coyote Time" feature to your player character)
2021-08-04 12:29:38 +02:00
Todor Imreorov
9491a8ed45 Fix buttons to save/load/change the file in Piskel sometimes not displayed properly (#2844) 2021-08-04 11:29:35 +02:00
D8H
f945dfd987 Fix an error log when the new instruction dialog opens (#2869)
Don't show in changelog
2021-08-04 09:35:04 +02:00
Florian Rival
ff42591354 Increase the GDJS tests Karma timeout (#2861)
Semaphore CI tests were sometimes timing out, notably the ones involving PixiJS and real resources to load.

Don't show in changelog
2021-08-03 16:20:35 +02:00
D8H
21fb93c66a Improve TypeScript types for the Shape Painter object (#2860)
Only show in developer changelog
2021-08-03 16:17:11 +02:00
Arthur Pacaud
068fbe653a Add new lines between functions back in TS files (#2859)
Only show in developer changelog
2021-08-03 14:35:07 +02:00
Florian Rival
80891dcc59 Fix readme link
[skip ci]
Don't show in changelog
2021-08-02 19:57:24 +02:00
ClementPasteau
34155d65f1 Fix color picker not shown for the action setting the color of a light object (#2857) 2021-08-02 19:01:16 +02:00
Florian Rival
18f22470ca Fix wrong icon shown intermittently in the extensions store 2021-08-01 20:22:24 +02:00
Florian Rival
92a8ebc58b Fix extra scrollbar on small screens when selecting an object or an action/condition in the events sheet 2021-08-01 19:11:29 +02:00
Florian Rival
565384e270 Fix unability to scroll when a lot of parameters were shown for an action or a condition 2021-08-01 19:11:29 +02:00
Florian Rival
8335b2edf9 Add continuous delivery: automatic macOS and Windows builds (#2854)
* This automatically builds the signed macOS app, the Windows exe and AppX, the Linux AppImage.
* This is only for master and any "experimental-build/xxx" branches.
* Only Travis CI and Semaphore CI are running tests, for all branches.
2021-07-31 18:12:19 +02:00
Aurélien Vivet
12c77d0455 Add thumbnails in the resources list (#2842) 2021-07-27 15:59:06 +02:00
Florian Rival
13b62a06eb Add option to allow building Android App Bundles that can be uploaded if you already published APKs with Play App Signing enabled for your game
* If you already opted in for "Play App Signing" in the Play Developer Console and published some APKs like this, you need to enable the new "Old upload key" option, in the Signing Option, before *each* packaging for Android.
* Read this [page to learn more](http://wiki.compilgames.net/doku.php/gdevelop5/publishing/android_and_ios/play-store/upgrading-from-apk-to-aab#done_you_can_now_upload_your_aab) and have step by step details of what to do to update your game.
* If you publish a new game with Android App Bundles, there is nothing to do!
2021-07-22 15:09:58 +01:00
Florian Rival
d27119d8ea Remove deprecated examples upload script from web-app deployment
Don't show in changelog
2021-07-21 17:25:40 +01:00
Florian Rival
dd3ff554d5 Upgrade AppVeyor CI to Node.js 14 (#2826)
[ci skip] [skip ci]
2021-07-20 23:36:56 +01:00
Florian Rival
287a17b634 Add a maximum size for the GDevelop logo so that it's not too big on large resolutions 2021-07-20 18:13:42 +01:00
Florian Rival
7dc477e29e Bump newIDE version 2021-07-20 15:53:20 +01:00
Florian Rival
8174bca3b1 Fix progress bar not shown if GDevelop logo was hidden
Don't show in changelog
2021-07-19 21:41:24 +01:00
Florian Rival
7f6388c6f5 Make the loading screen customizable (#2814)
* Allow to choose the style of the GDevelop logo (light, dark, colored or plain)
* Allow to choose the background color and an optional background image
* Allow to choose the size of the progress bar (with a minimum and maximum size)
* Allow to choose the duration of the fade in animations and the minimum time the loading screen is shown
* Use the **new preview button in the game properties** dialog to run a preview with the loading screen shown
2021-07-19 19:52:25 +01:00
Florian Rival
da3a099ff2 Fix increasing size of the project file whenever changes on a layer were cancelled
* This was because of a duplication of the "cameras" stored in the layer.
* Also add a check to clean project files with the problem.

Fix #2812
2021-07-17 11:56:04 +01:00
Florian Rival
7b2c7b4a00 Bump newIDE version 2021-07-15 18:06:49 +01:00
133 changed files with 13886 additions and 1501 deletions

View File

@@ -1,9 +1,75 @@
# CircleCI 2.0 configuration file to build GDevelop app running
# on the Electron runtime (newIDE/electron-app).
# CircleCI configuration to build GDevelop app running
# on the Electron runtime (newIDE/electron-app) for macOS and Linux.
# For Windows, see the appveyor.yml file.
version: 2
version: 2.1
jobs:
build:
build-macos:
macos:
xcode: 12.5.1
steps:
- checkout
# System dependencies (for Emscripten and upload)
- run:
name: Install dependencies for Emscripten and AWS S3 upload
command: brew install cmake && brew install awscli
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
# GDevelop.js dependencies
- restore_cache:
keys:
- gd-macos-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
# fallback to using the latest cache if no exact match is found
- gd-macos-nodejs-dependencies---
- run:
name: Install GDevelop.js dependencies and build it
command: cd GDevelop.js && npm install && cd ..
# Build GDevelop.js (and run tests to ensure it works)
- run:
name: Build GDevelop.js
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
# GDevelop IDE dependencies (after building GDevelop.js to avoid downloading a pre-built version)
- run:
name: Install GDevelop IDE dependencies
command: cd newIDE/app && npm install && cd ../electron-app && npm install
- save_cache:
paths:
- newIDE/electron-app/node_modules
- newIDE/app/node_modules
- GDevelop.js/node_modules
key: gd-macos-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
# Build GDevelop IDE (seems like we need to allow Node.js to use more space than usual)
# Note: Code signing is done using CSC_LINK (see https://www.electron.build/code-signing).
- run:
name: Build GDevelop IDE
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && npm run build -- --mac --publish=never
- run:
name: Clean dist folder to keep only installers/binaries.
command: rm -rf "newIDE/electron-app/dist/mac/GDevelop 5.app"
# Upload artifacts (CircleCI)
- store_artifacts:
path: newIDE/electron-app/dist
# Upload artifacts (AWS)
- run:
name: Deploy to S3 (specific commit)
command: aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/
- run:
name: Deploy to S3 (latest)
command: aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/
build-linux:
# CircleCI docker workers are failing if they don't have enough memory (no swap)
resource_class: xlarge
docker:
@@ -23,10 +89,6 @@ jobs:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
- run:
name: Install Wine for Electron builder
command: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt install wine32
- run:
name: Install system dependencies for Electron builder
command: sudo apt install icnsutils && sudo apt install graphicsmagick && sudo apt install rsync
@@ -34,15 +96,15 @@ jobs:
# GDevelop.js dependencies
- restore_cache:
keys:
- gd-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
- gd-linux-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
# fallback to using the latest cache if no exact match is found
- gd-nodejs-dependencies---
- gd-linux-nodejs-dependencies---
- run:
name: Install GDevelop.js dependencies and build it
command: cd GDevelop.js && sudo npm install -g grunt-cli && npm install && cd ..
command: cd GDevelop.js && npm install && cd ..
# Build GDevelop.js
# Build GDevelop.js (and run tests to ensure it works)
- run:
name: Build GDevelop.js
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
@@ -57,16 +119,16 @@ jobs:
- newIDE/electron-app/node_modules
- newIDE/app/node_modules
- GDevelop.js/node_modules
key: gd-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}
key: gd-linux-nodejs-dependencies-{{ checksum "newIDE/app/package.json" }}-{{ checksum "newIDE/electron-app/package.json" }}-{{ checksum "GDevelop.js/package.json" }}
# Build GDevelop IDE (seems like we need to allow Node.js to use more space than usual)
- run:
name: Build GDevelop IDE
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && npm run build -- --mac zip --win --linux tar.gz --publish=never
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && npm run build -- --linux AppImage --publish=never
- run:
name: Clean dist folder to keep only installers/binaries.
command: rm -rf newIDE/electron-app/dist/linux-unpacked && rm -rf newIDE/electron-app/dist/win-unpacked && rm -rf newIDE/electron-app/dist/mac
command: rm -rf newIDE/electron-app/dist/linux-unpacked
# Upload artifacts (CircleCI)
- store_artifacts:
@@ -79,3 +141,20 @@ jobs:
- run:
name: Deploy to S3 (latest)
command: aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/
workflows:
builds:
jobs:
- build-macos:
filters:
branches:
only:
- master
- /experimental-build.*/
- build-linux:
filters:
branches:
only:
- master
- /experimental-build.*/

View File

@@ -1,3 +1,12 @@
# Travis CI configuration to build and run all tests
# (and typing/formatting) for the Core, newIDE, GDJS (and even GDCpp).
#
# This builds GDevelop.js and store it on a S3 so it can be used to run
# GDevelop without building it.
#
# See also Semaphore CI for quick tests (not building GDevelop.js, so
# faster but not always reliable).
language: cpp
sudo: false
compiler:

View File

@@ -275,6 +275,76 @@ BehaviorMetadata::AddExpressionAndConditionAndAction(
expression, condition, action);
}
#if defined(GD_IDE_ONLY)
gd::InstructionMetadata& BehaviorMetadata::AddDuplicatedAction(
const gd::String& newActionName, const gd::String& copiedActionName) {
gd::String newNameWithNamespace = extensionNamespace + newActionName;
gd::String copiedNameWithNamespace = extensionNamespace + copiedActionName;
auto copiedAction = actionsInfos.find(copiedNameWithNamespace);
if (copiedAction == actionsInfos.end()) {
gd::LogWarning("Could not find an action with name " +
copiedNameWithNamespace + " to copy.");
} else {
actionsInfos[newNameWithNamespace] = copiedAction->second;
}
return actionsInfos[newNameWithNamespace];
}
gd::InstructionMetadata& BehaviorMetadata::AddDuplicatedCondition(
const gd::String& newConditionName, const gd::String& copiedConditionName) {
gd::String newNameWithNamespace = extensionNamespace + newConditionName;
gd::String copiedNameWithNamespace = extensionNamespace + copiedConditionName;
auto copiedCondition = conditionsInfos.find(copiedNameWithNamespace);
if (copiedCondition == conditionsInfos.end()) {
gd::LogWarning("Could not find a condition with name " +
copiedNameWithNamespace + " to copy.");
} else {
conditionsInfos[newNameWithNamespace] = copiedCondition->second;
}
return conditionsInfos[newNameWithNamespace];
}
gd::ExpressionMetadata& BehaviorMetadata::AddDuplicatedExpression(
const gd::String& newExpressionName,
const gd::String& copiedExpressionName) {
gd::String newNameWithNamespace = extensionNamespace + newExpressionName;
gd::String copiedNameWithNamespace =
extensionNamespace + copiedExpressionName;
auto copiedExpression = expressionsInfos.find(copiedNameWithNamespace);
if (copiedExpression == expressionsInfos.end()) {
gd::LogWarning("Could not find an expression with name " +
copiedNameWithNamespace + " to copy.");
} else {
expressionsInfos[newNameWithNamespace] = copiedExpression->second;
}
return expressionsInfos[newNameWithNamespace];
}
gd::ExpressionMetadata& BehaviorMetadata::AddDuplicatedStrExpression(
const gd::String& newExpressionName,
const gd::String& copiedExpressionName) {
gd::String newNameWithNamespace = extensionNamespace + newExpressionName;
gd::String copiedNameWithNamespace =
extensionNamespace + copiedExpressionName;
auto copiedExpression = strExpressionsInfos.find(copiedNameWithNamespace);
if (copiedExpression == strExpressionsInfos.end()) {
gd::LogWarning("Could not find a string expression with name " +
copiedNameWithNamespace + " to copy.");
} else {
strExpressionsInfos[newNameWithNamespace] = copiedExpression->second;
}
return strExpressionsInfos[newNameWithNamespace];
}
#endif
BehaviorMetadata& BehaviorMetadata::SetFullName(const gd::String& fullname_) {
#if defined(GD_IDE_ONLY)
fullname = fullname_;

View File

@@ -138,6 +138,46 @@ class GD_CORE_API BehaviorMetadata {
const gd::String& group,
const gd::String& icon);
/**
* \brief Create a new action which is the duplicate of the specified one.
*
* Useful for handling a deprecated action that is just a "copy" of the new
* one.
*/
gd::InstructionMetadata& AddDuplicatedAction(
const gd::String& newActionName, const gd::String& copiedActionName);
/**
* \brief Create a new condition which is the duplicate of the specified one.
*
* Useful for handling a deprecated condition that is just a "copy" of the new
* one.
*/
gd::InstructionMetadata& AddDuplicatedCondition(
const gd::String& newConditionName,
const gd::String& copiedConditionName);
/**
* \brief Create a new expression which is the duplicate of the specified one.
*
* Useful for handling a deprecated expression that is just a "copy" of the
* new one.
*/
gd::ExpressionMetadata& AddDuplicatedExpression(
const gd::String& newExpressionName,
const gd::String& copiedExpressionName);
/**
* \brief Create a new string expression which is the duplicate of the
* specified one.
*
* Useful for handling a deprecated string expression that is just a "copy" of
* the new one.
*/
gd::ExpressionMetadata& AddDuplicatedStrExpression(
const gd::String& newExpressionName,
const gd::String& copiedExpressionName);
BehaviorMetadata& SetFullName(const gd::String& fullname_);
BehaviorMetadata& SetDefaultName(const gd::String& defaultName_);
BehaviorMetadata& SetDescription(const gd::String& description_);

View File

@@ -187,6 +187,19 @@ class GD_CORE_API InstructionMetadata {
return *this;
};
/**
* \brief Set the additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter), for the last added parameter.
*
* \see AddParameter
*/
InstructionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) {
if (!parameters.empty())
parameters.back().SetExtraInfo(extraInfo);
return *this;
};
/**
* \brief Add the default parameters for an instruction manipulating the
* specified type ("string", "number") with the default operators.

View File

@@ -68,14 +68,14 @@ class GD_CORE_API ParameterMetadata {
/**
* \brief Return an optional additional information, used for some parameters
* with special type (For example, it can contains the type of object accepted
* with special type (for example, it can contains the type of object accepted
* by the parameter).
*/
const gd::String &GetExtraInfo() const { return supplementaryInformation; }
/**
* \brief Set an optional additional information, used for some parameters
* with special type (For example, it can contains the type of object accepted
* with special type (for example, it can contains the type of object accepted
* by the parameter).
*/
ParameterMetadata &SetExtraInfo(const gd::String &supplementaryInformation_) {

View File

@@ -0,0 +1,25 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include "GDCore/CommonTools.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "EditorSettings.h"
namespace gd {
EditorSettings::EditorSettings() {}
void EditorSettings::SerializeTo(SerializerElement& element) const {
element = content;
}
void EditorSettings::UnserializeFrom(
const SerializerElement& element) {
content = element;
}
} // namespace gd
#endif

View File

@@ -0,0 +1,46 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef SCENECANVASSETTINGS_H
#define SCENECANVASSETTINGS_H
#include "GDCore/String.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
/**
* \brief Container for arbitrary serialized data to be used by the editor
* to store settings.
*
* \see Scene
*/
class GD_CORE_API EditorSettings {
public:
EditorSettings();
virtual ~EditorSettings(){};
/** \name Serialization
*/
///@{
/**
* \brief Serialize the settings.
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the settings.
*/
void UnserializeFrom(const SerializerElement& element);
///@}
private:
gd::SerializerElement content; ///< Arbitrary content, depending on the editor.
};
} // namespace gd
#endif // SCENECANVASSETTINGS_H
#endif

View File

@@ -1,59 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include "LayoutEditorCanvasOptions.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
LayoutEditorCanvasOptions::LayoutEditorCanvasOptions()
: grid(false),
snap(true),
gridWidth(32),
gridHeight(32),
gridOffsetX(0),
gridOffsetY(0),
gridType("rectangular"),
gridR(158),
gridG(180),
gridB(255),
zoomFactor(1),
windowMask(false) {}
void LayoutEditorCanvasOptions::SerializeTo(SerializerElement& element) const {
element.SetAttribute("grid", grid);
element.SetAttribute("snap", snap);
element.SetAttribute("gridWidth", gridWidth);
element.SetAttribute("gridHeight", gridHeight);
element.SetAttribute("gridOffsetX", gridOffsetX);
element.SetAttribute("gridOffsetY", gridOffsetY);
element.SetAttribute("gridType", gridType);
element.SetAttribute("gridR", gridR);
element.SetAttribute("gridG", gridG);
element.SetAttribute("gridB", gridB);
element.SetAttribute("zoomFactor", zoomFactor);
element.SetAttribute("windowMask", windowMask);
}
void LayoutEditorCanvasOptions::UnserializeFrom(
const SerializerElement& element) {
grid = element.GetBoolAttribute("grid");
snap = element.GetBoolAttribute("snap");
windowMask = element.GetBoolAttribute("windowMask");
gridWidth = element.GetDoubleAttribute("gridWidth", 32);
gridHeight = element.GetDoubleAttribute("gridHeight", 32);
gridOffsetX = element.GetDoubleAttribute("gridOffsetX", 0);
gridOffsetY = element.GetDoubleAttribute("gridOffsetY", 0);
gridType = element.GetStringAttribute("gridType", "rectangular");
gridR = element.GetIntAttribute("gridR", 158);
gridG = element.GetIntAttribute("gridG", 180);
gridB = element.GetIntAttribute("gridB", 255);
zoomFactor = element.GetDoubleAttribute("zoomFactor", 1.0);
}
} // namespace gd
#endif

View File

@@ -1,58 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef SCENECANVASSETTINGS_H
#define SCENECANVASSETTINGS_H
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
namespace gd {
/**
* \brief Tool class used to store settings of a LayoutEditorCanvas.
*
* \see Scene
*/
class GD_CORE_API LayoutEditorCanvasOptions {
public:
LayoutEditorCanvasOptions();
virtual ~LayoutEditorCanvasOptions(){};
/** \name Serialization
*/
///@{
/**
* \brief Serialize instances container.
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the instances container.
*/
void UnserializeFrom(const SerializerElement& element);
///@}
bool grid; ///< True if grid activated in editor
bool snap; ///< True if snap to grid activated in editor
double gridWidth; ///< Grid width in editor
double gridHeight; ///< Grid height in editor
double gridOffsetX; ///< Grid X offset
double gridOffsetY; ///< Grid Y offset
gd::String gridType; ///< Grid type: rectangular or isometric
int gridR; ///< Grid red color in editor
int gridG; ///< Grid green color in editor
int gridB; ///< Grid blue color in editor
double zoomFactor; ///< Stores the zoom factor
bool windowMask; ///< True if window mask displayed in editor
};
} // namespace gd
#endif // SCENECANVASSETTINGS_H
#endif

View File

@@ -5,7 +5,7 @@
*/
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/LayoutEditorCanvasOptions.h"
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h"
#include "GDCore/Project/InitialInstancesContainer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/TinyXml/tinyxml.h"
@@ -16,7 +16,7 @@ void ExternalLayout::UnserializeFrom(const SerializerElement& element) {
name = element.GetStringAttribute("name", "", "Name");
instances.UnserializeFrom(element.GetChild("instances", 0, "Instances"));
#if defined(GD_IDE_ONLY)
editionSettings.UnserializeFrom(element.GetChild("editionSettings"));
editorSettings.UnserializeFrom(element.GetChild("editionSettings"));
#endif
associatedLayout = element.GetStringAttribute("associatedLayout");
}
@@ -25,7 +25,7 @@ void ExternalLayout::UnserializeFrom(const SerializerElement& element) {
void ExternalLayout::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
instances.SerializeTo(element.AddChild("instances"));
editionSettings.SerializeTo(element.AddChild("editionSettings"));
editorSettings.SerializeTo(element.AddChild("editionSettings"));
element.SetAttribute("associatedLayout", associatedLayout);
}
#endif

View File

@@ -13,7 +13,7 @@ namespace gd {
class SerializerElement;
}
#if defined(GD_IDE_ONLY)
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/LayoutEditorCanvasOptions.h"
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h"
#endif
namespace gd {
@@ -58,15 +58,15 @@ class GD_CORE_API ExternalLayout {
/**
* \brief Get the user settings for the IDE.
*/
const gd::LayoutEditorCanvasOptions& GetAssociatedSettings() const {
return editionSettings;
const gd::EditorSettings& GetAssociatedEditorSettings() const {
return editorSettings;
}
/**
* \brief Get the user settings for the IDE.
*/
gd::LayoutEditorCanvasOptions& GetAssociatedSettings() {
return editionSettings;
gd::EditorSettings& GetAssociatedEditorSettings() {
return editorSettings;
}
#endif
@@ -100,7 +100,7 @@ class GD_CORE_API ExternalLayout {
gd::String name;
gd::InitialInstancesContainer instances;
#if defined(GD_IDE_ONLY)
gd::LayoutEditorCanvasOptions editionSettings;
gd::EditorSettings editorSettings;
#endif
gd::String associatedLayout;
};

View File

@@ -70,54 +70,35 @@ void Layer::UnserializeFrom(const SerializerElement& element) {
element.GetIntAttribute("ambientLightColorG", 200),
element.GetIntAttribute("ambientLightColorB", 200));
// Compatibility with GD <= 3.3
if (element.HasChild("Camera")) {
for (std::size_t i = 0; i < element.GetChildrenCount("Camera"); ++i) {
const SerializerElement& cameraElement = element.GetChild("Camera", i);
SetCameraCount(GetCameraCount() + 1);
Camera& camera = GetCamera(GetCameraCount() - 1);
cameras.clear();
SerializerElement& camerasElement = element.GetChild("cameras");
camerasElement.ConsiderAsArrayOf("camera");
for (std::size_t i = 0; i < camerasElement.GetChildrenCount(); ++i) {
const SerializerElement& cameraElement = camerasElement.GetChild(i);
camera.SetUseDefaultSize(
cameraElement.GetBoolAttribute("DefaultSize", true));
camera.SetSize(cameraElement.GetDoubleAttribute("Width"),
cameraElement.GetDoubleAttribute("Height"));
Camera camera;
camera.SetUseDefaultSize(
cameraElement.GetBoolAttribute("defaultSize", true));
camera.SetSize(cameraElement.GetDoubleAttribute("width"),
cameraElement.GetDoubleAttribute("height"));
camera.SetUseDefaultViewport(
cameraElement.GetBoolAttribute("defaultViewport", true));
camera.SetViewport(
cameraElement.GetDoubleAttribute("viewportLeft"),
cameraElement.GetDoubleAttribute("viewportTop"),
cameraElement.GetDoubleAttribute("viewportRight"),
cameraElement.GetDoubleAttribute(
"viewportBottom"));
camera.SetUseDefaultViewport(
cameraElement.GetBoolAttribute("DefaultViewport", true));
camera.SetViewport(
cameraElement.GetDoubleAttribute("ViewportLeft"),
cameraElement.GetDoubleAttribute("ViewportTop"),
cameraElement.GetDoubleAttribute("ViewportRight"),
cameraElement.GetDoubleAttribute(
"ViewportBottom")); // (sf::Rect used Right and Bottom instead of
// Width and Height before)
}
cameras.push_back(camera);
}
// End of compatibility code
else {
SerializerElement& camerasElement = element.GetChild("cameras");
camerasElement.ConsiderAsArrayOf("camera");
for (std::size_t i = 0; i < camerasElement.GetChildrenCount(); ++i) {
const SerializerElement& cameraElement = camerasElement.GetChild(i);
SetCameraCount(GetCameraCount() + 1);
Camera& camera = GetCamera(GetCameraCount() - 1);
camera.SetUseDefaultSize(
cameraElement.GetBoolAttribute("defaultSize", true));
camera.SetSize(cameraElement.GetDoubleAttribute("width"),
cameraElement.GetDoubleAttribute("height"));
camera.SetUseDefaultViewport(
cameraElement.GetBoolAttribute("defaultViewport", true));
camera.SetViewport(
cameraElement.GetDoubleAttribute("viewportLeft"),
cameraElement.GetDoubleAttribute("viewportTop"),
cameraElement.GetDoubleAttribute("viewportRight"),
cameraElement.GetDoubleAttribute(
"viewportBottom")); // (sf::Rect used Right and Bottom instead of
// Width and Height before)
}
if (camerasElement.GetChildrenCount() > 50) {
// Highly unlikely that we want as many cameras, as they were not even exposed in
// the editor nor used in the game engine. Must be because of a bug in the editor that
// duplicated cameras when cancelling changes on a layer.
// Reset to one camera.
SetCameraCount(1);
}
const SerializerElement& effectsElement = element.GetChild("effects");

View File

@@ -272,7 +272,7 @@ void Layout::SerializeTo(SerializerElement& element) const {
disableInputWhenNotFocused);
#if defined(GD_IDE_ONLY)
GetAssociatedSettings().SerializeTo(element.AddChild("uiSettings"));
editorSettings.SerializeTo(element.AddChild("uiSettings"));
#endif
GetObjectGroups().SerializeTo(element.AddChild("objectsGroups"));
@@ -333,7 +333,7 @@ void Layout::UnserializeFrom(gd::Project& project,
element.GetBoolAttribute("disableInputWhenNotFocused");
#if defined(GD_IDE_ONLY)
associatedSettings.UnserializeFrom(
editorSettings.UnserializeFrom(
element.GetChild("uiSettings", 0, "UISettings"));
GetObjectGroups().UnserializeFrom(
@@ -412,7 +412,7 @@ void Layout::Init(const Layout& other) {
#if defined(GD_IDE_ONLY)
events = other.events;
associatedSettings = other.associatedSettings;
editorSettings = other.editorSettings;
objectGroups = other.objectGroups;
profiler = other.profiler;

View File

@@ -18,7 +18,7 @@
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#if defined(GD_IDE_ONLY)
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/LayoutEditorCanvasOptions.h"
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h"
#endif
namespace gd {
class BaseEvent;
@@ -291,18 +291,18 @@ class GD_CORE_API Layout : public ObjectsContainer {
#if defined(GD_IDE_ONLY)
/**
* Return the settings associated to the layout.
* \see gd::LayoutEditorCanvasOptions
* \see gd::EditorSettings
*/
const gd::LayoutEditorCanvasOptions& GetAssociatedSettings() const {
return associatedSettings;
const gd::EditorSettings& GetAssociatedEditorSettings() const {
return editorSettings;
}
/**
* Return the settings associated to the layout.
* \see gd::LayoutEditorCanvasOptions
* \see gd::EditorSettings
*/
gd::LayoutEditorCanvasOptions& GetAssociatedSettings() {
return associatedSettings;
gd::EditorSettings& GetAssociatedEditorSettings() {
return editorSettings;
}
#endif
@@ -436,7 +436,7 @@ class GD_CORE_API Layout : public ObjectsContainer {
///< specified behavior shared data.
#if defined(GD_IDE_ONLY)
EventsList events; ///< Scene events
gd::LayoutEditorCanvasOptions associatedSettings;
gd::EditorSettings editorSettings;
#endif
// TODO: GD C++ Platform specific code below

View File

@@ -5,15 +5,63 @@
*/
#include "LoadingScreen.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
LoadingScreen::LoadingScreen()
: showGDevelopSplash(true),
gdevelopLogoStyle("light"),
backgroundImageResourceName(""),
backgroundColor(0),
backgroundFadeInDuration(0.2),
minDuration(1.5),
logoAndProgressFadeInDuration(0.2),
logoAndProgressLogoFadeInDelay(0.2),
showProgressBar(true),
progressBarMinWidth(40),
progressBarMaxWidth(200),
progressBarWidthPercent(30),
progressBarHeight(20),
progressBarColor(0xFFFFFF){};
void LoadingScreen::SerializeTo(SerializerElement& element) const {
element.SetAttribute("showGDevelopSplash", showGDevelopSplash);
element.SetAttribute("gdevelopLogoStyle",
gdevelopLogoStyle);
element.SetAttribute("backgroundImageResourceName",
backgroundImageResourceName);
element.SetAttribute("backgroundColor", backgroundColor);
element.SetAttribute("backgroundFadeInDuration", backgroundFadeInDuration);
element.SetAttribute("minDuration", minDuration);
element.SetAttribute("logoAndProgressFadeInDuration", logoAndProgressFadeInDuration);
element.SetAttribute("logoAndProgressLogoFadeInDelay", logoAndProgressLogoFadeInDelay);
element.SetAttribute("showProgressBar", showProgressBar);
element.SetAttribute("progressBarMinWidth", progressBarMinWidth);
element.SetAttribute("progressBarMaxWidth", progressBarMaxWidth);
element.SetAttribute("progressBarWidthPercent", progressBarWidthPercent);
element.SetAttribute("progressBarHeight", progressBarHeight);
element.SetAttribute("progressBarColor", progressBarColor);
}
void LoadingScreen::UnserializeFrom(const SerializerElement& element) {
showGDevelopSplash = element.GetBoolAttribute("showGDevelopSplash", true);
gdevelopLogoStyle =
element.GetStringAttribute("gdevelopLogoStyle", "light");
backgroundImageResourceName =
element.GetStringAttribute("backgroundImageResourceName");
backgroundColor = element.GetIntAttribute("backgroundColor", 0);
backgroundFadeInDuration =
element.GetDoubleAttribute("backgroundFadeInDuration", 0.2);
minDuration = element.GetDoubleAttribute("minDuration", 1.5);
logoAndProgressFadeInDuration = element.GetDoubleAttribute("logoAndProgressFadeInDuration", 0.2);
logoAndProgressLogoFadeInDelay = element.GetDoubleAttribute("logoAndProgressLogoFadeInDelay", 0.2);
showProgressBar = element.GetBoolAttribute("showProgressBar", true);
progressBarMinWidth = element.GetDoubleAttribute("progressBarMinWidth", 40);
progressBarMaxWidth = element.GetDoubleAttribute("progressBarMaxWidth", 200);
progressBarWidthPercent = element.GetDoubleAttribute("progressBarWidthPercent", 30);
progressBarHeight = element.GetDoubleAttribute("progressBarHeight", 20);
progressBarColor = element.GetIntAttribute("progressBarColor", 0xFFFFFF);
}
} // namespace gd

View File

@@ -22,36 +22,151 @@ namespace gd {
*/
class GD_CORE_API LoadingScreen {
public:
LoadingScreen(){};
LoadingScreen();
virtual ~LoadingScreen(){};
/**
* \brief Set if the GDevelop splash should be shown while loading assets.
*/
void ShowGDevelopSplash(bool show) { showGDevelopSplash = show; };
/**
* \brief Return true if the GDevelop splash should be shown while loading
* \brief Return true if the GDevelop logo should be shown while loading
* assets.
*/
bool IsGDevelopSplashShown() const { return showGDevelopSplash; };
/**
* \brief Set if the GDevelop logo should be shown while loading assets.
*/
LoadingScreen& ShowGDevelopSplash(bool show) {
showGDevelopSplash = show;
return *this;
};
const gd::String& GetGDevelopLogoStyle() const { return gdevelopLogoStyle; };
LoadingScreen& SetGDevelopLogoStyle(const gd::String& value) {
gdevelopLogoStyle = value;
return *this;
}
const gd::String& GetBackgroundImageResourceName() const {
return backgroundImageResourceName;
};
LoadingScreen& SetBackgroundImageResourceName(const gd::String& value) {
backgroundImageResourceName = value;
return *this;
}
int GetBackgroundColor() const { return backgroundColor; };
LoadingScreen& SetBackgroundColor(int value) {
backgroundColor = value;
return *this;
}
double GetBackgroundFadeInDuration() const {
return backgroundFadeInDuration;
};
LoadingScreen& SetBackgroundFadeInDuration(double value) {
backgroundFadeInDuration = value;
return *this;
}
double GetMinDuration() const { return minDuration; };
LoadingScreen& SetMinDuration(double value) {
minDuration = value;
return *this;
}
double GetLogoAndProgressFadeInDuration() const {
return logoAndProgressFadeInDuration;
}
LoadingScreen& SetLogoAndProgressFadeInDuration(double value) {
logoAndProgressFadeInDuration = value;
return *this;
}
double GetLogoAndProgressLogoFadeInDelay() const {
return logoAndProgressLogoFadeInDelay;
}
LoadingScreen& SetLogoAndProgressLogoFadeInDelay(double value) {
logoAndProgressLogoFadeInDelay = value;
return *this;
}
bool GetShowProgressBar() const { return showProgressBar; }
LoadingScreen& SetShowProgressBar(bool value) {
showProgressBar = value;
return *this;
}
double GetProgressBarMinWidth() const { return progressBarMinWidth; }
LoadingScreen& SetProgressBarMinWidth(double value) {
progressBarMinWidth = value;
return *this;
}
double GetProgressBarMaxWidth() const { return progressBarMaxWidth; }
LoadingScreen& SetProgressBarMaxWidth(double value) {
progressBarMaxWidth = value;
return *this;
}
double GetProgressBarWidthPercent() const { return progressBarWidthPercent; }
LoadingScreen& SetProgressBarWidthPercent(double value) {
progressBarWidthPercent = value;
return *this;
}
double GetProgressBarHeight() const { return progressBarHeight; }
LoadingScreen& SetProgressBarHeight(double value) {
progressBarHeight = value;
return *this;
}
int GetProgressBarColor() const { return progressBarColor; }
LoadingScreen& SetProgressBarColor(int value) {
progressBarColor = value;
return *this;
}
/** \name Saving and loading
*/
///@{
/**
* \brief Serialize objects groups container.
* \brief Serialize the loading screen setup.
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the objects groups container.
* \brief Unserialize the loading screen setup.
*/
void UnserializeFrom(const SerializerElement& element);
///@}
private:
bool showGDevelopSplash;
gd::String gdevelopLogoStyle;
gd::String backgroundImageResourceName;
int backgroundColor;
double backgroundFadeInDuration; // In seconds.
double minDuration; // In seconds.
double logoAndProgressFadeInDuration; // In seconds.
double logoAndProgressLogoFadeInDelay; // In seconds.
bool showProgressBar;
double progressBarMinWidth; // In pixels.
double progressBarMaxWidth; // In pixels.
double progressBarWidthPercent;
double progressBarHeight; // In pixels.
int progressBarColor;
};
} // namespace gd

View File

@@ -24,12 +24,14 @@ namespace gdjs {
}
}
};
export const isFocused = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isFocused();
}
return false;
};
export const show = function (activate: boolean) {
if (electronBrowserWindow) {
if (activate) {
@@ -39,12 +41,14 @@ namespace gdjs {
}
}
};
export const isVisible = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isVisible();
}
return false;
};
export const maximize = function (activate: boolean) {
if (electronBrowserWindow) {
if (activate) {
@@ -54,12 +58,14 @@ namespace gdjs {
}
}
};
export const isMaximized = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isMaximized();
}
return false;
};
export const minimize = function (activate: boolean) {
if (electronBrowserWindow) {
if (activate) {
@@ -69,89 +75,105 @@ namespace gdjs {
}
}
};
export const isMinimized = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isMinimized();
}
return false;
};
export const enable = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setEnabled(activate);
}
};
export const isEnabled = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isEnabled();
}
return false;
};
export const setResizable = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setResizable(activate);
}
};
export const isResizable = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isResizable();
}
return false;
};
export const setMovable = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setMovable(activate);
}
};
export const isMovable = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isMovable();
}
return false;
};
export const setMaximizable = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setMaximizable(activate);
}
};
export const isMaximizable = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isMaximizable();
}
return false;
};
export const setMinimizable = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setMinimizable(activate);
}
};
export const isMinimizable = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isMinimizable();
}
return false;
};
export const setFullScreenable = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setFullScreenable(activate);
}
};
export const isFullScreenable = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isFullScreenable();
}
return false;
};
export const setClosable = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setClosable(activate);
}
};
export const isClosable = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isClosable();
}
return false;
};
export const setAlwaysOnTop = function (
activate: boolean,
level:
@@ -168,73 +190,86 @@ namespace gdjs {
electronBrowserWindow.setAlwaysOnTop(activate, level);
}
};
export const isAlwaysOnTop = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isAlwaysOnTop();
}
return false;
};
export const setPosition = function (x: float, y: float) {
if (electronBrowserWindow) {
// Convert x and y to (32 bit) integers to avoid Electron errors.
electronBrowserWindow.setPosition(~~x, ~~y);
}
};
export const getPositionX = function (): number {
if (electronBrowserWindow) {
return electronBrowserWindow.getPosition()[0];
}
return 0;
};
export const getPositionY = function (): number {
if (electronBrowserWindow) {
return electronBrowserWindow.getPosition()[1];
}
return 0;
};
export const setKiosk = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setKiosk(activate);
}
};
export const isKiosk = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.isKiosk();
}
return false;
};
export const flash = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.flashFrame(activate);
}
};
export const setHasShadow = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setHasShadow(activate);
}
};
export const hasShadow = function (): boolean {
if (electronBrowserWindow) {
return electronBrowserWindow.hasShadow();
}
return false;
};
export const setOpacity = function (opacity: float) {
if (electronBrowserWindow) {
electronBrowserWindow.setOpacity(opacity);
}
};
export const getOpacity = function (): number {
if (electronBrowserWindow) {
return electronBrowserWindow.getOpacity();
}
return 1;
};
export const setContentProtection = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setContentProtection(activate);
}
};
export const setFocusable = function (activate: boolean) {
if (electronBrowserWindow) {
electronBrowserWindow.setFocusable(activate);

View File

@@ -11,6 +11,7 @@ namespace gdjs {
export const myConditionFunction = function (number, text) {
return number <= 10 && text.length < 5;
};
export const getString = function () {
return 'Hello World';
};

View File

@@ -18,18 +18,21 @@ namespace gdjs {
supportedAPIs.indexOf('getRewardedVideoAsync') !== -1
);
};
export const getPlayerId = function () {
if (typeof FBInstant === 'undefined') {
return '';
}
return FBInstant.player.getID() || '';
};
export const getPlayerName = function () {
if (typeof FBInstant === 'undefined') {
return '';
}
return FBInstant.player.getName() || '';
};
export const loadPlayerData = function (
key,
successVariable,
@@ -49,6 +52,7 @@ namespace gdjs {
errorVariable.setString(error.message || 'Unknown error');
});
};
export const setPlayerData = function (
key,
variable,
@@ -71,6 +75,7 @@ namespace gdjs {
errorVariable.setString(error.message || 'Unknown error');
});
};
export const setPlayerScore = function (
leaderboardName,
score,
@@ -95,6 +100,7 @@ namespace gdjs {
errorVariable.setString(error.message || 'Unknown error');
});
};
export const getPlayerEntry = function (
leaderboardName,
rankVariable,
@@ -124,6 +130,7 @@ namespace gdjs {
errorVariable.setString(error.message || 'Unknown error');
});
};
export const loadInterstitialAd = function (
adPlacementId,
errorVariable
@@ -155,6 +162,7 @@ namespace gdjs {
errorVariable.setString(err.message || 'Unknown error');
});
};
export const showInterstitialAd = function (errorVariable) {
if (typeof FBInstant === 'undefined') {
return;
@@ -175,9 +183,11 @@ namespace gdjs {
gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded = false;
});
};
export const isInterstitialAdReady = function () {
return gdjs.evtTools.facebookInstantGames._preloadedInterstitialLoaded;
};
export const loadRewardedVideo = function (adPlacementId, errorVariable) {
if (typeof FBInstant === 'undefined') {
return;
@@ -206,6 +216,7 @@ namespace gdjs {
errorVariable.setString(err.message || 'Unknown error');
});
};
export const showRewardedVideo = function (errorVariable) {
if (typeof FBInstant === 'undefined') {
return;
@@ -226,6 +237,7 @@ namespace gdjs {
gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded = false;
});
};
export const isRewardedVideoReady = function () {
return gdjs.evtTools.facebookInstantGames._preloadedRewardedVideoLoaded;
};

View File

@@ -24,6 +24,7 @@ namespace gdjs {
}
return gdjs.fileSystem._fs;
};
export const getDirectoryName = function (fileOrFolderPath: string) {
const path = gdjs.fileSystem._getPath();
if (!path) {
@@ -31,6 +32,7 @@ namespace gdjs {
}
return path.dirname(fileOrFolderPath);
};
export const getFileName = function (filePath: string) {
const path = gdjs.fileSystem._getPath();
if (!path) {
@@ -38,6 +40,7 @@ namespace gdjs {
}
return path.basename(filePath);
};
export const getExtensionName = function (filePath: string) {
const path = gdjs.fileSystem._getPath();
if (!path) {

View File

@@ -18,15 +18,19 @@ namespace gdjs {
export const add = function (runtimeScene, inventoryName, name) {
return InventoryManager.get(runtimeScene, inventoryName).add(name);
};
export const remove = function (runtimeScene, inventoryName, name) {
return InventoryManager.get(runtimeScene, inventoryName).remove(name);
};
export const count = function (runtimeScene, inventoryName, name) {
return InventoryManager.get(runtimeScene, inventoryName).count(name);
};
export const has = function (runtimeScene, inventoryName, name) {
return InventoryManager.get(runtimeScene, inventoryName).has(name);
};
export const setMaximum = function (
runtimeScene,
inventoryName,
@@ -38,6 +42,7 @@ namespace gdjs {
maxCount
);
};
export const setUnlimited = function (
runtimeScene,
inventoryName,
@@ -49,20 +54,24 @@ namespace gdjs {
enable
);
};
export const isFull = function (runtimeScene, inventoryName, name) {
return InventoryManager.get(runtimeScene, inventoryName).isFull(name);
};
export const equip = function (runtimeScene, inventoryName, name, equip) {
return InventoryManager.get(runtimeScene, inventoryName).equip(
name,
equip
);
};
export const isEquipped = function (runtimeScene, inventoryName, name) {
return InventoryManager.get(runtimeScene, inventoryName).isEquipped(
name
);
};
export const serializeToVariable = function (
runtimeScene,
inventoryName: string,
@@ -81,6 +90,7 @@ namespace gdjs {
serializedItem.getChild('equipped').setBoolean(item.equipped);
}
};
export const unserializeFromVariable = function (
runtimeScene,
inventoryName: string,

View File

@@ -221,7 +221,7 @@ module.exports = {
'res/actions/color.png'
)
.addParameter('object', _('Object'), 'LightObject', false)
.addParameter('string', _('Color'), '', false)
.addParameter('color', _('Color'), '', false)
.getCodeExtraInformation()
.setFunctionName('setColor');

View File

@@ -90,6 +90,7 @@ namespace gdjs {
}
LinksManager.getManager(runtimeScene).linkObjects(objA, objB);
};
export const removeLinkBetween = function (
runtimeScene: gdjs.RuntimeScene,
objA: gdjs.RuntimeObject,
@@ -100,6 +101,7 @@ namespace gdjs {
}
LinksManager.getManager(runtimeScene).removeLinkBetween(objA, objB);
};
export const removeAllLinksOf = function (
runtimeScene: gdjs.RuntimeScene,
objA: gdjs.RuntimeObject
@@ -109,12 +111,14 @@ namespace gdjs {
}
LinksManager.getManager(runtimeScene).removeAllLinksOf(objA);
};
export const _objectIsInList = function (
obj: gdjs.RuntimeObject,
linkedObjects: gdjs.RuntimeObject[]
) {
return linkedObjects.indexOf(obj) !== -1;
};
export const pickObjectsLinkedTo = function (
runtimeScene: gdjs.RuntimeScene,
objectsLists: Hashtable<gdjs.RuntimeObject[]>,

View File

@@ -2,10 +2,18 @@ namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
class PanelSpriteRuntimeObjectPixiRenderer {
_object: gdjs.PanelSpriteRuntimeObject;
_spritesContainer: any;
_centerSprite: any;
_borderSprites: any;
_alpha: float;
/**
* The _wrapperContainer must be considered as the main container of this object
* All transformations are applied on this container
* See updateOpacity for more info why.
*/
_wrapperContainer: PIXI.Container;
/**
* The _spritesContainer is used to create the sprites and apply cacheAsBitmap only.
*/
_spritesContainer: PIXI.Container;
_centerSprite: PIXI.Sprite | PIXI.TilingSprite;
_borderSprites: Array<PIXI.Sprite | PIXI.TilingSprite>;
_wasRendered: boolean = false;
_textureWidth = 0;
_textureHeight = 0;
@@ -17,38 +25,37 @@ namespace gdjs {
tiled: boolean
) {
this._object = runtimeObject;
if (this._spritesContainer === undefined) {
const texture = (runtimeScene
.getGame()
.getImageManager() as gdjs.PixiImageManager).getPIXITexture(
textureName
);
const StretchedSprite = !tiled ? PIXI.Sprite : PIXI.TilingSprite;
this._spritesContainer = new PIXI.Container();
const texture = (runtimeScene
.getGame()
.getImageManager() as gdjs.PixiImageManager).getPIXITexture(
textureName
);
const StretchedSprite = !tiled ? PIXI.Sprite : PIXI.TilingSprite;
this._spritesContainer = new PIXI.Container();
this._wrapperContainer = new PIXI.Container();
// @ts-ignore
this._centerSprite = new StretchedSprite(new PIXI.Texture(texture));
this._borderSprites = [
// @ts-ignore
this._centerSprite = new StretchedSprite(new PIXI.Texture(texture));
this._borderSprites = [
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Right
new PIXI.Sprite(texture),
//Top-Right
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Top
new PIXI.Sprite(texture),
//Top-Left
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Left
new PIXI.Sprite(texture),
//Bottom-Left
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Bottom
new PIXI.Sprite(texture),
];
}
new StretchedSprite(new PIXI.Texture(texture)),
//Right
new PIXI.Sprite(texture),
//Top-Right
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Top
new PIXI.Sprite(texture),
//Top-Left
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Left
new PIXI.Sprite(texture),
//Bottom-Left
// @ts-ignore
new StretchedSprite(new PIXI.Texture(texture)),
//Bottom
new PIXI.Sprite(texture),
];
//Bottom-Right
this.setTexture(textureName, runtimeScene);
@@ -57,44 +64,43 @@ namespace gdjs {
for (let i = 0; i < this._borderSprites.length; ++i) {
this._spritesContainer.addChild(this._borderSprites[i]);
}
this._alpha = this._spritesContainer.alpha;
this._wrapperContainer.addChild(this._spritesContainer);
runtimeScene
.getLayer('')
.getRenderer()
.addRendererObject(this._spritesContainer, runtimeObject.getZOrder());
.addRendererObject(this._wrapperContainer, runtimeObject.getZOrder());
}
getRendererObject() {
return this._spritesContainer;
return this._wrapperContainer;
}
ensureUpToDate() {
if (this._spritesContainer.visible && this._wasRendered) {
// Update the alpha of the container to make sure that it's applied.
// If not done, the alpha will be back to full opaque when changing the color
// of the object.
this._spritesContainer.alpha = this._alpha;
// Cache the rendered sprites as a bitmap to speed up rendering when
// lots of panel sprites are on the scene.
// Sadly, because of this, we need a wrapper container to workaround
// a PixiJS issue with alpha (see updateOpacity).
this._spritesContainer.cacheAsBitmap = true;
}
this._wasRendered = true;
}
updateOpacity(): void {
//TODO: Workaround a not working property in PIXI.js:
this._spritesContainer.alpha = this._spritesContainer.visible
? this._object.opacity / 255
: 0;
this._alpha = this._spritesContainer.alpha;
// The alpha is updated on a wrapper around the sprite because a known bug
// in Pixi will create a flicker when cacheAsBitmap is set to true.
// (see https://github.com/pixijs/pixijs/issues/4610)
this._wrapperContainer.alpha = this._object.opacity / 255;
}
updateAngle(): void {
this._spritesContainer.rotation = gdjs.toRad(this._object.angle);
this._wrapperContainer.rotation = gdjs.toRad(this._object.angle);
}
updatePosition(): void {
this._spritesContainer.position.x =
this._wrapperContainer.position.x =
this._object.x + this._object._width / 2;
this._spritesContainer.position.y =
this._wrapperContainer.position.y =
this._object.y + this._object._height / 2;
}
@@ -316,19 +322,19 @@ namespace gdjs {
this._updateSpritesAndTexturesSize();
this._updateLocalPositions();
this.updatePosition();
this._spritesContainer.pivot.x = this._object._width / 2;
this._spritesContainer.pivot.y = this._object._height / 2;
this._wrapperContainer.pivot.x = this._object._width / 2;
this._wrapperContainer.pivot.y = this._object._height / 2;
}
updateWidth(): void {
this._spritesContainer.pivot.x = this._object._width / 2;
this._wrapperContainer.pivot.x = this._object._width / 2;
this._updateSpritesAndTexturesSize();
this._updateLocalPositions();
this.updatePosition();
}
updateHeight(): void {
this._spritesContainer.pivot.y = this._object._height / 2;
this._wrapperContainer.pivot.y = this._object._height / 2;
this._updateSpritesAndTexturesSize();
this._updateLocalPositions();
this.updatePosition();
@@ -339,25 +345,21 @@ namespace gdjs {
if (colors.length < 3) {
return;
}
this._centerSprite.tint =
'0x' +
gdjs.rgbToHex(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
this._centerSprite.tint = gdjs.rgbToHexNumber(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
for (
let borderCounter = 0;
borderCounter < this._borderSprites.length;
borderCounter++
) {
this._borderSprites[borderCounter].tint =
'0x' +
gdjs.rgbToHex(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
this._borderSprites[borderCounter].tint = gdjs.rgbToHexNumber(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
}
this._spritesContainer.cacheAsBitmap = false;
}

View File

@@ -14,6 +14,7 @@ namespace gdjs {
behavior
);
};
export const setTimeScale = function (objectsLists, behavior, timeScale) {
const lists = gdjs.staticArray(gdjs.physics2.setTimeScale);
objectsLists.values(lists);

View File

@@ -5,8 +5,9 @@ Copyright (c) 2014-2016 Florian Rival (Florian.Rival@gmail.com)
This project is released under the MIT License.
*/
#include "GDCpp/Extensions/ExtensionBase.h"
#include "PlatformBehavior.h"
#include "GDCpp/Extensions/ExtensionBase.h"
#include "PlatformRuntimeBehavior.h"
#include "PlatformerObjectBehavior.h"
#include "PlatformerObjectRuntimeBehavior.h"
@@ -381,6 +382,19 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.SetFunctionName("SetCanJump")
.SetIncludeFile("PlatformBehavior/PlatformerObjectRuntimeBehavior.h");
aut.AddScopedAction(
"SetCanNotAirJump",
_("Forbid jumping again in the air"),
_("This revokes the effect of \"Allow jumping again\". The object "
"is made unable to jump while in mid air. This has no effect if "
"the object is not in the air."),
_("Forbid _PARAM0_ to air jump"),
_("Options"),
"CppPlatform/Extensions/platformerobjecticon24.png",
"CppPlatform/Extensions/platformerobjecticon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior");
aut.AddCondition("CanJump",
_("Can jump"),
_("Check if the object can jump."),
@@ -461,6 +475,18 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.SetFunctionName("SimulateLadderKey")
.SetIncludeFile("PlatformBehavior/PlatformerObjectRuntimeBehavior.h");
aut.AddAction(
"SimulateReleaseLadderKey",
_("Simulate release ladder key press"),
_("Simulate a press of the Release Ladder key (used to get off a ladder)."),
_("Simulate pressing Release Ladder key for _PARAM0_"),
_("Controls"),
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.MarkAsAdvanced();
aut.AddAction("SimulateJumpKey",
_("Simulate jump key press"),
_("Simulate a press of the jump key."),
@@ -473,30 +499,35 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.SetFunctionName("SimulateJumpKey")
.SetIncludeFile("PlatformBehavior/PlatformerObjectRuntimeBehavior.h");
aut.AddAction("SimulateReleaseKey",
_("Simulate release key press"),
_("Simulate a press of the release key (used when grabbing a "
aut.AddAction("SimulateReleasePlatformKey",
_("Simulate release platform key press"),
_("Simulate a press of the release platform key (used when grabbing a "
"platform ledge)."),
_("Simulate pressing Release key for _PARAM0_"),
_("Simulate pressing Release Platform key for _PARAM0_"),
_("Controls"),
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.SetFunctionName("SimulateReleaseKey")
.SetFunctionName("SimulateReleasePlatformKey")
.SetIncludeFile("PlatformBehavior/PlatformerObjectRuntimeBehavior.h");
// Support for deprecated names:
aut.AddDuplicatedAction("SimulateReleaseKey", "SimulateReleasePlatformKey").SetHidden();
aut.AddAction("SimulateControl",
_("Simulate control"),
_("Simulate a press of a key.\nValid keys are Left, Right, "
"Jump, Ladder, Up, Down."),
"Jump, Ladder, Release Ladder, Up, Down."),
_("Simulate pressing _PARAM2_ key for _PARAM0_"),
_("Controls"),
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.AddParameter("string", _("Key"))
.AddParameter("stringWithSelector",
_("Key"),
"[\"Left\", \"Right\", \"Jump\", \"Ladder\", \"Release Ladder\", \"Up\", \"Down\"]")
.MarkAsAdvanced()
.SetFunctionName("SimulateControl")
.SetIncludeFile("PlatformBehavior/PlatformerObjectRuntimeBehavior.h");

View File

@@ -119,6 +119,7 @@ class PlatformBehaviorJsExtension : public gd::PlatformExtension {
"getCurrentJumpSpeed");
autExpressions["CurrentJumpSpeed"].SetFunctionName("getCurrentJumpSpeed");
autActions["PlatformBehavior::SetCanJump"].SetFunctionName("setCanJump");
autActions["PlatformBehavior::PlatformerObjectBehavior::SetCanNotAirJump"].SetFunctionName("setCanNotAirJump");
autConditions["PlatformBehavior::CanJump"].SetFunctionName(
"canJump");
autActions["PlatformBehavior::SimulateLeftKey"].SetFunctionName(
@@ -131,10 +132,15 @@ class PlatformBehaviorJsExtension : public gd::PlatformExtension {
"simulateDownKey");
autActions["PlatformBehavior::SimulateLadderKey"].SetFunctionName(
"simulateLadderKey");
autActions["PlatformBehavior::SimulateReleaseLadderKey"].SetFunctionName(
"simulateReleaseLadderKey");
autActions["PlatformBehavior::SimulateJumpKey"].SetFunctionName(
"simulateJumpKey");
// deprecated release platform key.
autActions["PlatformBehavior::SimulateReleaseKey"].SetFunctionName(
"simulateReleaseKey");
"simulateReleasePlatformKey");
autActions["PlatformBehavior::SimulateReleasePlatformKey"].SetFunctionName(
"simulateReleasePlatformKey");
autActions["PlatformBehavior::SimulateControl"].SetFunctionName(
"simulateControl");
autActions["PlatformBehavior::IgnoreDefaultControls"].SetFunctionName(

View File

@@ -40,7 +40,8 @@ namespace gdjs {
_upKey: boolean = false;
_downKey: boolean = false;
_jumpKey: boolean = false;
_releaseKey: boolean = false;
_releasePlatformKey: boolean = false;
_releaseLadderKey: boolean = false;
private _state: State;
_falling: Falling;
@@ -181,8 +182,8 @@ namespace gdjs {
(this._downKey =
!this._ignoreDefaultControls && inputManager.isKeyPressed(DOWNKEY));
this._releaseKey ||
(this._releaseKey =
this._releasePlatformKey ||
(this._releasePlatformKey =
!this._ignoreDefaultControls && inputManager.isKeyPressed(DOWNKEY));
this._requestedDeltaX += this._updateSpeed(timeDelta);
@@ -230,13 +231,16 @@ namespace gdjs {
this._leftKey = false;
this._rightKey = false;
this._ladderKey = false;
this._releaseLadderKey = false;
this._upKey = false;
this._downKey = false;
this._releaseKey = false;
this._releasePlatformKey = false;
this._jumpKey = false;
//5) Track the movement
this._hasReallyMoved = Math.abs(object.getX() - oldX) >= 1;
this._hasReallyMoved =
Math.abs(object.getX() - oldX) >= 1 ||
Math.abs(object.getY() - oldY) >= 1;
this._lastDeltaY = object.getY() - oldY;
}
@@ -546,7 +550,7 @@ namespace gdjs {
}
/**
* Mark the platformer object has not being grabbing any platform.
* Mark the platformer object as not grabbing any platform.
*/
_releaseGrabbedPlatform() {
if (this._state === this._grabbingPlatform) {
@@ -554,6 +558,15 @@ namespace gdjs {
}
}
/**
* Mark the platformer object as falling if on a ladder.
*/
_releaseLadder() {
if (this._state === this._onLadder) {
this._setFalling();
}
}
/**
* Separate the object from all platforms passed in parameter.
* @param candidates The platform to be tested for collision
@@ -784,7 +797,7 @@ namespace gdjs {
/**
* Simulate a control action in the Platformer Object by specifying an input.
* @param input The string expression of the control action [Left,Right,Up,Down,Ladder,Jump,Release].
* @param input The string expression of the control action [Left,Right,Up,Down,Ladder,Jump,Release,Release Ladder].
*/
simulateControl(input: string) {
if (input === 'Left') {
@@ -800,7 +813,9 @@ namespace gdjs {
} else if (input === 'Jump') {
this._jumpKey = true;
} else if (input === 'Release') {
this._releaseKey = true;
this._releasePlatformKey = true;
} else if (input === 'Release Ladder') {
this._releaseLadderKey = true;
}
}
@@ -999,6 +1014,15 @@ namespace gdjs {
this._canJump = true;
}
/**
* Forbid the Platformer Object to air jump.
*/
setCanNotAirJump(): void {
if (this._state === this._jumping || this._state === this._falling) {
this._canJump = false;
}
}
/**
* Set if the Platformer Object can grab platforms.
* @param enable Enable / Disable grabbing of platforms.
@@ -1039,6 +1063,13 @@ namespace gdjs {
this._ladderKey = true;
}
/**
* Simulate the "Release Ladder" control of the Platformer Object.
*/
simulateReleaseLadderKey() {
this._releaseLadderKey = true;
}
/**
* Simulate the "Up" control of the Platformer Object.
*/
@@ -1063,8 +1094,8 @@ namespace gdjs {
/**
* Simulate the "Release" control of the Platformer Object.
*/
simulateReleaseKey() {
this._releaseKey = true;
simulateReleasePlatformKey() {
this._releasePlatformKey = true;
}
/**
@@ -1135,7 +1166,8 @@ namespace gdjs {
*/
isMoving(): boolean {
return (
(this._hasReallyMoved && this._currentSpeed !== 0) ||
(this._hasReallyMoved &&
(this._currentSpeed !== 0 || this._state === this._onLadder)) ||
this._jumping.getCurrentJumpSpeed() !== 0 ||
this._currentFallSpeed !== 0
);
@@ -1560,7 +1592,8 @@ namespace gdjs {
//Go on a ladder
behavior._checkTransitionOnLadder();
if (behavior._releaseKey) {
//Release the platform
if (behavior._releasePlatformKey) {
behavior._releaseGrabbedPlatform();
}
@@ -1569,7 +1602,6 @@ namespace gdjs {
}
beforeMovingY(timeDelta: float, oldX: float) {
const behavior = this._behavior;
this._grabbedPlatformLastX = this._grabbedPlatform.owner.getX();
this._grabbedPlatformLastY = this._grabbedPlatform.owner.getY();
}
@@ -1611,11 +1643,17 @@ namespace gdjs {
//Jumping
behavior._checkTransitionJumping();
//Release the ladder
if (behavior._releaseLadderKey) {
behavior._releaseLadder();
}
}
beforeMovingY(timeDelta: float, oldX: float) {
const behavior = this._behavior;
// TODO: we could consider supporting acceleration for ladder climbing in the future.
if (behavior._upKey) {
behavior._requestedDeltaY -= behavior._ladderClimbingSpeed * timeDelta;
}

View File

@@ -332,7 +332,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
);
expect(object.getY()).to.be(platform.getY());
object.getBehavior('auto1').simulateReleaseKey();
object.getBehavior('auto1').simulateReleasePlatformKey();
for (let i = 0; i < 10; ++i) {
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isFalling()).to.be(true);
@@ -389,7 +389,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getBehavior('auto1').isGrabbingPlatform()).to.be(true);
// Release upper platform
object.getBehavior('auto1').simulateReleaseKey();
object.getBehavior('auto1').simulateReleasePlatformKey();
for (let i = 0; i < 35; ++i) {
object.getBehavior('auto1').simulateLeftKey();
runtimeScene.renderAndStep(1000 / 60);
@@ -550,6 +550,11 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
// Forbid to jump
object.getBehavior('auto1').setCanNotAirJump();
// It has no impact as the object is on a platform.
expect(object.getBehavior('auto1').canJump()).to.be(true);
// Jump with sustaining as much as possible, and
// even more (18 frames at 60fps is greater than 0.2s)
for (let i = 0; i < 18; ++i) {
@@ -649,7 +654,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
// Fell from the platform
// Fall from the platform
for (let i = 0; i < 35; ++i) {
object.getBehavior('auto1').simulateLeftKey();
runtimeScene.renderAndStep(1000 / 60);
@@ -663,6 +668,96 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getBehavior('auto1').isFallingWithoutJumping()).to.be(true);
});
it('can be allowed to jump in mid air', function () {
// Ensure the object falls on the platform
for (let i = 0; i < 10; ++i) {
runtimeScene.renderAndStep(1000 / 60);
}
// Check the object is on the platform
expect(object.getY()).to.be(-30); // -30 = -10 (platform y) + -20 (object height)
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isFallingWithoutJumping()).to.be(
false
);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
// Fall from the platform
for (let i = 0; i < 20; ++i) {
object.getBehavior('auto1').simulateLeftKey();
runtimeScene.renderAndStep(1000 / 60);
}
// Allow to jump in mid air
expect(object.getBehavior('auto1').isFalling()).to.be(true);
object.getBehavior('auto1').setCanJump();
expect(object.getBehavior('auto1').canJump()).to.be(true);
// Can jump in the air
object.getBehavior('auto1').simulateJumpKey();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(true);
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isFallingWithoutJumping()).to.be(
false
);
for (let i = 0; i < 40; ++i) {
object.getBehavior('auto1').simulateLeftKey();
runtimeScene.renderAndStep(1000 / 60);
}
expect(object.getBehavior('auto1').isJumping()).to.be(false);
// Can no longer to jump
object.getBehavior('auto1').simulateJumpKey();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(false);
expect(object.getBehavior('auto1').isFalling()).to.be(true);
expect(object.getBehavior('auto1').isFallingWithoutJumping()).to.be(true);
});
it('can allow coyote time', function () {
// Ensure the object falls on the platform
for (let i = 0; i < 10; ++i) {
runtimeScene.renderAndStep(1000 / 60);
}
// Check the object is on the platform
expect(object.getY()).to.be(-30); // -30 = -10 (platform y) + -20 (object height)
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isFallingWithoutJumping()).to.be(
false
);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
// Fall from the platform
for (let i = 0; i < 20; ++i) {
object.getBehavior('auto1').simulateLeftKey();
runtimeScene.renderAndStep(1000 / 60);
}
// Allow to jump
expect(object.getBehavior('auto1').isFalling()).to.be(true);
object.getBehavior('auto1').setCanJump();
expect(object.getBehavior('auto1').canJump()).to.be(true);
// Still falling from the platform
for (let i = 0; i < 4; ++i) {
object.getBehavior('auto1').simulateLeftKey();
runtimeScene.renderAndStep(1000 / 60);
}
// Suppose that we miss an eventual time frame or some condition.
// So we forbid to jump again:
object.getBehavior('auto1').setCanNotAirJump();
expect(object.getBehavior('auto1').canJump()).to.be(false);
// Can no longer to jump in mid air
object.getBehavior('auto1').simulateJumpKey();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(false);
expect(object.getBehavior('auto1').isFalling()).to.be(true);
expect(object.getBehavior('auto1').isFallingWithoutJumping()).to.be(true);
});
it('should not grab a platform while in the ascending phase of a jump', function () {
const topPlatform = addPlatformObject(runtimeScene);
topPlatform.setPosition(12, -80);
@@ -1532,12 +1627,22 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
object.getBehavior('auto1').simulateUpKey();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnLadder()).to.be(true);
//TODO Probably a bug, uncomment it after it's fixed
//expect(object.getBehavior('auto1').isMoving()).to.be(true);
expect(object.getBehavior('auto1').isMoving()).to.be(true);
expect(object.getY()).to.be.below(lastY);
}
};
const releaseLadder = (frameCount) => {
object.getBehavior('auto1').simulateReleaseLadderKey();
for (let i = 0; i < frameCount; ++i) {
const lastY = object.getY();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnLadder()).to.be(false);
expect(object.getBehavior('auto1').isMoving()).to.be(true);
expect(object.getY()).to.be.above(lastY);
}
};
const stayOnLadder = (frameCount) => {
for (let i = 0; i < frameCount; ++i) {
const lastY = object.getY();
@@ -1586,7 +1691,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getBehavior('auto1').isMoving()).to.be(false);
};
it('can climb a ladder', function () {
it('can climb and release a ladder', function () {
object.setPosition(30, -32);
// Ensure the object falls on the platform
fallOnPlatform(10);
@@ -1595,7 +1700,15 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
object.getBehavior('auto1').simulateLadderKey();
climbLadder(10);
stayOnLadder(10);
climbLadder(14);
const objectPositionAfterFirstClimb = object.getY();
releaseLadder(10);
object.getBehavior('auto1').simulateLadderKey();
expect(object.getY()).to.be.within(
// gravity is 1500, 10 frames falling ~ 23px
objectPositionAfterFirstClimb + 22,
objectPositionAfterFirstClimb + 24
);
climbLadder(24);
// Check that we reached the maximum height
const playerAtLadderTop = ladder.getY() - object.getHeight();
expect(object.getY()).to.be.within(

View File

@@ -375,6 +375,16 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.SetFunctionName("closePath")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddScopedAction(
"ClearShapes",
_("Clear shapes"),
_("Clear the rendered shape(s). Useful if not set to be done automatically."),
_("Clear the rendered image of _PARAM0_"),
_("Advanced"),
"res/actions/visibilite24.png",
"res/actions/visibilite.png")
.AddParameter("object", _("Shape Painter object"), "Drawer");
obj.AddAction(
"ClearBetweenFrames",
_("Clear between frames"),

View File

@@ -106,6 +106,9 @@ class PrimitiveDrawingJsExtension : public gd::PlatformExtension {
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::ArcTo"]
.SetFunctionName("drawArcTo");*/
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Drawer::ClearShapes"]
.SetFunctionName("clear");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::ClearBetweenFrames"]
.SetFunctionName("setClearBetweenFrames");

View File

@@ -2,14 +2,15 @@ namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
class ShapePainterRuntimeObjectPixiRenderer {
_object: any;
_graphics: any;
_object: gdjs.ShapePainterRuntimeObject;
_graphics: PIXI.Graphics;
constructor(runtimeObject, runtimeScene) {
constructor(
runtimeObject: gdjs.ShapePainterRuntimeObject,
runtimeScene: gdjs.RuntimeScene
) {
this._object = runtimeObject;
if (this._graphics === undefined) {
this._graphics = new PIXI.Graphics();
}
this._graphics = new PIXI.Graphics();
runtimeScene
.getLayer('')
.getRenderer()
@@ -24,7 +25,7 @@ namespace gdjs {
this._graphics.clear();
}
drawRectangle(x1, y1, x2, y2) {
drawRectangle(x1: float, y1: float, x2: float, y2: float) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
@@ -34,7 +35,7 @@ namespace gdjs {
this._graphics.endFill();
}
drawCircle(x, y, radius) {
drawCircle(x: float, y: float, radius: float) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
@@ -44,7 +45,7 @@ namespace gdjs {
this._graphics.endFill();
}
drawLine(x1, y1, x2, y2, thickness) {
drawLine(x1: float, y1: float, x2: float, y2: float, thickness: float) {
this._graphics.beginFill(
this._object._fillColor,
this._object._fillOpacity / 255
@@ -69,7 +70,7 @@ namespace gdjs {
this._graphics.endFill();
}
drawLineV2(x1, y1, x2, y2, thickness) {
drawLineV2(x1: float, y1: float, x2: float, y2: float, thickness: float) {
this._graphics.lineStyle(
thickness,
this._object._outlineColor,
@@ -80,7 +81,7 @@ namespace gdjs {
this._graphics.endFill();
}
drawEllipse(x1, y1, width, height) {
drawEllipse(x1: float, y1: float, width: float, height: float) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
@@ -90,7 +91,13 @@ namespace gdjs {
this._graphics.endFill();
}
drawRoundedRectangle(x1, y1, x2, y2, radius) {
drawRoundedRectangle(
x1: float,
y1: float,
x2: float,
y2: float,
radius: float
) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
@@ -101,12 +108,20 @@ namespace gdjs {
this._graphics.endFill();
}
drawStar(x1, y1, points, radius, innerRadius, rotation) {
drawStar(
x1: float,
y1: float,
points: float,
radius: float,
innerRadius: float,
rotation: float
) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
this._object._fillOpacity / 255
);
//@ts-ignore from @pixi/graphics-extras
this._graphics.drawStar(
x1,
y1,
@@ -119,7 +134,15 @@ namespace gdjs {
this._graphics.endFill();
}
drawArc(x1, y1, radius, startAngle, endAngle, anticlockwise, closePath) {
drawArc(
x1: float,
y1: float,
radius: float,
startAngle: float,
endAngle: float,
anticlockwise: boolean,
closePath: boolean
) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
@@ -143,7 +166,16 @@ namespace gdjs {
this._graphics.endFill();
}
drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2) {
drawBezierCurve(
x1: float,
y1: float,
cpX: float,
cpY: float,
cpX2: float,
cpY2: float,
x2: float,
y2: float
) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
@@ -154,7 +186,14 @@ namespace gdjs {
this._graphics.endFill();
}
drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2) {
drawQuadraticCurve(
x1: float,
y1: float,
cpX: float,
cpY: float,
x2: float,
y2: float
) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
@@ -176,19 +215,33 @@ namespace gdjs {
this._graphics.endFill();
}
drawPathMoveTo(x1, y1) {
drawPathMoveTo(x1: float, y1: float) {
this._graphics.moveTo(x1, y1);
}
drawPathLineTo(x1, y1) {
drawPathLineTo(x1: float, y1: float) {
this._graphics.lineTo(x1, y1);
}
drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY) {
drawPathBezierCurveTo(
cpX: float,
cpY: float,
cpX2: float,
cpY2: float,
toX: float,
toY: float
) {
this._graphics.bezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
}
drawPathArc(x1, y1, radius, startAngle, endAngle, anticlockwise) {
drawPathArc(
x1: float,
y1: float,
radius: float,
startAngle: float,
endAngle: float,
anticlockwise: boolean
) {
this._graphics.arc(
x1,
y1,
@@ -199,7 +252,7 @@ namespace gdjs {
);
}
drawPathQuadraticCurveTo(cpX, cpY, toX, toY) {
drawPathQuadraticCurveTo(cpX: float, cpY: float, toX: float, toY: float) {
this._graphics.quadraticCurveTo(cpX, cpY, toX, toY);
}

View File

@@ -6,11 +6,11 @@ namespace gdjs {
/** Represents a color in RGB Format */
export type RGBColor = {
/** The Red component of the color, from 0 to 255. */
r: number;
r: integer;
/** The Green component of the color, from 0 to 255. */
g: number;
g: integer;
/** The Blue component of the color, from 0 to 255. */
b: number;
b: integer;
};
/** Initial properties for a for {@link gdjs.ShapePainterRuntimeObject}. */
@@ -19,12 +19,12 @@ namespace gdjs {
fillColor: RGBColor;
/** The color (in RGB format) of the outline of the painted shape */
outlineColor: RGBColor;
/** The opacity of the inner part of the painted shape */
fillOpacity: number;
/** The opacity of the outline of the painted shape */
outlineOpacity: number;
/** The opacity of the inner part of the painted shape, from 0 to 255 */
fillOpacity: float;
/** The opacity of the outline of the painted shape, from 0 to 255 */
outlineOpacity: float;
/** The size of the outline of the painted shape, in pixels. */
outlineSize: number;
outlineSize: float;
/** Use absolute coordinates? */
absoluteCoordinates: boolean;
/** Clear the previous render before the next draw? */
@@ -41,7 +41,7 @@ namespace gdjs {
_outlineColor: integer;
_fillOpacity: float;
_outlineOpacity: float;
_outlineSize: number;
_outlineSize: float;
_absoluteCoordinates: boolean;
_clearBetweenFrames: boolean;
_renderer: gdjs.ShapePainterRuntimeObjectRenderer;
@@ -145,39 +145,52 @@ namespace gdjs {
return true;
}
stepBehaviorsPreEvents(runtimeScene) {
stepBehaviorsPreEvents(runtimeScene: gdjs.RuntimeScene) {
//We redefine stepBehaviorsPreEvents just to clear the graphics before running events.
if (this._clearBetweenFrames) {
this._renderer.clear();
this.clear();
}
super.stepBehaviorsPreEvents(runtimeScene);
}
/**
* Clear the graphics.
*/
clear() {
this._renderer.clear();
}
getVisibilityAABB() {
return this._absoluteCoordinates ? null : this.getAABB();
}
drawRectangle(x1, y1, x2, y2) {
drawRectangle(x1: float, y1: float, x2: float, y2: float) {
this._renderer.drawRectangle(x1, y1, x2, y2);
}
drawCircle(x, y, radius) {
drawCircle(x: float, y: float, radius: float) {
this._renderer.drawCircle(x, y, radius);
}
drawLine(x1, y1, x2, y2, thickness) {
drawLine(x1: float, y1: float, x2: float, y2: float, thickness: float) {
this._renderer.drawLine(x1, y1, x2, y2, thickness);
}
drawLineV2(x1, y1, x2, y2, thickness) {
drawLineV2(x1: float, y1: float, x2: float, y2: float, thickness: float) {
this._renderer.drawLineV2(x1, y1, x2, y2, thickness);
}
drawEllipse(centerX, centerY, width, height) {
drawEllipse(centerX: float, centerY: float, width: float, height: float) {
this._renderer.drawEllipse(centerX, centerY, width, height);
}
drawRoundedRectangle(startX1, startY1, endX2, endY2, radius) {
drawRoundedRectangle(
startX1: float,
startY1: float,
endX2: float,
endY2: float,
radius: float
) {
this._renderer.drawRoundedRectangle(
startX1,
startY1,
@@ -187,7 +200,14 @@ namespace gdjs {
);
}
drawStar(centerX, centerY, points, radius, innerRadius, rotation) {
drawStar(
centerX: float,
centerY: float,
points: float,
radius: float,
innerRadius: float,
rotation: float
) {
this._renderer.drawStar(
centerX,
centerY,
@@ -199,13 +219,13 @@ namespace gdjs {
}
drawArc(
centerX,
centerY,
radius,
startAngle,
endAngle,
anticlockwise,
closePath
centerX: float,
centerY: float,
radius: float,
startAngle: float,
endAngle: float,
anticlockwise: boolean,
closePath: boolean
) {
this._renderer.drawArc(
centerX,
@@ -218,15 +238,31 @@ namespace gdjs {
);
}
drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2) {
drawBezierCurve(
x1: float,
y1: float,
cpX: float,
cpY: float,
cpX2: float,
cpY2: float,
x2: float,
y2: float
) {
this._renderer.drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2);
}
drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2) {
drawQuadraticCurve(
x1: float,
y1: float,
cpX: float,
cpY: float,
x2: float,
y2: float
) {
this._renderer.drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2);
}
beginFillPath(x1, y1) {
beginFillPath(x1: float, y1: float) {
this._renderer.beginFillPath();
this._renderer.drawPathMoveTo(x1, y1);
}
@@ -235,19 +271,33 @@ namespace gdjs {
this._renderer.endFillPath();
}
drawPathMoveTo(x1, y1) {
drawPathMoveTo(x1: float, y1: float) {
this._renderer.drawPathMoveTo(x1, y1);
}
drawPathLineTo(x1, y1) {
drawPathLineTo(x1: float, y1: float) {
this._renderer.drawPathLineTo(x1, y1);
}
drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY) {
drawPathBezierCurveTo(
cpX: float,
cpY: float,
cpX2: float,
cpY2: float,
toX: float,
toY: float
) {
this._renderer.drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
}
drawPathArc(cx, cy, radius, startAngle, endAngle, anticlockwise) {
drawPathArc(
cx: float,
cy: float,
radius: float,
startAngle: float,
endAngle: float,
anticlockwise: boolean
) {
this._renderer.drawPathArc(
cx,
cy,
@@ -258,7 +308,7 @@ namespace gdjs {
);
}
drawPathQuadraticCurveTo(cpX, cpY, toX, toY) {
drawPathQuadraticCurveTo(cpX: float, cpY: float, toX: float, toY: float) {
this._renderer.drawPathQuadraticCurveTo(cpX, cpY, toX, toY);
}
@@ -266,7 +316,7 @@ namespace gdjs {
this._renderer.closePath();
}
setClearBetweenFrames(value): void {
setClearBetweenFrames(value: boolean): void {
this._clearBetweenFrames = value;
}
@@ -274,7 +324,7 @@ namespace gdjs {
return this._clearBetweenFrames;
}
setCoordinatesRelative(value): void {
setCoordinatesRelative(value: boolean): void {
this._absoluteCoordinates = !value;
}
@@ -282,7 +332,11 @@ namespace gdjs {
return !this._absoluteCoordinates;
}
setFillColor(rgbColor): void {
/**
*
* @param rgbColor semicolon separated decimal values
*/
setFillColor(rgbColor: string): void {
const colors = rgbColor.split(';');
if (colors.length < 3) {
return;
@@ -307,7 +361,11 @@ namespace gdjs {
return gdjs.hexNumberToRGB(this._fillColor).b;
}
setOutlineColor(rgbColor): void {
/**
*
* @param rgbColor semicolon separated decimal values
*/
setOutlineColor(rgbColor: string): void {
const colors = rgbColor.split(';');
if (colors.length < 3) {
return;
@@ -333,7 +391,7 @@ namespace gdjs {
return gdjs.hexNumberToRGB(this._outlineColor).b;
}
setOutlineSize(size): void {
setOutlineSize(size: float): void {
this._outlineSize = size;
this._renderer.updateOutline();
}
@@ -342,24 +400,40 @@ namespace gdjs {
return this._outlineSize;
}
setFillOpacity(opacity): void {
/**
*
* @param opacity from 0 to 255
*/
setFillOpacity(opacity: float): void {
this._fillOpacity = opacity;
}
/**
*
* @returns an opacity value from 0 to 255.
*/
getFillOpacity() {
return this._fillOpacity;
}
setOutlineOpacity(opacity): void {
/**
*
* @param opacity from 0 to 255
*/
setOutlineOpacity(opacity: float): void {
this._outlineOpacity = opacity;
this._renderer.updateOutline();
}
/**
*
* @returns an opacity value from 0 to 255.
*/
getOutlineOpacity() {
return this._outlineOpacity;
}
setX(x): void {
setX(x: float): void {
if (x === this.x) {
return;
}
@@ -367,7 +441,7 @@ namespace gdjs {
this._renderer.updateXPosition();
}
setY(y): void {
setY(y: float): void {
if (y === this.y) {
return;
}

View File

@@ -39,6 +39,7 @@ namespace gdjs {
const shopifyClient = ShopifyBuy.buildClient(config);
ShopifyClientsManager.set(runtimeScene, name, shopifyClient);
};
export const getCheckoutUrlForProduct = function (
runtimeScene,
name,

View File

@@ -80,7 +80,12 @@ bool Exporter::ExportWholePixiProject(
// end of compatibility code
// Export engine libraries
helper.AddLibsInclude(true, false, false, includesFiles);
helper.AddLibsInclude(
/*pixiRenderers=*/true,
/*cocosRenderers=*/false,
/*websocketDebuggerClient=*/false,
exportedProject.GetLoadingScreen().GetGDevelopLogoStyle(),
includesFiles);
// Export files for object and behaviors
helper.ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);
@@ -191,7 +196,12 @@ bool Exporter::ExportWholeCocos2dProject(gd::Project &project,
// end of compatibility code
// Export engine libraries
helper.AddLibsInclude(false, true, false, includesFiles);
helper.AddLibsInclude(
/*pixiRenderers=*/false,
/*cocosRenderers=*/true,
/*websocketDebuggerClient=*/false,
exportedProject.GetLoadingScreen().GetGDevelopLogoStyle(),
includesFiles);
// Export files for object and behaviors
helper.ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);

View File

@@ -79,8 +79,12 @@ bool ExporterHelper::ExportProjectForPixiPreview(
gd::Project exportedProject = options.project;
// Always disable the splash for preview
exportedProject.GetLoadingScreen().ShowGDevelopSplash(false);
if (!options.fullLoadingScreen) {
// Most of the time, we skip the logo and minimum duration so that
// the preview start as soon as possible.
exportedProject.GetLoadingScreen().ShowGDevelopSplash(false);
exportedProject.GetLoadingScreen().SetMinDuration(0);
}
// Export resources (*before* generating events as some resources filenames
// may be updated)
@@ -96,7 +100,11 @@ bool ExporterHelper::ExportProjectForPixiPreview(
// end of compatibility code
// Export engine libraries
AddLibsInclude(true, false, true, includesFiles);
AddLibsInclude(/*pixiRenderers=*/true,
/*cocosRenderers=*/false,
/*websocketDebuggerClient=*/true,
exportedProject.GetLoadingScreen().GetGDevelopLogoStyle(),
includesFiles);
// Export files for object and behaviors
ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);
@@ -598,12 +606,12 @@ bool ExporterHelper::CompleteIndexFile(
void ExporterHelper::AddLibsInclude(bool pixiRenderers,
bool cocosRenderers,
bool websocketDebuggerClient,
gd::String gdevelopLogoStyle,
std::vector<gd::String> &includesFiles) {
// First, do not forget common includes (they must be included before events
// generated code files).
InsertUnique(includesFiles, "libs/jshashtable.js");
InsertUnique(includesFiles, "gd.js");
InsertUnique(includesFiles, "gd-splash-image.js");
InsertUnique(includesFiles, "libs/rbush.js");
InsertUnique(includesFiles, "inputmanager.js");
InsertUnique(includesFiles, "jsonmanager.js");
@@ -635,6 +643,16 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
InsertUnique(includesFiles, "events-tools/windowtools.js");
InsertUnique(includesFiles, "events-tools/networktools.js");
if (gdevelopLogoStyle == "dark") {
InsertUnique(includesFiles, "splash/gd-logo-dark.js");
} else if (gdevelopLogoStyle == "dark-colored") {
InsertUnique(includesFiles, "splash/gd-logo-dark-colored.js");
} else if (gdevelopLogoStyle == "light-colored") {
InsertUnique(includesFiles, "splash/gd-logo-light-colored.js");
} else {
InsertUnique(includesFiles, "splash/gd-logo-light.js");
}
if (websocketDebuggerClient) {
InsertUnique(includesFiles, "websocket-debugger-client/hot-reloader.js");
InsertUnique(includesFiles,

View File

@@ -35,6 +35,7 @@ struct PreviewExportOptions {
: project(project_),
exportPath(exportPath_),
projectDataOnlyExport(false),
fullLoadingScreen(false),
nonRuntimeScriptsCacheBurst(0){};
/**
@@ -85,6 +86,15 @@ struct PreviewExportOptions {
return *this;
}
/**
* \brief Set if the export should show the full loading screen (false
* by default, skipping the minimum duration and GDevelop logo).
*/
PreviewExportOptions &SetFullLoadingScreen(bool enable) {
fullLoadingScreen = enable;
return *this;
}
/**
* \brief If set to a non zero value, the exported script URLs will have an
* extra search parameter added (with the given value) to ensure browser cache
@@ -103,6 +113,7 @@ struct PreviewExportOptions {
gd::String externalLayoutName;
std::map<gd::String, int> includeFileHashes;
bool projectDataOnlyExport;
bool fullLoadingScreen;
unsigned int nonRuntimeScriptsCacheBurst;
};
@@ -158,6 +169,7 @@ class ExporterHelper {
void AddLibsInclude(bool pixiRenderers,
bool cocosRenderers,
bool websocketDebuggerClient,
gd::String gdevelopLogoStyle,
std::vector<gd::String> &includesFiles);
/**

View File

@@ -5,11 +5,13 @@
*/
namespace gdjs {
export class LoadingScreenCocosRenderer {
render(percent) {
setPercent(percent) {
console.log('Loading ' + percent + '%');
}
unload() {}
unload() {
return Promise.resolve();
}
}
// Nothing to do

View File

@@ -17,6 +17,7 @@ namespace gdjs {
}
runtimeScene.getLayer(layer).setCameraX(x, cameraId);
};
export const setCameraY = function (
runtimeScene: gdjs.RuntimeScene,
y: float,
@@ -28,6 +29,7 @@ namespace gdjs {
}
runtimeScene.getLayer(layer).setCameraY(y, cameraId);
};
export const getCameraX = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
@@ -38,6 +40,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).getCameraX();
};
export const getCameraY = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
@@ -48,6 +51,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).getCameraY();
};
export const getCameraWidth = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
@@ -58,6 +62,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).getCameraWidth();
};
export const getCameraHeight = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
@@ -68,6 +73,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).getCameraHeight();
};
export const showLayer = function (
runtimeScene: gdjs.RuntimeScene,
layer: string
@@ -77,6 +83,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).show(true);
};
export const hideLayer = function (
runtimeScene: gdjs.RuntimeScene,
layer: string
@@ -86,6 +93,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).show(false);
};
export const layerIsVisible = function (
runtimeScene: gdjs.RuntimeScene,
layer: string
@@ -95,6 +103,7 @@ namespace gdjs {
runtimeScene.getLayer(layer).isVisible()
);
};
export const setCameraRotation = function (
runtimeScene: gdjs.RuntimeScene,
rotation: float,
@@ -108,6 +117,7 @@ namespace gdjs {
.getLayer(layer)
.setCameraRotation(rotation, cameraId);
};
export const getCameraRotation = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
@@ -118,6 +128,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).getCameraRotation(cameraId);
};
export const getCameraZoom = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
@@ -128,6 +139,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).getCameraZoom(cameraId);
};
export const setCameraZoom = function (
runtimeScene: gdjs.RuntimeScene,
newZoom: float,
@@ -139,6 +151,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).setCameraZoom(newZoom, cameraId);
};
export const centerCamera = function (
runtimeScene: gdjs.RuntimeScene,
object: gdjs.RuntimeObject | null,
@@ -162,6 +175,7 @@ namespace gdjs {
layer.setCameraX(object.getDrawableX() + object.getCenterX(), cameraId);
layer.setCameraY(object.getDrawableY() + object.getCenterY(), cameraId);
};
export const centerCameraWithinLimits = function (
runtimeScene: gdjs.RuntimeScene,
object: gdjs.RuntimeObject | null,
@@ -309,6 +323,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).isEffectEnabled(effect);
};
export const setLayerTimeScale = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
@@ -319,6 +334,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).setTimeScale(timeScale);
};
export const getLayerTimeScale = function (
runtimeScene: gdjs.RuntimeScene,
layer: string
@@ -328,6 +344,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).getTimeScale();
};
export const setLayerDefaultZOrder = function (
runtimeScene: gdjs.RuntimeScene,
layer: string,
@@ -338,6 +355,7 @@ namespace gdjs {
}
return runtimeScene.getLayer(layer).setDefaultZOrder(defaultZOrder);
};
export const getLayerDefaultZOrder = function (
runtimeScene: gdjs.RuntimeScene,
layer: string

View File

@@ -179,9 +179,11 @@ namespace gdjs {
export const anyKeyPressed = function (runtimeScene) {
return runtimeScene.getGame().getInputManager().anyKeyPressed();
};
export const anyKeyReleased = function (runtimeScene) {
return runtimeScene.getGame().getInputManager().anyKeyReleased();
};
export const isMouseButtonPressed = function (runtimeScene, button) {
if (button === 'Left') {
return runtimeScene
@@ -203,6 +205,7 @@ namespace gdjs {
}
return false;
};
export const isMouseButtonReleased = function (runtimeScene, button) {
if (button === 'Left') {
return runtimeScene
@@ -224,21 +227,27 @@ namespace gdjs {
}
return false;
};
export const hideCursor = function (runtimeScene) {
runtimeScene.getRenderer().hideCursor();
};
export const showCursor = function (runtimeScene) {
runtimeScene.getRenderer().showCursor();
};
export const getMouseWheelDelta = function (runtimeScene) {
return runtimeScene.getGame().getInputManager().getMouseWheelDelta();
};
export const isScrollingUp = function (runtimeScene) {
return runtimeScene.getGame().getInputManager().isScrollingUp();
};
export const isScrollingDown = function (runtimeScene) {
return runtimeScene.getGame().getInputManager().isScrollingDown();
};
export const getMouseX = function (runtimeScene, layer, camera) {
return runtimeScene
.getLayer(layer)
@@ -247,6 +256,7 @@ namespace gdjs {
runtimeScene.getGame().getInputManager().getMouseY()
)[0];
};
export const getMouseY = function (runtimeScene, layer, camera) {
return runtimeScene
.getLayer(layer)
@@ -255,9 +265,11 @@ namespace gdjs {
runtimeScene.getGame().getInputManager().getMouseY()
)[1];
};
export const _cursorIsOnObject = function (obj, runtimeScene) {
return obj.cursorOnObject(runtimeScene);
};
export const cursorOnObject = function (
objectsLists,
runtimeScene,
@@ -271,6 +283,7 @@ namespace gdjs {
runtimeScene
);
};
export const getTouchX = function (
runtimeScene,
identifier,
@@ -284,6 +297,7 @@ namespace gdjs {
runtimeScene.getGame().getInputManager().getTouchY(identifier)
)[0];
};
export const getTouchY = function (
runtimeScene,
identifier,
@@ -297,12 +311,15 @@ namespace gdjs {
runtimeScene.getGame().getInputManager().getTouchY(identifier)
)[1];
};
export const getLastTouchId = function () {
return gdjs.evtTools.input.lastTouchId || 0;
};
export const getLastEndedTouchId = function () {
return gdjs.evtTools.input.lastEndedTouchId || 0;
};
export const popStartedTouch = function (runtimeScene) {
const startedTouchId = runtimeScene
.getGame()
@@ -314,6 +331,7 @@ namespace gdjs {
}
return false;
};
export const popEndedTouch = function (runtimeScene) {
const endedTouchId = runtimeScene
.getGame()
@@ -325,6 +343,7 @@ namespace gdjs {
}
return false;
};
export const touchSimulateMouse = function (runtimeScene, enable) {
runtimeScene.getGame().getInputManager().touchSimulateMouse(enable);
};

View File

@@ -217,6 +217,7 @@ namespace gdjs {
}
arr.length = finalSize;
};
export const hitBoxesCollisionTest = function (
objectsLists1,
objectsLists2,
@@ -232,9 +233,11 @@ namespace gdjs {
ignoreTouchingEdges
);
};
export const _distanceBetweenObjects = function (obj1, obj2, distance) {
return obj1.getSqDistanceToObject(obj2) <= distance;
};
export const distanceTest = function (
objectsLists1,
objectsLists2,
@@ -249,6 +252,7 @@ namespace gdjs {
distance * distance
);
};
export const _movesToward = function (obj1, obj2, tolerance) {
if (obj1.hasNoForces()) {
return false;
@@ -272,6 +276,7 @@ namespace gdjs {
tolerance / 2
);
};
export const movesTowardTest = function (
objectsLists1,
objectsLists2,
@@ -286,6 +291,7 @@ namespace gdjs {
tolerance
);
};
export const _turnedToward = function (obj1, obj2, tolerance) {
let objAngle = Math.atan2(
obj2.getDrawableY() +
@@ -303,6 +309,7 @@ namespace gdjs {
tolerance / 2
);
};
export const turnedTowardTest = function (
objectsLists1,
objectsLists2,
@@ -317,6 +324,7 @@ namespace gdjs {
tolerance
);
};
export const pickAllObjects = function (objectsContext, objectsLists) {
for (const name in objectsLists.items) {
if (objectsLists.items.hasOwnProperty(name)) {
@@ -328,6 +336,7 @@ namespace gdjs {
}
return true;
};
export const pickRandomObject = function (runtimeScene, objectsLists) {
// Compute one many objects we have
let objectsCount = 0;
@@ -366,6 +375,7 @@ namespace gdjs {
gdjs.evtTools.object.pickOnly(objectsLists, theChosenOne);
return true;
};
export const pickNearestObject = function (objectsLists, x, y, inverted) {
let bestObject = null;
let best = 0;
@@ -391,6 +401,7 @@ namespace gdjs {
gdjs.evtTools.object.pickOnly(objectsLists, bestObject);
return true;
};
export const raycastObject = function (
objectsLists,
x,
@@ -412,6 +423,7 @@ namespace gdjs {
inverted
);
};
export const raycastObjectToPosition = function (
objectsLists,
x,

View File

@@ -9,12 +9,15 @@ namespace gdjs {
export const sceneJustBegins = function (runtimeScene) {
return runtimeScene.getTimeManager().isFirstFrame();
};
export const sceneJustResumed = function (runtimeScene) {
return runtimeScene.sceneJustResumed();
};
export const getSceneName = function (runtimeScene) {
return runtimeScene.getName();
};
export const setBackgroundColor = function (runtimeScene, rgbColor) {
const colors = rgbColor.split(';');
if (colors.length < 3) {
@@ -26,15 +29,19 @@ namespace gdjs {
parseInt(colors[2])
);
};
export const getElapsedTimeInSeconds = function (runtimeScene) {
return runtimeScene.getTimeManager().getElapsedTime() / 1000;
};
export const setTimeScale = function (runtimeScene, timeScale) {
return runtimeScene.getTimeManager().setTimeScale(timeScale);
};
export const getTimeScale = function (runtimeScene) {
return runtimeScene.getTimeManager().getTimeScale();
};
export const timerElapsedTime = function (
runtimeScene,
timeInSeconds,
@@ -49,6 +56,7 @@ namespace gdjs {
timeManager.getTimer(timerName).getTime() / 1000 >= timeInSeconds
);
};
export const timerPaused = function (runtimeScene, timerName) {
const timeManager = runtimeScene.getTimeManager();
if (!timeManager.hasTimer(timerName)) {
@@ -56,6 +64,7 @@ namespace gdjs {
}
return timeManager.getTimer(timerName).isPaused();
};
export const resetTimer = function (runtimeScene, timerName) {
const timeManager = runtimeScene.getTimeManager();
if (!timeManager.hasTimer(timerName)) {
@@ -64,6 +73,7 @@ namespace gdjs {
timeManager.getTimer(timerName).reset();
}
};
export const pauseTimer = function (runtimeScene, timerName) {
const timeManager = runtimeScene.getTimeManager();
if (!timeManager.hasTimer(timerName)) {
@@ -71,6 +81,7 @@ namespace gdjs {
}
timeManager.getTimer(timerName).setPaused(true);
};
export const unpauseTimer = function (runtimeScene, timerName) {
const timeManager = runtimeScene.getTimeManager();
if (!timeManager.hasTimer(timerName)) {
@@ -78,10 +89,12 @@ namespace gdjs {
}
return timeManager.getTimer(timerName).setPaused(false);
};
export const removeTimer = function (runtimeScene, timerName) {
const timeManager = runtimeScene.getTimeManager();
timeManager.removeTimer(timerName);
};
export const getTimerElapsedTimeInSeconds = function (
runtimeScene,
timerName
@@ -92,9 +105,11 @@ namespace gdjs {
}
return timeManager.getTimer(timerName).getTime() / 1000;
};
export const getTimeFromStartInSeconds = function (runtimeScene) {
return runtimeScene.getTimeManager().getTimeFromStart() / 1000;
};
export const getTime = function (runtimeScene, what) {
if (what === 'timestamp') {
return Date.now();
@@ -123,6 +138,7 @@ namespace gdjs {
}
return 0;
};
export const replaceScene = function (
runtimeScene,
newSceneName,
@@ -138,6 +154,7 @@ namespace gdjs {
newSceneName
);
};
export const pushScene = function (runtimeScene, newSceneName) {
if (!runtimeScene.getGame().getSceneData(newSceneName)) {
return;
@@ -147,12 +164,15 @@ namespace gdjs {
newSceneName
);
};
export const popScene = function (runtimeScene) {
runtimeScene.requestChange(gdjs.SceneChangeRequest.POP_SCENE);
};
export const stopGame = function (runtimeScene) {
runtimeScene.requestChange(gdjs.SceneChangeRequest.STOP_GAME);
};
export const createObjectsFromExternalLayout = function (
scene,
externalLayout,

View File

@@ -11,6 +11,7 @@ namespace gdjs {
) {
return runtimeScene.getSoundManager().getGlobalVolume();
};
export const setGlobalVolume = function (
runtimeScene: gdjs.RuntimeScene,
globalVolume: float
@@ -34,6 +35,7 @@ namespace gdjs {
.getSoundManager()
.playSound(soundFile, loop, volume, pitch);
};
export const playSoundOnChannel = function (
runtimeScene: gdjs.RuntimeScene,
soundFile: string,
@@ -46,6 +48,7 @@ namespace gdjs {
.getSoundManager()
.playSoundOnChannel(soundFile, channel, loop, volume, pitch);
};
export const stopSoundOnChannel = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -53,6 +56,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
sound && sound.stop();
};
export const pauseSoundOnChannel = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -60,6 +64,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
sound && sound.pause();
};
export const continueSoundOnChannel = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -69,6 +74,7 @@ namespace gdjs {
sound.play();
}
};
export const isSoundOnChannelPlaying = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -76,6 +82,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
return sound ? sound.playing() : false;
};
export const isSoundOnChannelPaused = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -83,6 +90,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
return sound ? sound.paused() : false;
};
export const isSoundOnChannelStopped = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -90,6 +98,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
return sound ? sound.stopped() : true;
};
export const getSoundOnChannelVolume = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -97,6 +106,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
return sound ? sound.getVolume() * 100 : 100;
};
export const setSoundOnChannelVolume = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer,
@@ -105,6 +115,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
sound && sound.setVolume(volume / 100);
};
export const getSoundOnChannelPlayingOffset = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -112,6 +123,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
return sound ? sound.getSeek() : 0;
};
export const setSoundOnChannelPlayingOffset = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer,
@@ -120,6 +132,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
sound && sound.setSeek(playingOffset);
};
export const getSoundOnChannelPitch = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -127,6 +140,7 @@ namespace gdjs {
const sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
return sound ? sound.getRate() : 1;
};
export const setSoundOnChannelPitch = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer,
@@ -164,6 +178,7 @@ namespace gdjs {
.getSoundManager()
.playMusic(soundFile, loop, volume, pitch);
};
export const playMusicOnChannel = function (
runtimeScene: gdjs.RuntimeScene,
soundFile: string,
@@ -176,6 +191,7 @@ namespace gdjs {
.getSoundManager()
.playMusicOnChannel(soundFile, channel, loop, volume, pitch);
};
export const stopMusicOnChannel = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -183,6 +199,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
music && music.stop();
};
export const pauseMusicOnChannel = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -190,6 +207,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
music && music.pause();
};
export const continueMusicOnChannel = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -199,6 +217,7 @@ namespace gdjs {
music.play();
}
};
export const isMusicOnChannelPlaying = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -206,6 +225,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
return music ? music.playing() : false;
};
export const isMusicOnChannelPaused = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -213,6 +233,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
return music ? music.paused() : false;
};
export const isMusicOnChannelStopped = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -220,6 +241,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
return music ? music.stopped() : true;
};
export const getMusicOnChannelVolume = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -227,6 +249,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
return music ? music.getVolume() * 100 : 100;
};
export const setMusicOnChannelVolume = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer,
@@ -235,6 +258,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
music && music.setVolume(volume / 100);
};
export const getMusicOnChannelPlayingOffset = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -242,6 +266,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
return music ? music.getSeek() : 0;
};
export const setMusicOnChannelPlayingOffset = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer,
@@ -250,6 +275,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
music && music.setSeek(playingOffset);
};
export const getMusicOnChannelPitch = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer
@@ -257,6 +283,7 @@ namespace gdjs {
const music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
return music ? music.getRate() : 1;
};
export const setMusicOnChannelPitch = function (
runtimeScene: gdjs.RuntimeScene,
channel: integer,

View File

@@ -126,6 +126,7 @@ namespace gdjs {
}
return returnValue;
};
export const clearJSONFile = (name: string) => {
return loadObject(name, (jsObject) => {
for (const p in jsObject) {
@@ -136,6 +137,7 @@ namespace gdjs {
return true;
});
};
export const elementExistsInJSONFile = (
name: string,
elementPath: string
@@ -152,6 +154,7 @@ namespace gdjs {
return true;
});
};
export const deleteElementFromJSONFile = (
name: string,
elementPath: string
@@ -172,6 +175,7 @@ namespace gdjs {
return true;
});
};
export const writeNumberInJSONFile = (
name: string,
elementPath: string,
@@ -193,6 +197,7 @@ namespace gdjs {
return true;
});
};
export const writeStringInJSONFile = (
name: string,
elementPath: string,
@@ -214,6 +219,7 @@ namespace gdjs {
return true;
});
};
export const readNumberFromJSONFile = (
name: string,
elementPath: string,
@@ -239,6 +245,7 @@ namespace gdjs {
return true;
});
};
export const readStringFromJSONFile = (
name: string,
elementPath: string,

View File

@@ -21,6 +21,7 @@ namespace gdjs {
.getRenderer()
.setMargins(top, right, bottom, left);
};
export const setFullScreen = function (
runtimeScene: gdjs.RuntimeScene,
enable: boolean,
@@ -29,11 +30,13 @@ namespace gdjs {
runtimeScene.getGame().getRenderer().keepAspectRatio(keepAspectRatio);
runtimeScene.getGame().getRenderer().setFullScreen(enable);
};
export const isFullScreen = function (
runtimeScene: gdjs.RuntimeScene
): boolean {
return runtimeScene.getGame().getRenderer().isFullScreen();
};
export const setWindowSize = function (
runtimeScene: gdjs.RuntimeScene,
width: float,
@@ -45,9 +48,11 @@ namespace gdjs {
runtimeScene.getGame().setGameResolutionSize(width, height);
}
};
export const centerWindow = function (runtimeScene: gdjs.RuntimeScene) {
runtimeScene.getGame().getRenderer().centerWindow();
};
export const setGameResolutionSize = function (
runtimeScene: gdjs.RuntimeScene,
width: float,
@@ -55,29 +60,34 @@ namespace gdjs {
) {
runtimeScene.getGame().setGameResolutionSize(width, height);
};
export const setGameResolutionResizeMode = function (
runtimeScene: gdjs.RuntimeScene,
resizeMode: string
) {
runtimeScene.getGame().setGameResolutionResizeMode(resizeMode);
};
export const setAdaptGameResolutionAtRuntime = function (
runtimeScene: gdjs.RuntimeScene,
enable: boolean
) {
runtimeScene.getGame().setAdaptGameResolutionAtRuntime(enable);
};
export const setWindowTitle = function (
runtimeScene: gdjs.RuntimeScene,
title: string
) {
runtimeScene.getGame().getRenderer().setWindowTitle(title);
};
export const getWindowTitle = function (
runtimeScene: gdjs.RuntimeScene
): string {
return runtimeScene.getGame().getRenderer().getWindowTitle();
};
export const getWindowInnerWidth = function (): number {
if (
gdjs.RuntimeGameRenderer &&
@@ -88,6 +98,7 @@ namespace gdjs {
// @ts-ignore
return typeof window !== 'undefined' ? window.innerWidth : 800;
};
export const getWindowInnerHeight = function (): number {
if (
gdjs.RuntimeGameRenderer &&
@@ -98,16 +109,19 @@ namespace gdjs {
// @ts-ignore
return typeof window !== 'undefined' ? window.innerHeight : 800;
};
export const getGameResolutionWidth = function (
runtimeScene: gdjs.RuntimeScene
): number {
return runtimeScene.getGame().getGameResolutionWidth();
};
export const getGameResolutionHeight = function (
runtimeScene: gdjs.RuntimeScene
): number {
return runtimeScene.getGame().getGameResolutionHeight();
};
export const openURL = function (
url: string,
runtimeScene: gdjs.RuntimeScene

File diff suppressed because one or more lines are too long

View File

@@ -38,6 +38,9 @@ namespace gdjs {
export const callbacksRuntimeSceneUnloaded: Array<RuntimeSceneCallback> = [];
export const callbacksObjectDeletedFromScene: Array<RuntimeSceneRuntimeObjectCallback> = [];
/** Base64 encoded logo of GDevelop for the splash screen. */
export let gdevelopLogo: string = '';
/**
* Convert a rgb color value to a hex string.
*

View File

@@ -1,89 +1,255 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
class LoadingScreenPixiRenderer {
_pixiRenderer: any;
_loadingScreen: any;
_progressText: any;
_madeWithText: any;
_websiteText: any;
_splashImage: any;
enum LoadingScreenState {
NOT_STARTED,
STARTED,
FINISHED,
}
constructor(runtimeGamePixiRenderer, loadingScreenSetup) {
const fadeIn = (
object: PIXI.DisplayObject | null,
duration: float,
deltaTimeInMs: float
) => {
if (!object) return;
if (duration > 0) {
object.alpha += ((1 / duration) * deltaTimeInMs) / 1000;
if (object.alpha > 1) object.alpha = 1;
} else {
object.alpha = 1;
}
};
const hasFadedIn = (object: PIXI.DisplayObject | null) => {
return !object || object.alpha >= 1;
};
class LoadingScreenPixiRenderer {
_pixiRenderer: PIXI.Renderer | null;
_loadingScreenData: LoadingScreenData;
_loadingScreenContainer: PIXI.Container;
_backgroundSprite: PIXI.Sprite | null = null;
_gdevelopLogoSprite: PIXI.Sprite | null = null;
_progressBarGraphics: PIXI.Graphics | null = null;
_state: LoadingScreenState = LoadingScreenState.NOT_STARTED;
_startTimeInMs: float = 0;
_backgroundReadyTimeInMs: float = 0;
_lastFrameTimeInMs: float = 0;
_progressPercent: float = 0;
constructor(
runtimeGamePixiRenderer: gdjs.RuntimeGamePixiRenderer,
imageManager: gdjs.PixiImageManager,
loadingScreenData: LoadingScreenData
) {
this._loadingScreenData = loadingScreenData;
this._loadingScreenContainer = new PIXI.Container();
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(' ', {
fontSize: '30px',
fontFamily: 'Arial',
fill: '#FFFFFF',
align: 'center',
});
this._loadingScreen.addChild(this._progressText);
if (loadingScreenSetup && loadingScreenSetup.showGDevelopSplash) {
this._madeWithText = new PIXI.Text('Made with', {
fontSize: '30px',
fontFamily: 'Arial',
fill: '#FFFFFF',
align: 'center',
});
this._madeWithText.position.y = this._pixiRenderer.height / 2 - 130;
this._websiteText = new PIXI.Text('gdevelop-app.com', {
fontSize: '30px',
fontFamily: 'Arial',
fill: '#FFFFFF',
align: 'center',
});
this._websiteText.position.y = this._pixiRenderer.height / 2 + 100;
this._splashImage = PIXI.Sprite.from(gdjs.splashImage);
this._splashImage.position.x = this._pixiRenderer.width / 2;
this._splashImage.position.y = this._pixiRenderer.height / 2;
this._splashImage.anchor.x = 0.5;
this._splashImage.anchor.y = 0.5;
this._splashImage.scale.x = this._pixiRenderer.width / 800;
this._splashImage.scale.y = this._pixiRenderer.width / 800;
this._loadingScreen.addChild(this._splashImage);
this._loadingScreen.addChild(this._madeWithText);
this._loadingScreen.addChild(this._websiteText);
this._pixiRenderer.backgroundColor = this._loadingScreenData.backgroundColor;
const backgroundTexture = imageManager.getPIXITexture(
loadingScreenData.backgroundImageResourceName
);
if (backgroundTexture !== imageManager.getInvalidPIXITexture()) {
this._backgroundSprite = PIXI.Sprite.from(backgroundTexture);
this._backgroundSprite.alpha = 0;
this._backgroundSprite.anchor.x = 0.5;
this._backgroundSprite.anchor.y = 0.5;
this._loadingScreenContainer.addChild(this._backgroundSprite);
}
if (loadingScreenData.showGDevelopSplash) {
this._gdevelopLogoSprite = PIXI.Sprite.from(gdjs.gdevelopLogo);
this._gdevelopLogoSprite.alpha = 0;
this._gdevelopLogoSprite.anchor.x = 0.5;
this._gdevelopLogoSprite.anchor.y = 0.5;
this._loadingScreenContainer.addChild(this._gdevelopLogoSprite);
}
if (loadingScreenData.showProgressBar) {
this._progressBarGraphics = new PIXI.Graphics();
this._progressBarGraphics.alpha = 0;
this._loadingScreenContainer.addChild(this._progressBarGraphics);
}
this._render(performance.now());
}
setPercent(percent: number) {
this._progressPercent = percent;
}
private _startLoadingScreen() {
if (!this._pixiRenderer) return;
this._state = LoadingScreenState.STARTED;
this._startTimeInMs = performance.now();
}
private _updatePositions() {
if (!this._pixiRenderer) return;
if (this._backgroundSprite && this._backgroundSprite.texture.valid) {
this._backgroundSprite.position.x = this._pixiRenderer.width / 2;
this._backgroundSprite.position.y = this._pixiRenderer.height / 2;
const scale = Math.max(
this._pixiRenderer.width / this._backgroundSprite.texture.width,
this._pixiRenderer.height / this._backgroundSprite.texture.height
);
this._backgroundSprite.scale.x = scale;
this._backgroundSprite.scale.y = scale;
}
if (this._gdevelopLogoSprite) {
this._gdevelopLogoSprite.position.x = this._pixiRenderer.width / 2;
this._gdevelopLogoSprite.position.y = this._pixiRenderer.height / 2;
const logoWidth = 680;
const border =
this._pixiRenderer.width > this._pixiRenderer.height &&
this._pixiRenderer.width > 500
? 150
: 35;
const desiredWidth = Math.min(
logoWidth,
Math.max(1, this._pixiRenderer.width - border * 2)
);
const scale = desiredWidth / logoWidth;
this._gdevelopLogoSprite.scale.x = scale;
this._gdevelopLogoSprite.scale.y = scale;
// Give up trying to show the logo if the resolution is really too small.
// TODO: use a low resolution logo instead.
this._gdevelopLogoSprite.visible =
this._pixiRenderer.width > 200 && this._pixiRenderer.height > 200;
}
}
render(percent) {
private _render(timeInMs: float) {
if (!this._pixiRenderer) {
return;
}
const screenBorder = 10;
if (this._madeWithText) {
this._madeWithText.position.x =
this._pixiRenderer.width / 2 - this._madeWithText.width / 2;
this._madeWithText.position.y =
this._pixiRenderer.height / 2 -
this._splashImage.height / 2 -
this._madeWithText.height -
20;
// Continue the rendering loop as long as the loading screen is not finished.
if (this._state !== LoadingScreenState.FINISHED) {
requestAnimationFrame(() => this._render(performance.now()));
}
if (this._websiteText) {
this._websiteText.position.x =
this._pixiRenderer.width - this._websiteText.width - screenBorder;
this._websiteText.position.y =
this._pixiRenderer.height - this._websiteText.height - screenBorder;
const deltaTimeInMs = this._lastFrameTimeInMs
? timeInMs - this._lastFrameTimeInMs
: 0;
this._lastFrameTimeInMs = timeInMs;
this._updatePositions();
if (this._state == LoadingScreenState.NOT_STARTED) {
if (!this._backgroundSprite || this._backgroundSprite.texture.valid) {
this._startLoadingScreen();
}
} else if (this._state == LoadingScreenState.STARTED) {
const backgroundFadeInDuration = this._loadingScreenData
.backgroundFadeInDuration;
fadeIn(this._backgroundSprite, backgroundFadeInDuration, deltaTimeInMs);
if (hasFadedIn(this._backgroundSprite)) {
if (!this._backgroundReadyTimeInMs)
this._backgroundReadyTimeInMs = timeInMs;
const logoAndProgressFadeInDuration = this._loadingScreenData
.logoAndProgressFadeInDuration;
const logoAndProgressLogoFadeInDelay = this._loadingScreenData
.logoAndProgressLogoFadeInDelay;
if (
timeInMs - this._backgroundReadyTimeInMs >
logoAndProgressLogoFadeInDelay * 1000
) {
fadeIn(
this._gdevelopLogoSprite,
logoAndProgressFadeInDuration,
deltaTimeInMs
);
fadeIn(
this._progressBarGraphics,
logoAndProgressFadeInDuration,
deltaTimeInMs
);
}
}
if (this._progressBarGraphics) {
const color = this._loadingScreenData.progressBarColor;
let progressBarWidth =
(this._loadingScreenData.progressBarWidthPercent / 100) *
this._pixiRenderer.width;
if (this._loadingScreenData.progressBarMaxWidth > 0) {
if (progressBarWidth > this._loadingScreenData.progressBarMaxWidth)
progressBarWidth = this._loadingScreenData.progressBarMaxWidth;
}
if (this._loadingScreenData.progressBarMinWidth > 0) {
if (progressBarWidth < this._loadingScreenData.progressBarMinWidth)
progressBarWidth = this._loadingScreenData.progressBarMinWidth;
}
const progressBarHeight = this._loadingScreenData.progressBarHeight;
const progressBarX = Math.floor(
this._pixiRenderer.width / 2 - progressBarWidth / 2
);
const progressBarY =
this._pixiRenderer.height < 350
? Math.floor(this._pixiRenderer.height - 10 - progressBarHeight)
: Math.floor(this._pixiRenderer.height - 90 - progressBarHeight);
const lineWidth = 1;
// Display bar with an additional 1% to ensure it's filled at the end.
const progress = Math.min(1, (this._progressPercent + 1) / 100);
this._progressBarGraphics.clear();
this._progressBarGraphics.lineStyle(lineWidth, color, 1, 0);
this._progressBarGraphics.drawRect(
progressBarX,
progressBarY,
progressBarWidth,
progressBarHeight
);
this._progressBarGraphics.beginFill(color, 1);
this._progressBarGraphics.lineStyle(0, color, 1);
this._progressBarGraphics.drawRect(
progressBarX + lineWidth,
progressBarY + lineWidth,
progressBarWidth * progress - lineWidth * 2,
progressBarHeight - lineWidth * 2
);
this._progressBarGraphics.endFill();
}
}
this._progressText.text = percent + '%';
this._progressText.position.x = screenBorder;
this._progressText.position.y =
this._pixiRenderer.height - this._progressText.height - screenBorder;
this._pixiRenderer.render(this._loadingScreen);
this._pixiRenderer.render(this._loadingScreenContainer);
}
unload() {}
unload(): Promise<void> {
const totalElapsedTime = (performance.now() - this._startTimeInMs) / 1000;
const remainingTime =
this._loadingScreenData.minDuration - totalElapsedTime;
this.setPercent(100);
// Ensure we have shown the loading screen for at least minDuration.
if (remainingTime <= 0) {
this._state = LoadingScreenState.FINISHED;
return Promise.resolve();
}
return new Promise((resolve) =>
setTimeout(() => {
this._state = LoadingScreenState.FINISHED;
resolve();
}, remainingTime * 1000)
);
}
}
// Nothing to do
export const LoadingScreenRenderer = LoadingScreenPixiRenderer;
//Register the class to let the engine use it.
export const LoadingScreenRenderer = LoadingScreenPixiRenderer;
}

View File

@@ -5,6 +5,7 @@ namespace gdjs {
export const clampValue = function (value, min, max) {
return Math.max(min, Math.min(max, value));
};
export const clampKernelSize = function (value, min, max) {
const len = Math.round((max - min) / 2 + 1);
const arr = new Array(len);

View File

@@ -65,6 +65,9 @@ namespace gdjs {
const res = this._resources[i];
if (res.name === resourceName && res.kind === 'image') {
texture = PIXI.Texture.from(res.file);
if (!res.smoothed) {
texture.baseTexture.scaleMode = PIXI.SCALE_MODES.NEAREST;
}
break;
}
}

View File

@@ -26,12 +26,12 @@ namespace gdjs {
//Used to track if the window is displayed as fullscreen (see setFullscreen method).
_forceFullscreen: any;
_pixiRenderer: PIXI.Renderer | null = null;
_canvasWidth: float = 0;
// Current width of the canvas (might be scaled down/up compared to renderer)
_canvasWidth: float = 0;
// Current height of the canvas (might be scaled down/up compared to renderer)
_canvasHeight: float = 0;
// Current height of the canvas (might be scaled down/up compared to renderer)
_keepRatio: boolean = true;
_marginLeft: any;
_marginTop: any;
@@ -72,9 +72,9 @@ namespace gdjs {
this._pixiRenderer.view
);
this._pixiRenderer.view.style['position'] = 'absolute';
//Ensure that the canvas has the focus.
this._pixiRenderer.view.tabIndex = 1;
//Ensure that the canvas has the focus.
this._resizeCanvas();
// Handle scale mode

View File

@@ -431,6 +431,7 @@ namespace gdjs {
loadAllAssets(callback: () => void, progressCallback?: (float) => void) {
const loadingScreen = new gdjs.LoadingScreenRenderer(
this.getRenderer(),
this._imageManager,
this._data.properties.loadingScreen
);
const allAssetsTotal = this._data.resources.resources.length;
@@ -442,7 +443,7 @@ namespace gdjs {
this._imageManager.loadTextures(
function (count, total) {
const percent = Math.floor((count / allAssetsTotal) * 100);
loadingScreen.render(percent);
loadingScreen.setPercent(percent);
if (progressCallback) {
progressCallback(percent);
}
@@ -453,7 +454,7 @@ namespace gdjs {
const percent = Math.floor(
((texturesTotalCount + count) / allAssetsTotal) * 100
);
loadingScreen.render(percent);
loadingScreen.setPercent(percent);
if (progressCallback) {
progressCallback(percent);
}
@@ -466,7 +467,7 @@ namespace gdjs {
allAssetsTotal) *
100
);
loadingScreen.render(percent);
loadingScreen.setPercent(percent);
if (progressCallback) {
progressCallback(percent);
}
@@ -482,7 +483,7 @@ namespace gdjs {
allAssetsTotal) *
100
);
loadingScreen.render(percent);
loadingScreen.setPercent(percent);
if (progressCallback) {
progressCallback(percent);
}
@@ -499,11 +500,11 @@ namespace gdjs {
allAssetsTotal) *
100
);
loadingScreen.render(percent);
loadingScreen.setPercent(percent);
if (progressCallback) progressCallback(percent);
})
.then(() => loadingScreen.unload())
.then(() => {
loadingScreen.unload();
callback();
});
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -190,6 +190,18 @@ declare interface ExtensionProperty {
declare interface LoadingScreenData {
showGDevelopSplash: boolean;
backgroundImageResourceName: string;
backgroundColor: integer;
backgroundFadeInDuration: float;
minDuration: float;
logoAndProgressFadeInDuration: float;
logoAndProgressLogoFadeInDelay: float;
showProgressBar: boolean;
progressBarMinWidth: float;
progressBarMaxWidth: float;
progressBarWidthPercent: float;
progressBarHeight: float;
progressBarColor: integer;
}
declare interface ResourcesData {

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 B

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@ module.exports = function (config) {
client: {
mocha: {
reporter: 'html',
timeout: 5000, // Give a bit more time for CIs (the default 2s can be too low sometimes)
timeout: 10000, // Give a bit more time for CIs (the default 2s can be too low sometimes, as a real browser is involved).
},
},
files: [

View File

@@ -32,6 +32,18 @@ gdjs.getPixiRuntimeGameWithAssets = () => {
verticalSync: true,
loadingScreen: {
showGDevelopSplash: true,
backgroundImageResourceName: '',
backgroundColor: 0,
backgroundFadeInDuration: 0.2,
minDuration: 0,
logoAndProgressFadeInDuration: 0.2,
logoAndProgressLogoFadeInDelay: 0.2,
showProgressBar: true,
progressBarMinWidth: 40,
progressBarMaxWidth: 300,
progressBarWidthPercent: 40,
progressBarHeight: 20,
progressBarColor: 0xFFFFFF,
},
currentPlatform: '',
extensionProperties: [],

View File

@@ -293,8 +293,35 @@ interface PlatformSpecificAssets {
interface LoadingScreen {
void LoadingScreen();
void ShowGDevelopSplash(boolean show);
boolean IsGDevelopSplashShown();
void ShowGDevelopSplash(boolean show);
[Const, Ref] DOMString GetGDevelopLogoStyle();
[Ref] LoadingScreen SetGDevelopLogoStyle([Const] DOMString value);
[Const, Ref] DOMString GetBackgroundImageResourceName();
[Ref] LoadingScreen SetBackgroundImageResourceName([Const] DOMString value);
long GetBackgroundColor();
[Ref] LoadingScreen SetBackgroundColor(long value);
double GetBackgroundFadeInDuration();
[Ref] LoadingScreen SetBackgroundFadeInDuration(double value);
double GetMinDuration();
[Ref] LoadingScreen SetMinDuration(double value);
double GetLogoAndProgressFadeInDuration();
[Ref] LoadingScreen SetLogoAndProgressFadeInDuration(double value);
double GetLogoAndProgressLogoFadeInDelay();
[Ref] LoadingScreen SetLogoAndProgressLogoFadeInDelay(double value);
boolean GetShowProgressBar();
[Ref] LoadingScreen SetShowProgressBar(boolean value);
double GetProgressBarMaxWidth();
[Ref] LoadingScreen SetProgressBarMaxWidth(double value);
double GetProgressBarMinWidth();
[Ref] LoadingScreen SetProgressBarMinWidth(double value);
double GetProgressBarWidthPercent();
[Ref] LoadingScreen SetProgressBarWidthPercent(double value);
double GetProgressBarHeight();
[Ref] LoadingScreen SetProgressBarHeight(double value);
long GetProgressBarColor();
[Ref] LoadingScreen SetProgressBarColor(long value);
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Const, Ref] SerializerElement element);
@@ -580,7 +607,7 @@ interface Layout {
void SerializeLayersTo([Ref] SerializerElement element);
void UnserializeLayersFrom([Const, Ref] SerializerElement element);
[Ref] LayoutEditorCanvasOptions GetAssociatedSettings();
[Ref] EditorSettings GetAssociatedEditorSettings();
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
@@ -629,7 +656,7 @@ interface ExternalLayout {
[Const, Ref] DOMString GetAssociatedLayout();
[Ref] InitialInstancesContainer GetInitialInstances();
[Ref] LayoutEditorCanvasOptions GetAssociatedSettings();
[Ref] EditorSettings GetAssociatedEditorSettings();
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Const, Ref] SerializerElement element);
@@ -694,6 +721,11 @@ interface Layer {
[Ref] EffectsContainer GetEffects();
// Note that for now, cameras are not used in the editor (and not supported in the game engine).
// Just exposing the count to ensure we can test it.
unsigned long GetCameraCount();
void SetCameraCount(unsigned long cameraCount);
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Const, Ref] SerializerElement element);
};
@@ -1078,6 +1110,7 @@ interface InstructionMetadata {
[Const] DOMString type, [Const] DOMString supplementaryInformation);
[Ref] InstructionMetadata SetDefaultValue([Const] DOMString defaultValue);
[Ref] InstructionMetadata SetParameterLongDescription([Const] DOMString longDescription);
[Ref] InstructionMetadata SetParameterExtraInfo([Const] DOMString extraInfo);
[Ref] InstructionMetadata UseStandardOperatorParameters([Const] DOMString type);
[Ref] InstructionMetadata UseStandardRelationalOperatorParameters([Const] DOMString type);
@@ -1365,6 +1398,19 @@ interface BehaviorMetadata {
[Const] DOMString group,
[Const] DOMString icon);
[Ref] InstructionMetadata AddDuplicatedAction(
[Const] DOMString newActionName,
[Const] DOMString copiedActionName);
[Ref] InstructionMetadata AddDuplicatedCondition(
[Const] DOMString newConditionName,
[Const] DOMString copiedConditionName);
[Ref] ExpressionMetadata AddDuplicatedExpression(
[Const] DOMString newExpressionName,
[Const] DOMString copiedExpressionName);
[Ref] ExpressionMetadata AddDuplicatedStrExpression(
[Const] DOMString newExpressionName,
[Const] DOMString copiedExpressionName);
[Ref] BehaviorMetadata SetIncludeFile([Const] DOMString includeFile);
[Ref] BehaviorMetadata AddIncludeFile([Const] DOMString includeFile);
@@ -2268,8 +2314,8 @@ interface ResourcesInUseHelper {
};
ResourcesInUseHelper implements ArbitraryResourceWorker;
interface LayoutEditorCanvasOptions {
void LayoutEditorCanvasOptions();
interface EditorSettings {
void EditorSettings();
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Const, Ref] SerializerElement element);
@@ -2646,6 +2692,7 @@ interface PreviewExportOptions {
[Ref] PreviewExportOptions SetExternalLayoutName([Const] DOMString externalLayoutName);
[Ref] PreviewExportOptions SetIncludeFileHash([Const] DOMString includeFile, long hash);
[Ref] PreviewExportOptions SetProjectDataOnlyExport(boolean enable);
[Ref] PreviewExportOptions SetFullLoadingScreen(boolean enable);
[Ref] PreviewExportOptions SetNonRuntimeScriptsCacheBurst(unsigned long value);
};

View File

@@ -20,7 +20,7 @@
#include <GDCore/Extensions/Metadata/ParameterMetadataTools.h>
#include <GDCore/Extensions/Platform.h>
#include <GDCore/IDE/AbstractFileSystem.h>
#include <GDCore/IDE/Dialogs/LayoutEditorCanvas/LayoutEditorCanvasOptions.h>
#include <GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h>
#include <GDCore/IDE/Events/ArbitraryEventsWorker.h>
#include <GDCore/IDE/Events/EventsContextAnalyzer.h>
#include <GDCore/IDE/Events/EventsListUnfolder.h>

View File

@@ -235,15 +235,21 @@ describe('libGD.js', function () {
layer.setName('GUI');
layer.setVisibility(false);
layer.getEffects().insertNewEffect('MyEffect', 0);
layer.setCameraCount(1);
const element = new gd.SerializerElement();
layer.serializeTo(element);
layer2.unserializeFrom(element);
expect(layer2.getName()).toBe('GUI');
expect(layer2.getVisibility()).toBe(false);
expect(layer2.getEffects().getEffectsCount()).toBe(1);
expect(layer2.getEffects().getEffectAt(0).getName()).toBe('MyEffect');
for (let i = 0; i < 5; ++i) {
// Repeat multiple time to check idempotency.
layer2.unserializeFrom(element);
expect(layer2.getName()).toBe('GUI');
expect(layer2.getVisibility()).toBe(false);
expect(layer2.getEffects().getEffectsCount()).toBe(1);
expect(layer2.getEffects().getEffectAt(0).getName()).toBe('MyEffect');
expect(layer2.getCameraCount()).toBe(1);
}
layer.delete();
layer2.delete();
@@ -951,8 +957,8 @@ describe('libGD.js', function () {
});
});
describe('gd.BitmapFontResource', function() {
it('should have name and file', function() {
describe('gd.BitmapFontResource', function () {
it('should have name and file', function () {
const resource = new gd.BitmapFontResource();
resource.setName('MyBitmapFontResource');
resource.setFile('MyBitmapFontFile');
@@ -960,7 +966,7 @@ describe('libGD.js', function () {
expect(resource.getFile()).toBe('MyBitmapFontFile');
resource.delete();
});
it('can have metadata', function() {
it('can have metadata', function () {
const resource = new gd.BitmapFontResource();
expect(resource.getMetadata()).toBe('');
resource.setMetadata(JSON.stringify({ hello: 'world' }));
@@ -969,8 +975,8 @@ describe('libGD.js', function () {
});
});
describe('gd.VideoResource', function() {
it('should have name and file', function() {
describe('gd.VideoResource', function () {
it('should have name and file', function () {
const resource = new gd.VideoResource();
resource.setName('MyVideoResource');
resource.setFile('MyVideoFile');
@@ -1936,10 +1942,11 @@ describe('libGD.js', function () {
action.setParametersCount(2);
action.setParameter(0, 'MyCharacter');
var formattedTexts = gd.InstructionSentenceFormatter.get().getAsFormattedText(
action,
gd.MetadataProvider.getActionMetadata(gd.JsPlatform.get(), 'Delete')
);
var formattedTexts =
gd.InstructionSentenceFormatter.get().getAsFormattedText(
action,
gd.MetadataProvider.getActionMetadata(gd.JsPlatform.get(), 'Delete')
);
expect(formattedTexts.size()).toBe(2);
expect(formattedTexts.getString(0)).toBe('Delete ');
@@ -2414,7 +2421,8 @@ describe('libGD.js', function () {
resourcesMergingHelper.setBaseDirectory('/my/project/');
project.exposeResources(resourcesMergingHelper);
const oldAndNewFilenames = resourcesMergingHelper.getAllResourcesOldAndNewFilename();
const oldAndNewFilenames =
resourcesMergingHelper.getAllResourcesOldAndNewFilename();
expect(oldAndNewFilenames.get('/my/project/MyResource.png')).toBe(
'MyResource.png'
);
@@ -2900,11 +2908,12 @@ describe('libGD.js', function () {
layout
);
const expressionNode = parser.parseExpression(type, expression).get();
const completionDescriptions = gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
expressionNode,
// We're looking for completion for the character just before the caret.
Math.max(0, caretPosition - 1)
);
const completionDescriptions =
gd.ExpressionCompletionFinder.getCompletionDescriptionsFor(
expressionNode,
// We're looking for completion for the character just before the caret.
Math.max(0, caretPosition - 1)
);
for (let i = 0; i < completionDescriptions.size(); i++) {
const completionDescription = completionDescriptions.at(i);
@@ -3549,13 +3558,24 @@ describe('libGD.js', function () {
const instructionMetadata = new gd.InstructionMetadata();
expect(instructionMetadata.getParametersCount()).toBe(0);
instructionMetadata.addParameter('type', 'label', '', false);
instructionMetadata.addParameter(
'type',
'label',
'AdditionalStuffThatWillBeReplaced',
false
);
instructionMetadata.setParameterLongDescription('Blabla');
instructionMetadata.setParameterExtraInfo(
'AdditionalStuffLikeTypicallyAnObjectOrBehaviorType'
);
expect(instructionMetadata.getParametersCount()).toBe(1);
expect(instructionMetadata.getParameter(0).getType()).toBe('type');
expect(instructionMetadata.getParameter(0).getDescription()).toBe(
'label'
);
expect(instructionMetadata.getParameter(0).getExtraInfo()).toBe(
'AdditionalStuffLikeTypicallyAnObjectOrBehaviorType'
);
expect(instructionMetadata.getParameter(0).getLongDescription()).toBe(
'Blabla'
);
@@ -3592,4 +3612,26 @@ describe('libGD.js', function () {
);
});
});
describe('gd.EditorSettings', () => {
it('can store anything', () => {
const element = gd.Serializer.fromJSObject({
test: 1,
anything: {
canBeStored: true,
},
});
const editorSettings = new gd.EditorSettings();
editorSettings.unserializeFrom(element);
const element2 = new gd.SerializerElement();
editorSettings.serializeTo(element2);
expect(element2.getChild('test').getIntValue()).toBe(1);
expect(
element2.getChild('anything').getChild('canBeStored').getBoolValue()
).toBe(true);
});
});
});

View File

@@ -15,6 +15,10 @@ declare class gdBehaviorMetadata {
addStrExpression(name: string, fullname: string, description: string, group: string, smallicon: string): gdExpressionMetadata;
addExpressionAndCondition(type: string, name: string, fullname: string, description: string, sentenceName: string, group: string, icon: string): gdMultipleInstructionMetadata;
addExpressionAndConditionAndAction(type: string, name: string, fullname: string, description: string, sentenceName: string, group: string, icon: string): gdMultipleInstructionMetadata;
addDuplicatedAction(newActionName: string, copiedActionName: string): gdInstructionMetadata;
addDuplicatedCondition(newConditionName: string, copiedConditionName: string): gdInstructionMetadata;
addDuplicatedExpression(newExpressionName: string, copiedExpressionName: string): gdExpressionMetadata;
addDuplicatedStrExpression(newExpressionName: string, copiedExpressionName: string): gdExpressionMetadata;
setIncludeFile(includeFile: string): gdBehaviorMetadata;
addIncludeFile(includeFile: string): gdBehaviorMetadata;
setObjectType(objectType: string): gdBehaviorMetadata;

View File

@@ -1,5 +1,5 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdLayoutEditorCanvasOptions {
declare class gdEditorSettings {
constructor(): void;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(element: gdSerializerElement): void;

View File

@@ -6,7 +6,7 @@ declare class gdExternalLayout {
setAssociatedLayout(name: string): void;
getAssociatedLayout(): string;
getInitialInstances(): gdInitialInstancesContainer;
getAssociatedSettings(): gdLayoutEditorCanvasOptions;
getAssociatedEditorSettings(): gdEditorSettings;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(element: gdSerializerElement): void;
delete(): void;

View File

@@ -23,6 +23,7 @@ declare class gdInstructionMetadata {
addCodeOnlyParameter(type: string, supplementaryInformation: string): gdInstructionMetadata;
setDefaultValue(defaultValue: string): gdInstructionMetadata;
setParameterLongDescription(longDescription: string): gdInstructionMetadata;
setParameterExtraInfo(extraInfo: string): gdInstructionMetadata;
useStandardOperatorParameters(type: string): gdInstructionMetadata;
useStandardRelationalOperatorParameters(type: string): gdInstructionMetadata;
markAsSimple(): gdInstructionMetadata;

View File

@@ -14,6 +14,8 @@ declare class gdLayer {
getAmbientLightColorGreen(): number;
getAmbientLightColorBlue(): number;
getEffects(): gdEffectsContainer;
getCameraCount(): number;
setCameraCount(cameraCount: number): void;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(element: gdSerializerElement): void;
delete(): void;

View File

@@ -27,7 +27,7 @@ declare class gdLayout extends gdObjectsContainer {
moveLayer(oldIndex: number, newIndex: number): void;
serializeLayersTo(element: gdSerializerElement): void;
unserializeLayersFrom(element: gdSerializerElement): void;
getAssociatedSettings(): gdLayoutEditorCanvasOptions;
getAssociatedEditorSettings(): gdEditorSettings;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(project: gdProject, element: gdSerializerElement): void;
setStopSoundsOnStartup(enable: boolean): void;

View File

@@ -1,8 +1,34 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdLoadingScreen {
constructor(): void;
showGDevelopSplash(show: boolean): void;
isGDevelopSplashShown(): boolean;
showGDevelopSplash(show: boolean): void;
getGDevelopLogoStyle(): string;
setGDevelopLogoStyle(value: string): gdLoadingScreen;
getBackgroundImageResourceName(): string;
setBackgroundImageResourceName(value: string): gdLoadingScreen;
getBackgroundColor(): number;
setBackgroundColor(value: number): gdLoadingScreen;
getBackgroundFadeInDuration(): number;
setBackgroundFadeInDuration(value: number): gdLoadingScreen;
getMinDuration(): number;
setMinDuration(value: number): gdLoadingScreen;
getLogoAndProgressFadeInDuration(): number;
setLogoAndProgressFadeInDuration(value: number): gdLoadingScreen;
getLogoAndProgressLogoFadeInDelay(): number;
setLogoAndProgressLogoFadeInDelay(value: number): gdLoadingScreen;
getShowProgressBar(): boolean;
setShowProgressBar(value: boolean): gdLoadingScreen;
getProgressBarMaxWidth(): number;
setProgressBarMaxWidth(value: number): gdLoadingScreen;
getProgressBarMinWidth(): number;
setProgressBarMinWidth(value: number): gdLoadingScreen;
getProgressBarWidthPercent(): number;
setProgressBarWidthPercent(value: number): gdLoadingScreen;
getProgressBarHeight(): number;
setProgressBarHeight(value: number): gdLoadingScreen;
getProgressBarColor(): number;
setProgressBarColor(value: number): gdLoadingScreen;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(element: gdSerializerElement): void;
delete(): void;

View File

@@ -6,6 +6,7 @@ declare class gdPreviewExportOptions {
setExternalLayoutName(externalLayoutName: string): gdPreviewExportOptions;
setIncludeFileHash(includeFile: string, hash: number): gdPreviewExportOptions;
setProjectDataOnlyExport(enable: boolean): gdPreviewExportOptions;
setFullLoadingScreen(enable: boolean): gdPreviewExportOptions;
setNonRuntimeScriptsCacheBurst(value: number): gdPreviewExportOptions;
delete(): void;
ptr: number;

View File

@@ -176,7 +176,7 @@ declare class libGDevelop {
ResourcesRenamer: Class<gdResourcesRenamer>;
ProjectResourcesCopier: Class<gdProjectResourcesCopier>;
ResourcesInUseHelper: Class<gdResourcesInUseHelper>;
LayoutEditorCanvasOptions: Class<gdLayoutEditorCanvasOptions>;
EditorSettings: Class<gdEditorSettings>;
Point: Class<gdPoint>;
VectorPoint: Class<gdVectorPoint>;
Polygon2d: Class<gdPolygon2d>;

View File

@@ -13,7 +13,7 @@ GDevelop is a full-featured, open-source game development software, allowing to
| Create/improve an extension | Download [Node.js] and follow this [README](newIDE/README-extensions.md). |
| Help to translate GDevelop | Go on the [GDevelop project on Crowdin](https://crowdin.com/project/gdevelop). |
> Are you interested in contributing to GDevelop for the first time? Or want to participate in [Google Summer of Code 2021](https://summerofcode.withgoogle.com/organizations/5586892420022272/)? Take a look at the list of **[good first issues](https://github.com/4ian/GDevelop/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%91%8Cgood+first+issue%22)**, **[good first contributions](https://github.com/4ian/GDevelop/discussions/categories/good-first-contribution)** or the **["🏐 not too hard" cards](https://trello.com/b/qf0lM7k8/gdevelop-roadmap?menu=filter&filter=label:Not%20too%20hard%20%E2%9A%BD%EF%B8%8F)** on the Roadmap.
> Are you interested in contributing to GDevelop for the first time? Take a look at the list of **[good first issues](https://github.com/4ian/GDevelop/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%91%8Cgood+first+issue%22)**, **[good first contributions](https://github.com/4ian/GDevelop/discussions/categories/good-first-contribution)** or the **["🏐 not too hard" cards](https://trello.com/b/qf0lM7k8/gdevelop-roadmap?menu=filter&filter=label:Not%20too%20hard%20%E2%9A%BD%EF%B8%8F)** on the Roadmap.
## Overview of the architecture
@@ -30,7 +30,7 @@ To learn more about GDevelop Architecture, read the [architecture overview here]
Pre-generated documentation of the Core library, C++ and TypeScript game engines is [available here](https://docs.gdevelop-app.com).
Status of the tests and builds: [![Build status](https://circleci.com/gh/4ian/GDevelop.svg?style=shield)](https://app.circleci.com/pipelines/github/4ian/GDevelop) [![Quick tests status](https://semaphoreci.com/api/v1/4ian/gd/branches/master/shields_badge.svg)](https://semaphoreci.com/4ian/gd) [![All tests Status](https://www.travis-ci.com/4ian/GDevelop.svg?branch=master)](https://www.travis-ci.com/github/4ian/GDevelop) [![Windows Build status](https://ci.appveyor.com/api/projects/status/84uhtdox47xp422x/branch/master?svg=true)](https://ci.appveyor.com/project/4ian/gdevelop/branch/master) [![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation)
Status of the tests and builds: [![macOS and Linux build status](https://circleci.com/gh/4ian/GDevelop.svg?style=shield)](https://app.circleci.com/pipelines/github/4ian/GDevelop) [![Quick tests status](https://semaphoreci.com/api/v1/4ian/gd/branches/master/shields_badge.svg)](https://semaphoreci.com/4ian/gd) [![All tests status](https://www.travis-ci.com/4ian/GDevelop.svg?branch=master)](https://www.travis-ci.com/github/4ian/GDevelop) [![Windows Build status](https://ci.appveyor.com/api/projects/status/84uhtdox47xp422x/branch/master?svg=true)](https://ci.appveyor.com/project/4ian/gdevelop/branch/master) [![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation)
## Links
@@ -63,5 +63,3 @@ Status of the tests and builds: [![Build status](https://circleci.com/gh/4ian/GD
Games exported with GDevelop are based on the native and/or HTML5 game engines (see `Core`, `GDCpp` and `GDJS` folders): these engines are distributed under the MIT license so that you can **distribute, sell or do anything** with the games you created with GDevelop. In particular, you are not forced to make your game open source.
[node.js]: https://nodejs.org
[cmake]: http://www.cmake.org/
[ninja]: http://martine.github.io/ninja/

View File

@@ -1,13 +1,22 @@
# AppVeyor configuration to build GDevelop app running
# on the Electron runtime (newIDE/electron-app) for Windows.
# For macOS and Linux, see the config.yml file.
version: 1.0.{build}
pull_requests:
do_not_increment_build_number: true
image: Visual Studio 2019
clone_depth: 5
# Only build
branches:
only:
- master
- /experimental-build.*/
init:
- ps: ''
install:
- ps: Install-Product node 10
build_script:
- ps: Install-Product node 14
# Build GDevelop.js (and run tests to ensure it works)
- cmd: >-
cd GDevelop.js
@@ -29,21 +38,34 @@ build_script:
cd ..
# Build GDevelop IDE
- cmd: >-
cd newIDE\app
npm install
cd ..\..
cd GDJS
npm install
cd tests
cd ..\electron-app
npm install
cd ..\..
# Package the app for Windows.
build_script:
- cmd: >-
cd newIDE\electron-app
node --max-old-space-size=3072 scripts/build.js --win appx --publish=never
node scripts/build.js --skip-app-build --win nsis --publish=never
cd ..\..
# Clean dist folder to keep only installers/binaries.
- cmd: >-
DEL /F/Q/S newIDE\electron-app\dist\win-unpacked
# Run a few tests on Windows.
test_script:
- cmd: >-
cd GDevelop.js
@@ -57,5 +79,14 @@ test_script:
npm test
cd ..\..
on_finish:
- ps: ''
artifacts:
- path: newIDE\electron-app\dist
name: GDevelopWindows
# Upload artifacts (AWS) - configuration is stored on AppVeyor itself.
deploy:
- provider: Environment
name: Amazon S3 releases
- provider: Environment
name: Amazon S3 latest releases

View File

@@ -134,7 +134,7 @@ cd newIDE/web-app
yarn deploy # or npm run deploy
```
> Note: this will also upload the game engine (GDJS) and extension sources, needed by the IDE and purge the CloudFlare cache. If examples have been added/changed, be also sure to run `newIDE/web-app/scripts/deploy-examples-resources.js`.
> Note: this will also upload the game engine (GDJS) and extension sources, needed by the IDE and purge the CloudFlare cache.
### (Optional) Updating translations

View File

@@ -2,6 +2,7 @@
<head>
<title>GDevelop Jfxr Editor</title>
<link rel="stylesheet" type="text/css" href="gdide://external/utils/path-editor.css">
<link rel="stylesheet" type="text/css" href="jfxr-style.css">
</head>
@@ -11,4 +12,4 @@
<script type="module" src="gdide://external/jfxr/jfxr-main.js"></script>
</body>
</html>
</html>

View File

@@ -17,54 +17,3 @@ 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;
}

View File

@@ -2,6 +2,7 @@
<head>
<title>GDevelop Piskel Editor</title>
<link rel="stylesheet" type="text/css" href="gdide://external/utils/path-editor.css">
<link rel="stylesheet" type="text/css" href="piskel-style.css">
</head>
@@ -11,4 +12,4 @@
<script type="module" src="gdide://external/piskel/piskel-main.js"></script>
</body>
</html>
</html>

View File

@@ -16,54 +16,3 @@ 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: 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;
}

View File

@@ -0,0 +1,54 @@
/**
* Shared PathEditor styles
*/
#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;
}

View File

@@ -1,6 +1,7 @@
<html>
<head>
<title>GDevelop Dialogue Tree Editor (Yarn)</title>
<link rel="stylesheet" type="text/css" href="gdide://external/utils/path-editor.css">
<link rel="stylesheet" type="text/css" href="yarn-style.css" />
</head>

View File

@@ -17,31 +17,6 @@ body {
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;
@@ -63,7 +38,3 @@ body {
margin-top: 10px;
font-size: 15px;
}
#path-editor-header > span > * {
padding-left: 5px;
}

View File

@@ -1,6 +1,10 @@
// @flow
import * as React from 'react';
import { type AssetShortHeader } from '../Utils/GDevelopServices/Asset';
import {
type AssetShortHeader,
isPixelArt,
} from '../Utils/GDevelopServices/Asset';
import { getPixelatedImageRendering } from '../Utils/CssHelpers';
import ButtonBase from '@material-ui/core/ButtonBase';
import Text from '../UI/Text';
import { CorsAwareImage } from '../UI/CorsAwareImage';
@@ -19,6 +23,11 @@ const styles = {
verticalAlign: 'middle',
pointerEvents: 'none',
},
previewImagePixelated: {
width: '100%',
imageRendering: getPixelatedImageRendering(),
padding: 15,
},
icon: {
color: '#fff',
},
@@ -60,9 +69,12 @@ export const AssetCard = ({ assetShortHeader, onOpenDetails, size }: Props) => {
<CorsAwareImage
key={assetShortHeader.previewImageUrls[0]}
style={{
...styles.previewImage,
maxWidth: 128 - 2 * paddingSize,
maxHeight: 128 - 2 * paddingSize,
...styles.previewImage,
...(isPixelArt(assetShortHeader)
? styles.previewImagePixelated
: undefined),
}}
src={assetShortHeader.previewImageUrls[0]}
alt={assetShortHeader.name}

View File

@@ -10,7 +10,9 @@ import {
type Asset,
type Author,
getAsset,
isPixelArt,
} from '../Utils/GDevelopServices/Asset';
import { getPixelatedImageRendering } from '../Utils/CssHelpers';
import LeftLoader from '../UI/LeftLoader';
import PlaceholderLoader from '../UI/PlaceholderLoader';
import PlaceholderError from '../UI/PlaceholderError';
@@ -31,6 +33,11 @@ import Window from '../Utils/Window';
import CheckeredBackground from '../ResourcesList/CheckeredBackground';
const styles = {
previewImagePixelated: {
width: '100%',
imageRendering: getPixelatedImageRendering(),
padding: 15,
},
previewBackground: {
position: 'relative',
display: 'flex',
@@ -159,7 +166,12 @@ export const AssetDetails = ({
>
<CheckeredBackground />
<CorsAwareImage
style={styles.previewImage}
style={{
...styles.previewImage,
...(isPixelArt(assetShortHeader)
? styles.previewImagePixelated
: undefined),
}}
src={assetShortHeader.previewImageUrls[0]}
alt={assetShortHeader.name}
/>

View File

@@ -106,6 +106,7 @@ export const ExtensionStore = ({
getSearchItemUniqueId={getExtensionName}
renderSearchItem={(extensionShortHeader, onHeightComputed) => (
<ExtensionListItem
key={extensionShortHeader.name}
project={project}
onHeightComputed={onHeightComputed}
extensionShortHeader={extensionShortHeader}

View File

@@ -19,6 +19,7 @@ export type CommandName =
| 'OPEN_RECENT_PROJECT'
| 'OPEN_COMMAND_PALETTE'
| 'OPEN_PROJECT_PROPERTIES'
| 'OPEN_PROJECT_LOADING_SCREEN'
| 'OPEN_PROJECT_VARIABLES'
| 'OPEN_PLATFORM_SPECIFIC_ASSETS_DIALOG'
| 'OPEN_PROJECT_RESOURCES'
@@ -154,6 +155,10 @@ const commandsList: { [CommandName]: CommandMetadata } = {
area: 'PROJECT',
displayText: t`Open project properties`,
},
OPEN_PROJECT_LOADING_SCREEN: {
area: 'PROJECT',
displayText: t`Edit loading screen`,
},
OPEN_PROJECT_VARIABLES: {
area: 'PROJECT',
displayText: t`Edit global variables`,

View File

@@ -81,7 +81,6 @@ type Props = {|
|};
const iconSize = 24;
const minHeight = 400; // Avoid a super small list in empty scenes. 400 is enough to be displayed on an iPhone SE.
export default class InstructionOrObjectSelector extends React.PureComponent<
Props,
@@ -232,7 +231,7 @@ export default class InstructionOrObjectSelector extends React.PureComponent<
<div
style={{
backgroundColor: muiTheme.list.itemsBackgroundColor,
minHeight,
minHeight: 0,
...style,
}}
>
@@ -277,12 +276,7 @@ export default class InstructionOrObjectSelector extends React.PureComponent<
/>
</Tabs>
)}
<ScrollView
ref={
// $FlowFixMe - improper typing of ScrollView?
this._scrollView
}
>
<ScrollView ref={this._scrollView}>
{!isSearching && currentTab === 'objects' && (
<TagChips
tags={selectedObjectTags}

View File

@@ -15,7 +15,7 @@ import {
type ChooseResourceFunction,
} from '../../ResourcesList/ResourceSource.flow';
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
import { Line, Spacer } from '../../UI/Grid';
import { Column, Line, Spacer } from '../../UI/Grid';
import AlertMessage from '../../UI/AlertMessage';
import DismissableAlertMessage from '../../UI/DismissableAlertMessage';
import Window from '../../Utils/Window';
@@ -33,15 +33,10 @@ import Text from '../../UI/Text';
import { getInstructionMetadata } from './NewInstructionEditor';
import { ColumnStackLayout } from '../../UI/Layout';
import { setupInstructionParameters } from '../../InstructionOrExpression/SetupInstructionParameters';
import ScrollView from '../../UI/ScrollView';
const gd: libGDevelop = global.gd;
const styles = {
// When displaying parameters, take all the height:
container: {
display: 'flex',
flexDirection: 'column',
flex: 1,
},
// When displaying the empty message, center the message:
emptyContainer: {
display: 'flex',
@@ -50,7 +45,6 @@ const styles = {
},
parametersContainer: {
flex: 1,
overflowY: 'auto',
},
icon: {
width: 24,
@@ -245,150 +239,154 @@ export default class InstructionParametersEditor extends React.Component<
return (
<I18n>
{({ i18n }) => (
<div style={styles.container}>
<Line alignItems="flex-start">
<img
src={instructionMetadata.getIconFilename()}
alt=""
style={styles.icon}
/>
<Text style={styles.description}>
{instructionMetadata.getDescription()}
</Text>
{isAnEventFunctionMetadata(instructionMetadata) && (
<IconButton
onClick={() => {
this._openExtension(i18n);
}}
>
<OpenInNew />
</IconButton>
)}
</Line>
{instructionExtraInformation && (
<Line>
{instructionExtraInformation.identifier === undefined ? (
<AlertMessage kind={instructionExtraInformation.kind}>
{i18n._(instructionExtraInformation.message)}
</AlertMessage>
) : (
<DismissableAlertMessage
kind={instructionExtraInformation.kind}
identifier={instructionExtraInformation.identifier}
<ScrollView autoHideScrollbar>
<Column expand>
<Line alignItems="flex-start">
<img
src={instructionMetadata.getIconFilename()}
alt=""
style={styles.icon}
/>
<Text style={styles.description}>
{instructionMetadata.getDescription()}
</Text>
{isAnEventFunctionMetadata(instructionMetadata) && (
<IconButton
onClick={() => {
this._openExtension(i18n);
}}
>
{i18n._(instructionExtraInformation.message)}
</DismissableAlertMessage>
<OpenInNew />
</IconButton>
)}
</Line>
)}
{tutorialHints.length ? (
<Line>
<ColumnStackLayout expand>
{tutorialHints.map(tutorialHint => (
<DismissableTutorialMessage
key={tutorialHint.identifier}
tutorialHint={tutorialHint}
/>
))}
{instructionExtraInformation && (
<Line>
{instructionExtraInformation.identifier === undefined ? (
<AlertMessage kind={instructionExtraInformation.kind}>
{i18n._(instructionExtraInformation.message)}
</AlertMessage>
) : (
<DismissableAlertMessage
kind={instructionExtraInformation.kind}
identifier={instructionExtraInformation.identifier}
>
{i18n._(instructionExtraInformation.message)}
</DismissableAlertMessage>
)}
</Line>
)}
{tutorialHints.length ? (
<Line>
<ColumnStackLayout expand>
{tutorialHints.map(tutorialHint => (
<DismissableTutorialMessage
key={tutorialHint.identifier}
tutorialHint={tutorialHint}
/>
))}
</ColumnStackLayout>
</Line>
) : null}
<Spacer />
<div key={instructionType} style={styles.parametersContainer}>
<ColumnStackLayout noMargin>
{mapFor(0, instructionMetadata.getParametersCount(), i => {
const parameterMetadata = instructionMetadata.getParameter(
i
);
if (
!isParameterVisible(
parameterMetadata,
i,
objectParameterIndex
)
)
return null;
const parameterMetadataType = parameterMetadata.getType();
const ParameterComponent = ParameterRenderingService.getParameterComponent(
parameterMetadataType
);
// Track the field count on screen, to affect the ref to the
// first visible field.
const isFirstVisibleParameterField =
parameterFieldIndex === 0;
parameterFieldIndex++;
return (
<ParameterComponent
instructionMetadata={instructionMetadata}
instruction={instruction}
parameterMetadata={parameterMetadata}
parameterIndex={i}
value={instruction.getParameter(i)}
onChange={value => {
if (instruction.getParameter(i) !== value) {
instruction.setParameter(i, value);
this.setState({
isDirty: true,
});
}
}}
project={project}
scope={scope}
globalObjectsContainer={globalObjectsContainer}
objectsContainer={objectsContainer}
key={i}
parameterRenderingService={ParameterRenderingService}
resourceSources={this.props.resourceSources}
onChooseResource={this.props.onChooseResource}
resourceExternalEditors={
this.props.resourceExternalEditors
}
ref={field => {
if (isFirstVisibleParameterField) {
this._firstVisibleField = field;
}
}}
/>
);
})}
</ColumnStackLayout>
{this._getVisibleParametersCount(
instructionMetadata,
objectName
) === 0 && (
<EmptyMessage>
<Trans>There is nothing to configure.</Trans>
</EmptyMessage>
)}
{this.props.isCondition && (
<Toggle
label={<Trans>Invert condition</Trans>}
labelPosition="right"
toggled={instruction.isInverted()}
style={styles.invertToggle}
onToggle={(e, enabled) => {
instruction.setInverted(enabled);
this.forceUpdate();
}}
/>
)}
</div>
<Line>
{!noHelpButton && helpPage && (
<HelpButton
helpPagePath={instructionMetadata.getHelpPath()}
label={
this.props.isCondition ? (
<Trans>Help for this condition</Trans>
) : (
<Trans>Help for this action</Trans>
)
}
/>
)}
</Line>
) : null}
<Spacer />
<div key={instructionType} style={styles.parametersContainer}>
<ColumnStackLayout noMargin>
{mapFor(0, instructionMetadata.getParametersCount(), i => {
const parameterMetadata = instructionMetadata.getParameter(i);
if (
!isParameterVisible(
parameterMetadata,
i,
objectParameterIndex
)
)
return null;
const parameterMetadataType = parameterMetadata.getType();
const ParameterComponent = ParameterRenderingService.getParameterComponent(
parameterMetadataType
);
// Track the field count on screen, to affect the ref to the
// first visible field.
const isFirstVisibleParameterField =
parameterFieldIndex === 0;
parameterFieldIndex++;
return (
<ParameterComponent
instructionMetadata={instructionMetadata}
instruction={instruction}
parameterMetadata={parameterMetadata}
parameterIndex={i}
value={instruction.getParameter(i)}
onChange={value => {
if (instruction.getParameter(i) !== value) {
instruction.setParameter(i, value);
this.setState({
isDirty: true,
});
}
}}
project={project}
scope={scope}
globalObjectsContainer={globalObjectsContainer}
objectsContainer={objectsContainer}
key={i}
parameterRenderingService={ParameterRenderingService}
resourceSources={this.props.resourceSources}
onChooseResource={this.props.onChooseResource}
resourceExternalEditors={
this.props.resourceExternalEditors
}
ref={field => {
if (isFirstVisibleParameterField) {
this._firstVisibleField = field;
}
}}
/>
);
})}
</ColumnStackLayout>
{this._getVisibleParametersCount(
instructionMetadata,
objectName
) === 0 && (
<EmptyMessage>
<Trans>There is nothing to configure.</Trans>
</EmptyMessage>
)}
{this.props.isCondition && (
<Toggle
label={<Trans>Invert condition</Trans>}
labelPosition="right"
toggled={instruction.isInverted()}
style={styles.invertToggle}
onToggle={(e, enabled) => {
instruction.setInverted(enabled);
this.forceUpdate();
}}
/>
)}
</div>
<Line>
{!noHelpButton && helpPage && (
<HelpButton
helpPagePath={instructionMetadata.getHelpPath()}
label={
this.props.isCondition ? (
<Trans>Help for this condition</Trans>
) : (
<Trans>Help for this action</Trans>
)
}
/>
)}
</Line>
</div>
</Column>
</ScrollView>
)}
</I18n>
);

View File

@@ -13,10 +13,8 @@ import InstructionParametersEditor from './InstructionParametersEditor';
import InstructionOrObjectSelector, {
type TabName,
} from './InstructionOrObjectSelector';
import { Column } from '../../UI/Grid';
import InstructionOrExpressionSelector from './InstructionOrExpressionSelector';
import HelpButton from '../../UI/HelpButton';
import Background from '../../UI/Background';
import { type EventsScope } from '../../InstructionOrExpression/EventsScope.flow';
import { SelectColumns } from '../../UI/Reponsive/SelectColumns';
import {
@@ -172,51 +170,49 @@ export default function NewInstructionEditorDialog({
: undefined;
const renderInstructionOrObjectSelector = () => (
<Background noFullHeight key="instruction-or-object-selector">
<InstructionOrObjectSelector
style={styles.fullHeightSelector}
project={project}
scope={scope}
currentTab={currentInstructionOrObjectSelectorTab}
onChangeTab={setCurrentInstructionOrObjectSelectorTab}
globalObjectsContainer={globalObjectsContainer}
objectsContainer={objectsContainer}
isCondition={isCondition}
chosenInstructionType={!chosenObjectName ? instructionType : undefined}
onChooseInstruction={(instructionType: string) => {
chooseInstruction(instructionType);
setStep('parameters');
}}
chosenObjectName={chosenObjectName}
onChooseObject={(chosenObjectName: string) => {
chooseObject(chosenObjectName);
setStep('object-instructions');
}}
focusOnMount={!instructionType}
onSearchStartOrReset={forceUpdate}
/>
</Background>
<InstructionOrObjectSelector
key="instruction-or-object-selector"
style={styles.fullHeightSelector}
project={project}
scope={scope}
currentTab={currentInstructionOrObjectSelectorTab}
onChangeTab={setCurrentInstructionOrObjectSelectorTab}
globalObjectsContainer={globalObjectsContainer}
objectsContainer={objectsContainer}
isCondition={isCondition}
chosenInstructionType={!chosenObjectName ? instructionType : undefined}
onChooseInstruction={(instructionType: string) => {
chooseInstruction(instructionType);
setStep('parameters');
}}
chosenObjectName={chosenObjectName}
onChooseObject={(chosenObjectName: string) => {
chooseObject(chosenObjectName);
setStep('object-instructions');
}}
focusOnMount={!instructionType}
onSearchStartOrReset={forceUpdate}
/>
);
const renderParameters = () => (
<Column expand justifyContent="center" key="parameters">
<InstructionParametersEditor
project={project}
scope={scope}
globalObjectsContainer={globalObjectsContainer}
objectsContainer={objectsContainer}
objectName={chosenObjectName}
isCondition={isCondition}
instruction={instruction}
resourceSources={resourceSources}
onChooseResource={onChooseResource}
resourceExternalEditors={resourceExternalEditors}
openInstructionOrExpression={openInstructionOrExpression}
ref={instructionParametersEditor}
focusOnMount={!!instructionType}
noHelpButton
/>
</Column>
<InstructionParametersEditor
key="parameters"
project={project}
scope={scope}
globalObjectsContainer={globalObjectsContainer}
objectsContainer={objectsContainer}
objectName={chosenObjectName}
isCondition={isCondition}
instruction={instruction}
resourceSources={resourceSources}
onChooseResource={onChooseResource}
resourceExternalEditors={resourceExternalEditors}
openInstructionOrExpression={openInstructionOrExpression}
ref={instructionParametersEditor}
focusOnMount={!!instructionType}
noHelpButton
/>
);
const renderObjectInstructionSelector = () =>
@@ -292,6 +288,9 @@ export default function NewInstructionEditorDialog({
maxWidth={false}
noMargin
flexRowBody
fullHeight={
true /* Always use full height to avoid a very small dialog when there are not a lot of objects. */
}
>
<SelectColumns
columnsRenderer={{

View File

@@ -18,8 +18,6 @@ const styles = {
parametersEditor: {
flex: 2,
display: 'flex',
paddingLeft: 16,
paddingRight: 16,
zIndex: 1, // Put the Paper shadow on the type selector
},
};

View File

@@ -57,7 +57,11 @@ export const browserOnlineCordovaExportPipeline: ExportPipeline<
onlineBuildType: 'cordova-build',
packageNameWarningType: 'mobile',
getInitialExportState: () => ({ targets: ['androidApk'] }),
getInitialExportState: () => ({
targets: ['androidApk'],
keystore: 'new',
signingDialogOpen: false,
}),
canLaunchBuild: () => true,
@@ -157,7 +161,8 @@ export const browserOnlineCordovaExportPipeline: ExportPipeline<
getAuthorizationHeader,
profile.uid,
uploadBucketKey,
exportState.targets
exportState.targets,
exportState.keystore
);
},
};

View File

@@ -7,9 +7,14 @@ import { type TargetName } from '../../Utils/GDevelopServices/Build';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import Dialog from '../../UI/Dialog';
import FlatButton from '../../UI/FlatButton';
import HelpButton from '../../UI/HelpButton';
export type ExportState = {|
targets: Array<TargetName>,
keystore: 'old' | 'new',
signingDialogOpen: boolean,
|};
type HeaderProps = {|
@@ -40,6 +45,7 @@ export const SetupExportHeader = ({
onChange={event => {
const targetName = event.target.value;
updateExportState(prevExportState => ({
...prevExportState,
targets: [targetName],
}));
}}
@@ -61,6 +67,93 @@ export const SetupExportHeader = ({
}
/>
</RadioGroup>
<Line noMargin justifyContent="flex-end">
<FlatButton
label={<Trans>Signing options</Trans>}
onClick={() => {
updateExportState(prevExportState => ({
...prevExportState,
signingDialogOpen: true,
}));
}}
disabled={exportState.targets[0] !== 'androidAppBundle'}
/>
</Line>
{exportState.signingDialogOpen && (
<Dialog
actions={[
<FlatButton
key="close"
label={<Trans>Close</Trans>}
primary
keyboardFocused
onClick={() => {
updateExportState(prevExportState => ({
...prevExportState,
signingDialogOpen: false,
}));
}}
/>,
]}
secondaryActions={[
<HelpButton
helpPagePath="/publishing/android_and_ios/play-store/upgrading-from-apk-to-aab"
key="help"
/>,
]}
open
title={<Trans>Signing options</Trans>}
onRequestClose={() => {
updateExportState(prevExportState => ({
...prevExportState,
signingDialogOpen: false,
}));
}}
maxWidth="sm"
>
<Text>
<Trans>
Choose the upload key to use to identify your Android App Bundle.
In most cases you don't need to change this. Use the "Old upload
key" if you used to publish your game as an APK and you activated
Play App Signing before switching to Android App Bundle.
</Trans>
</Text>
<RadioGroup
name="signing-keystore"
value={exportState.keystore}
onChange={event => {
const keystore = event.target.value;
updateExportState(prevExportState => ({
...prevExportState,
keystore,
}));
}}
>
<FormControlLabel
value={'new'}
control={<Radio color="primary" />}
label={<Trans>New upload key (recommended)</Trans>}
/>
<FormControlLabel
value={'old'}
control={<Radio color="primary" />}
label={
<Trans>
Old upload key (only if you used to publish your game as an
APK and already activated Play App Signing)
</Trans>
}
/>
<FormControlLabel
value={'custom'}
control={<Radio color="primary" />}
label={<Trans>Custom upload key (not available yet)</Trans>}
disabled
/>
</RadioGroup>
</Dialog>
)}
</Column>
);
};

View File

@@ -51,7 +51,11 @@ export const localOnlineCordovaExportPipeline: ExportPipeline<
onlineBuildType: 'cordova-build',
packageNameWarningType: 'mobile',
getInitialExportState: () => ({ targets: ['androidApk'] }),
getInitialExportState: () => ({
targets: ['androidApk'],
keystore: 'new',
signingDialogOpen: false,
}),
canLaunchBuild: () => true,
@@ -144,7 +148,8 @@ export const localOnlineCordovaExportPipeline: ExportPipeline<
getAuthorizationHeader,
profile.uid,
uploadBucketKey,
exportState.targets
exportState.targets,
exportState.keystore
);
},
};

View File

@@ -217,6 +217,10 @@ export default class LocalPreviewLauncher extends React.Component<
shouldHotReload && previewOptions.projectDataOnlyExport
);
previewExportOptions.setFullLoadingScreen(
previewOptions.fullLoadingScreen
);
exporter.exportProjectForPixiPreview(previewExportOptions);
previewExportOptions.delete();
exporter.delete();

View File

@@ -8,6 +8,7 @@ export type PreviewOptions = {|
networkPreview: boolean,
hotReload: boolean,
projectDataOnlyExport: boolean,
fullLoadingScreen: boolean,
getIsMenuBarHiddenInPreview: () => boolean,
getIsAlwaysOnTopInPreview: () => boolean,
|};

View File

@@ -8,7 +8,7 @@ import RaisedButton from '../UI/RaisedButton';
import { ColumnStackLayout } from '../UI/Layout';
import Text from '../UI/Text';
import { type HotReloaderLog } from '../Export/PreviewLauncher.flow';
import PlayCircleFilledIcon from '@material-ui/icons/PlayCircleFilled';
import { NewPreviewIcon } from './HotReloadPreviewButton';
type Props = {|
logs: Array<HotReloaderLog>,
@@ -40,7 +40,7 @@ export default function HotReloadLogsDialog({
onClick={onClose}
/>,
<RaisedButton
icon={<PlayCircleFilledIcon />}
icon={<NewPreviewIcon />}
label={<Trans>Close and launch a new preview</Trans>}
key="new-preview"
primary

View File

@@ -8,15 +8,19 @@ import OfflineBoltIcon from '@material-ui/icons/OfflineBolt';
export type HotReloadPreviewButtonProps = {|
hasPreviewsRunning: boolean,
launchProjectDataOnlyPreview: () => void,
launchProjectWithLoadingScreenPreview: () => void,
|};
export const NewPreviewIcon = PlayCircleFilledIcon;
export const HotReloadPreviewIcon = OfflineBoltIcon;
export default function HotReloadPreviewButton({
launchProjectDataOnlyPreview,
hasPreviewsRunning,
}: HotReloadPreviewButtonProps) {
return (
<FlatButton
icon={hasPreviewsRunning ? <OfflineBoltIcon /> : <PlayCircleFilledIcon />}
icon={hasPreviewsRunning ? <HotReloadPreviewIcon /> : <NewPreviewIcon />}
label={
hasPreviewsRunning ? (
<Trans>Apply changes to preview</Trans>

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