Compare commits

...

98 Commits

Author SHA1 Message Date
Clément Pasteau
451d525b36 Fix objects owned by Multiplayer host not being properly migrated when host changes (#7078) 2024-10-17 15:35:41 +02:00
D8H
c755946d42 Add tutorial bubbles on actions replacing deprecated ones (#7077) 2024-10-17 15:12:24 +02:00
D8H
7a6b6fbf7f Update instance renderers when an event-based object is deleted, renamed or pasted (#7076) 2024-10-17 13:22:18 +02:00
github-actions[bot]
730c8283e5 Update translations [skip ci] (#7070)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-10-17 11:14:55 +02:00
D8H
7ea250706c Close custom object tabs when they are deleted from their extension (#7074) 2024-10-17 11:14:26 +02:00
Florian Rival
db7a108354 Reduce time before session analytics to improve bounce detection 2024-10-17 11:06:20 +02:00
AlexandreS
2e15d68bce Change max project warning copy and Add callout in Save to storage provider dialog (#7069)
Don't show in changelog
2024-10-17 11:05:33 +02:00
Florian Rival
f6c9e1408c Bump newIDE version 2024-10-17 00:37:34 +02:00
D8H
0b3d4d048a Fix autocompletion of layers and points for custom object children (#7071) 2024-10-17 00:35:54 +02:00
Clément Pasteau
5d625dd497 Always show instant-build page, even if not linked to a game (#7072) 2024-10-17 00:32:46 +02:00
D8H
079eca829a Fix a regression on the capability check in the editor (#7073)
Don't show in changelog
2024-10-17 00:18:42 +02:00
D8H
35b5f92c59 Fix object lists not always up-to-date in the extension events editor after adding an object in the custom object editor (#7068) 2024-10-16 17:33:35 +02:00
github-actions[bot]
70eb95b132 Update translations [skip ci] (#7064)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-10-16 15:15:57 +02:00
AlexandreS
7c7ee8b7fc Warn not enough cloud projects in quick customization flow (#7065) 2024-10-16 14:37:30 +02:00
Clément Pasteau
3556dd2e3c Improvements across the app (#7067)
* Shorter button label for github star
* More in app tutorials on the same row on mobile
* Smaller icon to get app on mobile
* Reduce spacing in Login & Signup dialog to see more
* Redirect store when in a dead end of game templates
* Improve guided lessons descriptions & re-order them
* Allow closing & deleting an opened cloud project
2024-10-16 14:26:26 +02:00
D8H
f74f77f66a Fix custom object editor not closed when the extension is renamed (#7066) 2024-10-16 13:11:25 +02:00
AlexandreS
712eb4b647 Avoid intermittent game crash, due to an audio issue, when resuming the game (when focused back again) (#7063) 2024-10-16 10:57:02 +02:00
D8H
c4474c766d Fix mouse and key parameters for event-functions (#7052) 2024-10-16 09:43:20 +02:00
github-actions[bot]
ad17a21973 Update translations [skip ci] (#7043)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-16 09:41:29 +02:00
D8H
5001411ccb Fix custom object loading when opening a project (#7060) 2024-10-16 09:38:16 +02:00
Florian Rival
05939f5c3e Fix extraction to custom object sometimes using an already used name (#7059) 2024-10-16 09:08:49 +02:00
Florian Rival
91978d4c6e Fix wait action in custom objects (#7056) 2024-10-15 17:15:07 +02:00
AlexandreS
d6433d89f0 Add icons to help understand to scroll during in app tutorial (#7057) 2024-10-15 14:43:37 +02:00
Clément Pasteau
e652ab9f5a Prevent players from claiming a leaderboard score as an anonymous user (#7055) 2024-10-15 14:36:40 +02:00
Florian Rival
e7decc7b92 Fix silent exceptions when resource loading finished after an instance was reset/removed (#7054)
Only show in developer changelog
2024-10-15 12:39:20 +02:00
AlexandreS
33dcc04112 Add support for free trial notification (#7053)
Don't show in changelog
2024-10-14 17:42:44 +02:00
Florian Rival
58ba2668c2 Add clang-tidy and builds with assertions/memory sanitizers for libGD.js (#7051)
Only show in developer changelog
2024-10-14 12:06:42 +02:00
Florian Rival
b34e802dcb Add a test with diagnostics that used to crash the app
Don't show in changelog
2024-10-14 09:39:07 +02:00
Florian Rival
31dac9cc93 Fix memory corruption/crashes when events in extensions had errors (#7050) 2024-10-13 22:44:36 +02:00
Florian Rival
ab97258832 Fix more tests and uninitialized variable (#7049)
Only show in developer changelog
2024-10-13 21:15:52 +02:00
Florian Rival
2ece223737 Fix wrong argument passed in a test (#7048) 2024-10-13 18:06:22 +02:00
Florian Rival
8342873b6e Fix missing or wrong arguments in GDevelop.js tests (#7047)
* These were "silently succeeding" but would create issues when Emscipten is run with SAFE_HEAP=1.

Only show in developer changelog
2024-10-13 17:39:54 +02:00
github-actions[bot]
0a85fd3814 Update translations [skip ci] (#7038)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-11 15:00:20 +02:00
Clément Pasteau
fe15b6d30b Update CSP (#7042)
Do not show in changelog
2024-10-11 14:59:59 +02:00
Florian Rival
f3c3559518 Fix warning 2024-10-11 14:56:36 +02:00
D8H
a403b1343b Fix extraction of 3D custom objects and prevent extracting 2D+3D custom objects (#7040) 2024-10-11 13:42:06 +02:00
D8H
83dba6c21e Allow to open the custom object editor from objects drop-down menu (#7039) 2024-10-11 12:45:23 +02:00
Florian Rival
7a4aea6557 Fix extension not regenerated after extracting instances to a custom object 2024-10-11 11:12:52 +02:00
AlexandreS
82a6abacb4 Suggest starter tilemaps when creating a tilemap from scratch (#7035) 2024-10-11 10:52:47 +02:00
AlexandreS
f60613cc64 Use user limits to determine if version history feature can be used (#7036)
Don't show in changelog
2024-10-11 10:47:32 +02:00
github-actions[bot]
274aedb3d9 Update translations [skip ci] (#7032)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-10-11 10:20:02 +02:00
D8H
efb31c3caf Fix text wrapping issue in the editor when used as a custom object child (#7034) 2024-10-11 09:05:36 +02:00
D8H
6781c0fd6e Add a drop-down menu action to extract instances as custom objects (#7030) 2024-10-10 20:13:42 +02:00
D8H
e9781133e1 Fix events refactoring when duplicating custom behaviors or custom objects (#7005) 2024-10-10 19:48:31 +02:00
Florian Rival
8972e0e3a6 Bump version (#7033) 2024-10-10 16:54:55 +02:00
Aurélien Vivet
4c76a5979b Add a new in-app tutorial explaining how to use a Tilemap (#6989) 2024-10-10 16:36:32 +02:00
D8H
484a0daec4 Show the tolerance in the sentence of "Is object turned toward" condition (#7022) 2024-10-10 16:25:38 +02:00
AlexandreS
de432c1bf2 Add a "Tilemap" filter in the Asset Store (#7031) 2024-10-10 16:15:18 +02:00
github-actions[bot]
8c7dcc1c23 Update translations [skip ci] (#7015)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-10-10 14:56:12 +02:00
AlexandreS
39648af248 Add margin between socials and donate link in profile (#7029)
Don't show in changelog
2024-10-10 10:21:15 +02:00
AlexandreS
5c7bbf5293 Add error callback to tilemap texture parsing (#7027)
To prevent crashes in the preview
2024-10-10 10:19:26 +02:00
Florian Rival
9d97b9d0eb Fix exception 't.bind is not a function' when a resource has an empty file (#7028) 2024-10-09 15:06:28 +02:00
AlexandreS
b39b12864f Add context menu options to duplicate instruction parameter or behavior property (#7026) 2024-10-08 15:50:28 +02:00
D8H
2c3dbbbbde Fix an exception when a touch is ended and then started in the same frame at runtime (#7025) 2024-10-08 11:45:40 +02:00
Clément Pasteau
72cc60bae9 Remove searchbar from Learn page. (#7024)
* As it does not return relevant results, it is thought best to remove it for now.
2024-10-07 18:16:16 +02:00
AlexandreS
c03e94849b Handle line breaks in font resource lines in resource store (#7023)
Don't show in changelog
2024-10-07 18:06:19 +02:00
AlexandreS
80daaf5e5b Fix Teach tab missing education resources button (#7021) 2024-10-07 17:25:04 +02:00
D8H
cb457cfd04 Fix the action to change the animation elapsed time for sprites and 3d models (#7020) 2024-10-07 17:18:40 +02:00
D8H
0a55aa631b Disable the "support us" dialog during an in-app tutorial (#7019) 2024-10-07 15:44:02 +02:00
AlexandreS
8e104f9ae4 Fix text input focus messing with the input manager (#7013) 2024-10-07 12:41:10 +02:00
D8H
a90b9a27e9 [Physics2] Avoid an exception when the object position is not finite (#7017) 2024-10-07 11:11:36 +02:00
D8H
6321e82f63 [Physics2] Forbid negative damping values (#7016) 2024-10-04 18:06:12 +02:00
AlexandreS
05fc63ab1b Fix ghost hitboxes on tilemap when tiles are flipped (#6990) 2024-10-04 17:25:34 +02:00
AlexandreS
85a6a21934 Do not display guided lesson tooltip if an error boundary is displayed (#7014)
Don't show in changelog
2024-10-04 17:23:28 +02:00
Clément Pasteau
a9741e7b42 Handle internal navigation for promotions (#7012)
Do not show in changelog
2024-10-04 17:22:56 +02:00
github-actions[bot]
23afe7b71c Update translations [skip ci] (#7011)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-10-04 17:22:22 +02:00
AlexandreS
85757e6d98 Rework asset store display when looking for a font or an audio (#7009)
The asset store is not available anymore when looking for other types of resource (image, 3D models, spine).
2024-10-04 15:56:50 +02:00
github-actions[bot]
524ca4dbb3 Update translations [skip ci] (#7002)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-10-03 17:40:13 +02:00
Aurélien Vivet
55cf710ef5 Add GitHub url to community tab (#7008) 2024-10-03 17:39:11 +02:00
Clément Pasteau
eee7e7f04c Remove most titles from home for a cleaner UI (#7010)
Do not show in changelog
2024-10-03 17:38:20 +02:00
Clément Pasteau
ecd984e08b Allow opening any homepage tab from url (#7007)
Do not show in changelog
2024-10-03 17:15:16 +02:00
D8H
d0a7fbbd02 Fix a regression on existing external tile map collision mask (#7004)
- Don't show in changelog
2024-10-02 14:44:07 +02:00
D8H
2a2c930b74 Add vertical alignment property for text object (#6975)
- Fix proportional anchors on custom object children
2024-10-02 11:17:48 +02:00
D8H
f6cb203029 Allow to filter external tile map collision per layer (#6998) 2024-10-02 10:14:52 +02:00
Florian Rival
fe743bbe57 Fix usage of libGD.js in Node 2024-09-30 19:06:24 +02:00
D8H
78c31408d4 Add a comment about anchor calculus (#6976)
- Don't show in changelog
2024-09-30 18:50:13 +02:00
github-actions[bot]
75a038344a Update translations [skip ci] (#7000)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-09-30 14:58:05 +02:00
Clément Pasteau
f0567b674c Fix example thumbnails (#7001) 2024-09-30 14:43:00 +02:00
Clément Pasteau
9b85f35856 Fix typo (#6999) 2024-09-30 08:40:38 +02:00
github-actions[bot]
f4568febf2 Update translations [skip ci] (#6995)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-09-30 08:14:52 +02:00
Clément Pasteau
de7f60b693 Bump to 5.4.214 (#6993) 2024-09-28 19:22:46 +02:00
Florian Rival
3a595b200e Update README [skip ci] [ci skip]
Don't show in changelog
2024-09-28 11:47:27 +02:00
AlexandreS
431b5929e8 Fine tune teach section display (#6994)
Don't show in changelog
2024-09-27 15:15:27 +02:00
github-actions[bot]
ec2e82cee5 Update translations [skip ci] (#6992)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-09-27 12:37:48 +02:00
Florian Rival
76517f1a2a Adapt margins in objects panel 2024-09-27 11:27:19 +02:00
Florian Rival
43f5bd1c0e Allow to edit object properties, behaviors, variables and effects directly from the properties panel (#6898)
* When an object is selected (or when "Edit object" button is clicked in the properties panel after choosing an instance), the properties panel will show the properties of the object.
* This allows for fast edition of most elements of an object: appearance, properties, but also behaviors, variables and effects. Most workflow and iterations on your game should be faster, from adapting the appearance of a text to tweaking behaviors and updating the preview to check the result in realtime.
2024-09-27 10:56:39 +02:00
github-actions[bot]
7198a22ae2 Update translations [skip ci] (#6964)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-09-26 10:43:31 +02:00
AlexandreS
46c52905f6 Add context menu to move Sprite object animation in list (#6987) 2024-09-25 17:28:58 +02:00
AlexandreS
40954bf497 Fix some crashes happening in preview (#6988) 2024-09-25 17:26:01 +02:00
Clément Pasteau
02c06ac6e7 Fix iOS automatic exports when using admob (#6986)
* internal ios deployment-target was conflicting between the plugins used, when admob was used in the project, preventing to build the app
2024-09-25 09:52:19 +02:00
AlexandreS
1fa3f59a77 Add possibility to paint tilemap with a rectangle selection from the tileset (#6977) 2024-09-23 17:37:20 +02:00
Clément Pasteau
cc371273ce Improve Quick Customization flow (#6978)
* Simplify the number of objects suggested for replacing
* Simplify the number of behavior properties suggested for tweaking
* New section to update the in-game title
* Simplify publication at the end of the flow
2024-09-23 15:07:31 +02:00
AlexandreS
364ec2ecfb Fix crashes in Preview due to ill-formed color values (#6980)
- Make color parsing from string more robust (issues when setting colors in Sprite, BBText, Particle emitter and effects with colors)
- Allow use of hex strings and shorthand hex strings in color fields
- Remove UI glitch when switching effect type and both effects have parameters with identical names
2024-09-23 09:21:37 +02:00
D8H
0d36a27b87 Fix anchor behavior when the object has a custom origin (#6970) 2024-09-18 13:54:10 +02:00
D8H
6d597a430b Always show custom objects in the extension editor (#6974) 2024-09-18 13:21:04 +02:00
Clément Pasteau
27b71b08e5 Fix instance selection rectangle not being flipped properly (#6971) 2024-09-18 09:51:08 +02:00
AlexandreS
82158f7073 Fix DismissableTutorialMessage stories typing (#6972)
Do not show in changelog
2024-09-18 09:50:35 +02:00
AlexandreS
8ba352d4ba Fix min width on right container (#6969)
Don't show in changelog
2024-09-17 17:35:48 +02:00
508 changed files with 18882 additions and 7675 deletions

View File

@@ -176,6 +176,7 @@ jobs:
# Build the WebAssembly library only (so that it's cached on a S3 and easy to re-use).
build-gdevelop_js-wasm-only:
resource_class: medium+ # Compilation time decrease linearly with the number of CPUs, but not linking (so "large" does not speedup total build time).
docker:
- image: cimg/node:16.13
@@ -232,10 +233,83 @@ jobs:
name: Deploy to S3 (latest)
command: aws s3 sync Binaries/embuild/GDevelop.js s3://gdevelop-gdevelop.js/$(git rev-parse --abbrev-ref HEAD)/latest/
# Build the WebAssembly library with clang-tidy and memory sanitizers.
build-gdevelop_js-debug-sanitizers-and-extra-checks:
resource_class: xlarge # Total time decrease linearly with the number of CPUs.
docker:
- image: cimg/node:16.13
working_directory: ~/GDevelop
steps:
- checkout
- aws-cli/setup
# System dependencies (for Emscripten)
- run:
name: Install dependencies for Emscripten
command: sudo apt-get update && sudo apt install cmake
- run:
name: Install dependencies for clang-tidy v19
command: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 19 && sudo apt install clang-tidy-19
- run:
name: Install Python3 dependencies for Emscripten
command: sudo apt install python-is-python3 python3-distutils -y
- run:
name: Install Emscripten (for GDevelop.js)
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
# GDevelop.js dependencies
- restore_cache:
keys:
- gdevelop.js-linux-nodejs-dependencies-{{ checksum "GDevelop.js/package-lock.json" }}
# fallback to using the latest cache if no exact match is found
- gdevelop.js-linux-nodejs-dependencies-
- run:
name: Install GDevelop.js dependencies and build it
command: cd GDevelop.js && npm install && cd ..
# Build GDevelop.js
- run:
name: Build GDevelop.js ('debug-sanitizers' variant)
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build -- --variant=debug-sanitizers
- run:
name: Run clang-tidy
command: cd GDevelop.js && npm run lint
- run:
name: Run tests
command: cd GDevelop.js && npm run test -- --maxWorkers=4
# Upload artifacts (CircleCI)
- store_artifacts:
path: Binaries/embuild/GDevelop.js
# Upload artifacts (AWS)
- run:
name: Deploy to S3 (specific commit)
command: aws s3 sync Binaries/embuild/GDevelop.js s3://gdevelop-gdevelop.js/$(git rev-parse --abbrev-ref HEAD)/variant/debug-sanitizers/commit/$(git rev-parse HEAD)/
workflows:
builds:
gdevelop_js-wasm:
jobs:
- build-gdevelop_js-wasm-only
gdevelop_js-wasm-extra-checks:
jobs:
- build-gdevelop_js-debug-sanitizers-and-extra-checks:
# Extra checks are resource intensive so don't all run them.
filters:
branches:
only:
- master
- /experimental-build.*/
builds:
jobs:
- build-macos:
filters:
branches:

4
.clang-tidy Normal file
View File

@@ -0,0 +1,4 @@
Checks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,-cppcoreguidelines-explicit-virtual-functions,-cppcoreguidelines-avoid-const-or-ref-data-members,-cppcoreguidelines-special-member-functions,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-non-private-member-variables-in-classes,-cppcoreguidelines-owning-memory,-cppcoreguidelines-virtual-class-destructor,-clang-analyzer-optin.performance.Padding,-cppcoreguidelines-narrowing-conversions'
WarningsAsErrors: 'cppcoreguidelines-pro-type-member-init, clang-analyzer-optin.cplusplus.UninitializedObject'
HeaderFilterRegex: '.*'
FormatStyle: none

View File

@@ -114,7 +114,8 @@
"__bits": "cpp",
"__verbose_abort": "cpp",
"variant": "cpp",
"charconv": "cpp"
"charconv": "cpp",
"execution": "cpp"
},
"files.exclude": {
"Binaries/*build*": true,

View File

@@ -69,12 +69,18 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# uninitialized variables or other hard to debug bugs.
add_compile_options(
-Wall
-Wextra
-Wuninitialized
-Wconditional-uninitialized
-Wno-unknown-warning-option
-Wno-reorder-ctor
-Wno-reorder
-Wno-unused-parameter
-Wno-pessimizing-move
-Wno-unused-variable
-Wno-unused-variable # Not a good style, but not a risk
-Wno-unused-private-field
-Wno-ignored-qualifiers # Not a risk
-Wno-sign-compare # Not a big risk
# Make as much warnings considered as errors as possible (only one for now).
-Werror=return-stack-address

View File

@@ -72,8 +72,6 @@ class GD_CORE_API WhileEvent : public gd::BaseEvent {
///< de/activate infinite loop warning when the
///< user create the event
mutable unsigned int whileConditionsHeight;
int GetConditionsHeight() const;
int GetActionsHeight() const;
int GetWhileConditionsHeight() const;

View File

@@ -14,10 +14,10 @@
namespace gd {
/**
* \brief
* \brief
*/
class GD_CORE_API ProjectDiagnostic {
public:
public:
enum ErrorType {
UndeclaredVariable,
MissingBehavior,
@@ -25,12 +25,17 @@ public:
MismatchedObjectType,
};
ProjectDiagnostic(ErrorType type_, const gd::String &message_,
ProjectDiagnostic(ErrorType type_,
const gd::String &message_,
const gd::String &actualValue_,
const gd::String &expectedValue_, const gd::String &objectName_ = "")
: type(type_), message(message_), actualValue(actualValue_), expectedValue(expectedValue_),
objectName(objectName_){};
virtual ~ProjectDiagnostic(){};
const gd::String &expectedValue_,
const gd::String &objectName_ = "")
: type(type_),
message(message_),
actualValue(actualValue_),
expectedValue(expectedValue_),
objectName(objectName_) {};
virtual ~ProjectDiagnostic() {};
ErrorType GetType() const { return type; };
const gd::String &GetMessage() const { return message; }
@@ -38,7 +43,7 @@ public:
const gd::String &GetActualValue() const { return actualValue; }
const gd::String &GetExpectedValue() const { return expectedValue; }
private:
private:
ErrorType type;
gd::String message;
gd::String objectName;
@@ -47,12 +52,12 @@ private:
};
/**
* \brief
* \brief
*/
class GD_CORE_API DiagnosticReport {
public:
DiagnosticReport(){};
virtual ~DiagnosticReport(){};
public:
DiagnosticReport() {};
virtual ~DiagnosticReport() {};
void Add(const gd::ProjectDiagnostic &projectDiagnostic) {
projectDiagnostics.push_back(
@@ -67,32 +72,39 @@ public:
const gd::String &GetSceneName() const { return sceneName; }
void SetSceneName(const gd::String &sceneName_) {
sceneName = sceneName_;
void SetSceneName(const gd::String &sceneName_) { sceneName = sceneName_; }
void LogAllDiagnostics() {
for (auto &diagnostic : projectDiagnostics) {
std::cout << diagnostic->GetMessage()
<< "(object: " << diagnostic->GetObjectName()
<< ", actual value: " << diagnostic->GetActualValue()
<< ", expected value: " << diagnostic->GetExpectedValue() << ")"
<< std::endl;
}
}
private:
private:
std::vector<std::unique_ptr<gd::ProjectDiagnostic>> projectDiagnostics;
gd::String sceneName;
};
/**
* \brief
* \brief
*/
class GD_CORE_API WholeProjectDiagnosticReport {
public:
WholeProjectDiagnosticReport(){};
virtual ~WholeProjectDiagnosticReport(){};
public:
WholeProjectDiagnosticReport() {};
virtual ~WholeProjectDiagnosticReport() {};
const DiagnosticReport &Get(std::size_t index) const {
return *diagnosticReports[index].get();
};
void Clear() {
diagnosticReports.clear();
};
void Clear() { diagnosticReports.clear(); };
DiagnosticReport& AddNewDiagnosticReportForScene(const gd::String &sceneName) {
DiagnosticReport &AddNewDiagnosticReportForScene(
const gd::String &sceneName) {
auto diagnosticReport = gd::make_unique<gd::DiagnosticReport>();
diagnosticReport->SetSceneName(sceneName);
diagnosticReports.push_back(std::move(diagnosticReport));
@@ -102,7 +114,7 @@ public:
std::size_t Count() const { return diagnosticReports.size(); };
bool HasAnyIssue() {
for (auto& diagnosticReport : diagnosticReports) {
for (auto &diagnosticReport : diagnosticReports) {
if (diagnosticReport->Count() > 0) {
return true;
}
@@ -110,8 +122,8 @@ public:
return false;
}
private:
private:
std::vector<std::unique_ptr<gd::DiagnosticReport>> diagnosticReports;
};
} // namespace gd
} // namespace gd

View File

@@ -345,14 +345,14 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
objectInParameter, "");
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Unknown object - skipped. */";
} else if (!expectedObjectType.empty() &&
actualObjectType != expectedObjectType) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
actualObjectType, expectedObjectType, objectInParameter);
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Mismatched object type - skipped. */";
}
}
@@ -509,7 +509,7 @@ void EventsCodeGenerator::CheckBehaviorParameters(
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MissingBehavior, "",
actualBehaviorType, expectedBehaviorType, lastObjectName);
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
}
}
});
@@ -567,14 +567,14 @@ gd::String EventsCodeGenerator::GenerateActionCode(
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
objectInParameter, "");
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Unknown object - skipped. */";
} else if (!expectedObjectType.empty() &&
actualObjectType != expectedObjectType) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
actualObjectType, expectedObjectType, objectInParameter);
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Mismatched object type - skipped. */";
}
}

View File

@@ -36,8 +36,8 @@ struct GD_CORE_API ExpressionParserLocation {
private:
bool isValid;
size_t startPosition;
size_t endPosition;
size_t startPosition = 0;
size_t endPosition = 0;
};
/**

View File

@@ -1768,6 +1768,22 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression",
_("Angle of tolerance, in degrees (0: minimum tolerance)"))
.AddCodeOnlyParameter("conditionInverted", "")
.SetHidden()
.MarkAsAdvanced();
extension
.AddCondition("IsTurnedTowardObject",
_("An object is turned toward another"),
_("Check if an object is turned toward another"),
_("_PARAM0_ is turned toward _PARAM1_ ± _PARAM2_°"),
_("Angle"),
"res/conditions/estTourne24.png",
"res/conditions/estTourne.png")
.AddParameter("objectList", _("Name of the object"))
.AddParameter("objectList", _("Name of the second object"))
.AddParameter("expression",
_("Angle of tolerance, in degrees (0: minimum tolerance)"))
.AddCodeOnlyParameter("conditionInverted", "")
.MarkAsAdvanced();
extension

View File

@@ -338,6 +338,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
.SetDefaultValue("0");
// Deprecated
extension
.AddCondition(
"PopStartedTouch",
@@ -354,6 +355,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
// Deprecated
extension
.AddCondition(
"PopEndedTouch",
@@ -370,6 +372,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCodeOnlyParameter("currentScene", "")
.SetHidden();
// Deprecated
extension
.AddCondition(
"HasAnyTouchStarted",

View File

@@ -33,6 +33,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
"most elements of a game."),
"CppPlatform/Extensions/spriteicon.png")
.SetCategoryFullName(_("General"))
.SetOpenFullEditorLabel(_("Edit animations"))
.AddDefaultBehavior("EffectCapability::EffectBehavior")
.AddDefaultBehavior("ResizableCapability::ResizableBehavior")
.AddDefaultBehavior("ScalableCapability::ScalableBehavior")

View File

@@ -47,19 +47,11 @@ void SpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
std::map<gd::String, gd::PropertyDescriptor> SpriteObject::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[_("Animate even if hidden or far from the screen")]
.SetValue(updateIfNotVisible ? "true" : "false")
.SetType("Boolean");
properties["PLEASE_ALSO_SHOW_EDIT_BUTTON_THANKS"].SetValue("");
return properties;
}
bool SpriteObject::UpdateProperty(const gd::String& name,
const gd::String& value) {
if (name == _("Animate even if hidden or far from the screen"))
updateIfNotVisible = value == "1";
return true;
}

View File

@@ -37,8 +37,7 @@ BehaviorMetadata::BehaviorMetadata(
className(className_),
iconFilename(icon24x24),
instance(instance_),
sharedDatasInstance(sharedDatasInstance_),
quickCustomizationVisibility(QuickCustomization::Visibility::Default) {
sharedDatasInstance(sharedDatasInstance_) {
SetFullName(gd::String(fullname_));
SetDescription(gd::String(description_));
SetDefaultName(gd::String(defaultName_));

View File

@@ -307,6 +307,15 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
return *this;
}
BehaviorMetadata &SetOpenFullEditorLabel(const gd::String& label) {
openFullEditorLabel = label;
return *this;
}
const gd::String& GetOpenFullEditorLabel() const {
return openFullEditorLabel;
}
/**
* \brief Return the associated gd::Behavior, handling behavior contents.
*
@@ -384,7 +393,8 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
mutable std::vector<gd::String> requiredBehaviors;
bool isPrivate = false;
bool isHidden = false;
QuickCustomization::Visibility quickCustomizationVisibility;
gd::String openFullEditorLabel;
QuickCustomization::Visibility quickCustomizationVisibility = QuickCustomization::Visibility::Default;
// TODO: Nitpicking: convert these to std::unique_ptr to clarify ownership.
std::shared_ptr<gd::Behavior> instance;

View File

@@ -185,10 +185,10 @@ class GD_CORE_API EffectMetadata {
gd::String fullname;
gd::String description;
std::vector<gd::String> includeFiles;
bool isMarkedAsNotWorkingForObjects;
bool isMarkedAsOnlyWorkingFor2D;
bool isMarkedAsOnlyWorkingFor3D;
bool isMarkedAsUnique;
bool isMarkedAsNotWorkingForObjects = false;
bool isMarkedAsOnlyWorkingFor2D = false;
bool isMarkedAsOnlyWorkingFor3D = false;
bool isMarkedAsUnique = false;
std::map<gd::String, gd::PropertyDescriptor> properties;
};

View File

@@ -19,7 +19,8 @@ EventMetadata::EventMetadata(const gd::String &name_,
: fullname(fullname_),
description(description_),
group(group_),
instance(instance_) {
instance(instance_),
hasCustomCodeGenerator(false) {
ClearCodeGenerationAndPreprocessing();
if (instance) instance->SetType(name_);
}

View File

@@ -83,7 +83,7 @@ class GD_CORE_API EventMetadata {
gd::String group;
std::shared_ptr<gd::BaseEvent> instance;
bool hasCustomCodeGenerator;
bool hasCustomCodeGenerator = false;
std::function<gd::String(gd::BaseEvent& event,
gd::EventsCodeGenerator& codeGenerator,
gd::EventsCodeGenerationContext& context)>

View File

@@ -288,8 +288,8 @@ class GD_CORE_API MetadataProvider {
static EffectMetadata badEffectMetadata;
static gd::InstructionMetadata badInstructionMetadata;
static gd::ExpressionMetadata badExpressionMetadata;
int useless; // Useless member to avoid emscripten "must have a positive
// integer typeid pointer" runtime error.
int useless = 0; // Useless member to avoid emscripten "must have a positive
// integer typeid pointer" runtime error.
};
} // namespace gd

View File

@@ -323,6 +323,15 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
*/
bool IsRenderedIn3D() const { return isRenderedIn3D; }
ObjectMetadata &SetOpenFullEditorLabel(const gd::String& label) {
openFullEditorLabel = label;
return *this;
}
const gd::String& GetOpenFullEditorLabel() const {
return openFullEditorLabel;
}
std::map<gd::String, gd::InstructionMetadata> conditionsInfos;
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
@@ -344,6 +353,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
std::set<gd::String> defaultBehaviorTypes;
bool hidden = false;
bool isRenderedIn3D = false;
gd::String openFullEditorLabel;
std::shared_ptr<gd::ObjectConfiguration>
blueprintObject; ///< The "blueprint" object to be copied when a new

View File

@@ -53,20 +53,23 @@ const gd::String &ValueTypeMetadata::GetExpressionPrimitiveValueType(
const gd::String &
ValueTypeMetadata::GetPrimitiveValueType(const gd::String &parameterType) {
if (parameterType == "variable" ||
gd::ValueTypeMetadata::IsTypeExpression("variable", parameterType)) {
return ValueTypeMetadata::variableType;
if (parameterType == "number" ||
gd::ValueTypeMetadata::IsTypeValue("number", parameterType)) {
return ValueTypeMetadata::numberType;
}
if (parameterType == "boolean" || parameterType == "yesorno" ||
parameterType == "trueorfalse") {
return ValueTypeMetadata::booleanType;
}
// These 2 types are not strings from the code generator point of view,
// but it is for event-based extensions.
if (parameterType == "key" || parameterType == "mouse") {
if (parameterType == "string" ||
gd::ValueTypeMetadata::IsTypeValue("string", parameterType)) {
return ValueTypeMetadata::stringType;
}
return GetExpressionPrimitiveValueType(parameterType);
if (parameterType == "variable" ||
gd::ValueTypeMetadata::IsTypeValue("variable", parameterType)) {
return ValueTypeMetadata::variableType;
}
if (parameterType == "boolean" ||
gd::ValueTypeMetadata::IsTypeValue("boolean", parameterType)) {
return ValueTypeMetadata::booleanType;
}
return parameterType;
}
const gd::String ValueTypeMetadata::numberValueType = "number";

View File

@@ -111,21 +111,21 @@ class GD_CORE_API ValueTypeMetadata {
* given type.
*/
bool IsNumber() const {
return gd::ValueTypeMetadata::IsTypeExpression("number", name);
return gd::ValueTypeMetadata::IsTypeValue("number", name);
}
/**
* \brief Return true if the type is a string.
*/
bool IsString() const {
return gd::ValueTypeMetadata::IsTypeExpression("string", name);
return gd::ValueTypeMetadata::IsTypeValue("string", name);
}
/**
* \brief Return true if the type is a boolean.
*/
bool IsBoolean() const {
return gd::ValueTypeMetadata::IsTypeExpression("boolean", name);
return gd::ValueTypeMetadata::IsTypeValue("boolean", name);
}
/**
@@ -135,7 +135,7 @@ class GD_CORE_API ValueTypeMetadata {
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
bool IsVariable() const {
return gd::ValueTypeMetadata::IsTypeExpression("variable", name);
return gd::ValueTypeMetadata::GetPrimitiveValueType(name) == "variable";
}
/**
@@ -175,7 +175,9 @@ class GD_CORE_API ValueTypeMetadata {
}
/**
* \brief Return true if the type is an expression of the given type.
* \brief Return true if the type is an expression of the given type from the
* caller point of view.
*
* \note If you are adding a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
@@ -186,6 +188,7 @@ class GD_CORE_API ValueTypeMetadata {
return parameterType == "number" || parameterType == "expression" ||
parameterType == "camera" || parameterType == "forceMultiplier";
} else if (type == "string") {
// "key" and "mouse" are not mapped her, see GetPrimitiveValueType.
return parameterType == "string" || parameterType == "layer" ||
parameterType == "color" || parameterType == "file" ||
parameterType == "stringWithSelector" ||
@@ -227,6 +230,26 @@ class GD_CORE_API ValueTypeMetadata {
return false;
}
/**
* \brief Return true if the type is a value of the given primitive type from
* the function events point of view
*/
static bool IsTypeValue(const gd::String &type,
const gd::String &parameterType) {
if (gd::ValueTypeMetadata::IsTypeExpression(type, parameterType)) {
return true;
}
// These 2 parameter types are not strings from the outside of a function as
// the generator add quote around a text, but from the events inside of the
// function the parameter is a string.
//
// See EventsCodeGenerator::GenerateParameterCodes
if (type == "string") {
return parameterType == "key" || parameterType == "mouse";
}
return false;
}
/**
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or

View File

@@ -808,6 +808,24 @@ gd::String PlatformExtension::GetObjectFullType(const gd::String &extensionName,
return extensionName + separator + objectName;
}
gd::String PlatformExtension::GetExtensionFromFullObjectType(const gd::String& type) {
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
if (separatorIndex == std::string::npos) {
return "";
}
return type.substr(0, separatorIndex);
}
gd::String PlatformExtension::GetObjectNameFromFullObjectType(const gd::String& type) {
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
if (separatorIndex == std::string::npos) {
return "";
}
return type.substr(separatorIndex + 2);
}
PlatformExtension::PlatformExtension()
: deprecated(false), category(_("General")) {}

View File

@@ -40,8 +40,7 @@ class Object;
class ObjectConfiguration;
} // namespace gd
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()>
CreateFunPtr;
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()> CreateFunPtr;
namespace gd {
@@ -51,25 +50,25 @@ namespace gd {
*/
class GD_CORE_API CompilationInfo {
public:
CompilationInfo() : informationCompleted(false){};
virtual ~CompilationInfo(){};
CompilationInfo() {};
virtual ~CompilationInfo() {};
bool informationCompleted;
bool informationCompleted = false;
bool runtimeOnly; ///< True if the extension was compiled for a runtime use
///< only
bool runtimeOnly = false; ///< True if the extension was compiled for a
///< runtime use only
#if defined(__GNUC__)
int gccMajorVersion;
int gccMinorVersion;
int gccPatchLevel;
int gccMajorVersion = 0;
int gccMinorVersion = 0;
int gccPatchLevel = 0;
#endif
int sfmlMajorVersion;
int sfmlMinorVersion;
int sfmlMajorVersion = 0;
int sfmlMinorVersion = 0;
gd::String gdCoreVersion;
int sizeOfpInt;
int sizeOfpInt = 0;
};
struct GD_CORE_API DuplicatedInstructionOptions {
@@ -239,11 +238,12 @@ class GD_CORE_API PlatformExtension {
* \param instance The "blueprint" object to be copied when a new object is
asked for.
*/
gd::ObjectMetadata& AddObject(const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_,
std::shared_ptr<gd::ObjectConfiguration> instance);
gd::ObjectMetadata& AddObject(
const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_,
std::shared_ptr<gd::ObjectConfiguration> instance);
/**
* \brief Declare a new events based object as being part of the extension.
@@ -253,11 +253,10 @@ class GD_CORE_API PlatformExtension {
* \param description The user friendly description of the object
* \param icon The icon of the object.
*/
gd::ObjectMetadata& AddEventsBasedObject(
const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_);
gd::ObjectMetadata& AddEventsBasedObject(const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_);
/**
* \brief Declare a new behavior as being part of the extension.
@@ -420,8 +419,7 @@ class GD_CORE_API PlatformExtension {
PlatformExtension& SetTags(const gd::String& csvTags) {
tags.clear();
tags = csvTags.Split(',');
for (size_t i = 0; i < tags.size(); i++)
{
for (size_t i = 0; i < tags.size(); i++) {
tags[i] = tags[i].Trim().LowerCase();
}
return *this;
@@ -634,26 +632,30 @@ class GD_CORE_API PlatformExtension {
*/
static gd::String GetNamespaceSeparator() { return "::"; }
static gd::String GetEventsFunctionFullType(const gd::String &extensionName,
const gd::String &functionName);
static gd::String GetEventsFunctionFullType(const gd::String& extensionName,
const gd::String& functionName);
static gd::String
GetBehaviorEventsFunctionFullType(const gd::String &extensionName,
const gd::String &behaviorName,
const gd::String &functionName);
static gd::String GetBehaviorEventsFunctionFullType(
const gd::String& extensionName,
const gd::String& behaviorName,
const gd::String& functionName);
static gd::String GetBehaviorFullType(const gd::String &extensionName,
const gd::String &behaviorName);
static gd::String GetBehaviorFullType(const gd::String& extensionName,
const gd::String& behaviorName);
static gd::String
GetObjectEventsFunctionFullType(const gd::String &extensionName,
const gd::String &objectName,
const gd::String &functionName);
static gd::String GetObjectEventsFunctionFullType(
const gd::String& extensionName,
const gd::String& objectName,
const gd::String& functionName);
static gd::String GetObjectFullType(const gd::String &extensionName,
const gd::String &objectName);
static gd::String GetObjectFullType(const gd::String& extensionName,
const gd::String& objectName);
private:
static gd::String GetExtensionFromFullObjectType(const gd::String& type);
static gd::String GetObjectNameFromFullObjectType(const gd::String& type);
private:
/**
* Set the namespace (the string all actions/conditions/expressions start
* with).
@@ -668,10 +670,10 @@ private:
gd::String fullname; ///< Name displayed to users in the editor.
gd::String informations; ///< Description displayed to users in the editor.
gd::String category;
gd::String author; ///< Author displayed to users in the editor.
gd::String license; ///< License name displayed to users in the editor.
bool deprecated; ///< true if the extension is deprecated and shouldn't be
///< shown in IDE.
gd::String author; ///< Author displayed to users in the editor.
gd::String license; ///< License name displayed to users in the editor.
bool deprecated; ///< true if the extension is deprecated and shouldn't be
///< shown in IDE.
gd::String helpPath; ///< The relative path to the help for this extension in
///< the documentation.
gd::String iconUrl; ///< The URL to the icon to be shown for this extension.

View File

@@ -5,6 +5,8 @@
* project is released under the MIT License.
*/
// NOLINTBEGIN
#ifndef GDCORE_PLATFORMEXTENSION_INL
#define GDCORE_PLATFORMEXTENSION_INL
@@ -36,3 +38,5 @@ gd::ObjectMetadata& PlatformExtension::AddObject(const gd::String& name,
} // namespace gd
#endif
// NOLINTEND

View File

@@ -29,9 +29,6 @@ void EventBasedBehaviorBrowser::ExposeEvents(
project, eventsFunctionsExtension, eventsBasedBehavior, worker);
}
void EventBasedBehaviorBrowser::ExposeObjects(
gd::Project &project, gd::ArbitraryObjectsWorker &worker) const {}
void EventBasedBehaviorBrowser::ExposeFunctions(
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) const {
worker.Launch(eventsBasedBehavior.GetEventsFunctions());
@@ -43,7 +40,4 @@ void EventBasedBehaviorBrowser::ExposeEventBasedBehaviors(
worker.Launch(eventsBasedBehavior);
}
void EventBasedBehaviorBrowser::ExposeBehaviorSharedDatas(
gd::Project &project, gd::ArbitraryBehaviorSharedDataWorker &worker) const {}
} // namespace gd

View File

@@ -67,7 +67,7 @@ public:
* \brief Do nothing.
*/
void ExposeObjects(gd::Project &project,
gd::ArbitraryObjectsWorker &worker) const override;
gd::ArbitraryObjectsWorker &worker) const override {};
/**
* \brief Call the specified worker on the event-based behavior.
@@ -80,7 +80,7 @@ public:
* \brief Do nothing.
*/
void ExposeBehaviorSharedDatas(gd::Project &project,
gd::ArbitraryBehaviorSharedDataWorker &worker) const override;
gd::ArbitraryBehaviorSharedDataWorker &worker) const override {};
private:
const gd::EventsFunctionsExtension &eventsFunctionsExtension;

View File

@@ -0,0 +1,37 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "EventBasedObjectBrowser.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventsFunctionsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
#include "GDCore/IDE/ProjectBrowserHelper.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
void EventBasedObjectBrowser::ExposeEvents(
gd::Project &project, gd::ArbitraryEventsWorker &worker) const {
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
project, eventsBasedObject, worker);
}
void EventBasedObjectBrowser::ExposeEvents(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) const {
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
project, eventsFunctionsExtension, eventsBasedObject, worker);
}
void EventBasedObjectBrowser::ExposeFunctions(
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) const {
worker.Launch(eventsBasedObject.GetEventsFunctions());
}
} // namespace gd

View File

@@ -0,0 +1,90 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/IDE/ProjectBrowser.h"
namespace gd {
class Project;
class String;
class EventsFunctionsExtension;
class EventsFunction;
class EventsBasedBehavior;
class EventsBasedObject;
class ArbitraryEventsWorker;
class ArbitraryEventsWorkerWithContext;
class ArbitraryEventsFunctionsWorker;
class ArbitraryObjectsWorker;
class ArbitraryEventBasedBehaviorsWorker;
class ArbitraryBehaviorSharedDataWorker;
} // namespace gd
namespace gd {
/**
* \brief Expose event-based object contents to workers.
*/
class GD_CORE_API EventBasedObjectBrowser : public ProjectBrowser {
public:
EventBasedObjectBrowser(
const gd::EventsFunctionsExtension &eventsFunctionsExtension_,
gd::EventsBasedObject &eventsBasedObject_)
: eventsFunctionsExtension(eventsFunctionsExtension_),
eventsBasedObject(eventsBasedObject_) {}
/**
* \brief Call the specified worker on all events of the event-based
* object.
*
* This should be the preferred way to traverse all the events of an event-based object.
*/
void ExposeEvents(gd::Project &project,
gd::ArbitraryEventsWorker &worker) const override;
/**
* \brief Call the specified worker on all events of the event-based
* object.
*
* This should be the preferred way to traverse all the events of an event-based object.
*/
void
ExposeEvents(gd::Project &project,
gd::ArbitraryEventsWorkerWithContext &worker) const override;
/**
* \brief Call the specified worker on all functions of the event-based object
*
* This should be the preferred way to traverse all the function signatures
* of an event-based object.
*/
void ExposeFunctions(gd::Project &project,
gd::ArbitraryEventsFunctionsWorker &worker) const override;
/**
* \brief Do nothing.
*/
void ExposeObjects(gd::Project &project,
gd::ArbitraryObjectsWorker &worker) const override {};
/**
* @brief Do nothing.
*/
void ExposeEventBasedBehaviors(
gd::Project &project,
gd::ArbitraryEventBasedBehaviorsWorker &worker) const override {};
/**
* \brief Do nothing.
*/
void ExposeBehaviorSharedDatas(gd::Project &project,
gd::ArbitraryBehaviorSharedDataWorker &worker) const override {};
private:
const gd::EventsFunctionsExtension &eventsFunctionsExtension;
gd::EventsBasedObject &eventsBasedObject;
};
} // namespace gd

View File

@@ -337,19 +337,19 @@ struct GD_CORE_API ExpressionCompletionDescription {
private:
CompletionKind completionKind;
gd::Variable::Type variableType;
gd::VariablesContainer::SourceType variableScope;
gd::Variable::Type variableType = gd::Variable::Unknown;
gd::VariablesContainer::SourceType variableScope = gd::VariablesContainer::Unknown;
gd::String type;
gd::String prefix;
gd::String completion;
size_t replacementStartPosition;
size_t replacementEndPosition;
size_t replacementStartPosition = 0;
size_t replacementEndPosition = 0;
gd::String objectName;
gd::String behaviorName;
bool isExact;
bool isLastParameter;
const gd::ParameterMetadata* parameterMetadata;
const gd::ObjectConfiguration* objectConfiguration;
bool isExact = false;
bool isLastParameter = false;
const gd::ParameterMetadata* parameterMetadata = &badParameterMetadata;
const gd::ObjectConfiguration* objectConfiguration = &badObjectConfiguration;
static const gd::ParameterMetadata badParameterMetadata;
static const gd::ObjectConfiguration badObjectConfiguration;

View File

@@ -33,8 +33,10 @@ class GD_CORE_API ExpressionNodeLocationFinder
* \brief Initialize the finder to search at the specified position.
*/
ExpressionNodeLocationFinder(size_t searchedPosition_)
: searchedPosition(searchedPosition_), foundNode(nullptr){};
virtual ~ExpressionNodeLocationFinder(){};
: searchedPosition(searchedPosition_),
foundNode(nullptr),
parentNode(nullptr) {};
virtual ~ExpressionNodeLocationFinder() {};
/**
* \brief Helper function to find the deepest node at the search position, if

View File

@@ -72,7 +72,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
child(nullptr) {};
const gd::String &GetType() {
return gd::ParameterMetadata::GetExpressionValueType(type);
return gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(type);
};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {

View File

@@ -42,7 +42,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
const gd::String &extraInfo_ = "")
: platform(platform_),
projectScopedContainers(projectScopedContainers_),
parentType(StringToType(gd::ParameterMetadata::GetExpressionValueType(rootType_))),
parentType(StringToType(gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(rootType_))),
childType(Type::Unknown),
forbidsUsageOfBracketsBecauseParentIsObject(false),
currentParameterExtraInfo(&extraInfo_) {};

View File

@@ -74,8 +74,8 @@ class GD_CORE_API ExpressionsParameterMover
const gd::Platform &platform;
gd::String functionName;
std::size_t oldIndex;
std::size_t newIndex;
std::size_t oldIndex = 0;
std::size_t newIndex = 0;
gd::String behaviorType;
gd::String objectType;
};

View File

@@ -4,6 +4,7 @@
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Extensions/Platform.h"
namespace gd {

View File

@@ -6,17 +6,9 @@
#include "ResourceExposer.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventsFunctionsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/IDE/ProjectBrowserHelper.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Effect.h"
@@ -24,7 +16,6 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
namespace gd {

View File

@@ -13,6 +13,7 @@
#include "GDCore/IDE/DependenciesAnalyzer.h"
#include "GDCore/IDE/GroupVariableHelper.h"
#include "GDCore/IDE/EventBasedBehaviorBrowser.h"
#include "GDCore/IDE/EventBasedObjectBrowser.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Events/BehaviorParametersFiller.h"
#include "GDCore/IDE/Events/BehaviorTypeRenamer.h"
@@ -28,11 +29,6 @@
#include "GDCore/IDE/Events/InstructionsTypeRenamer.h"
#include "GDCore/IDE/Events/LinkEventTargetRenamer.h"
#include "GDCore/IDE/Events/ProjectElementRenamer.h"
#include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventsFunctionsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/Project/BehaviorObjectTypeRenamer.h"
#include "GDCore/IDE/Project/BehaviorsSharedDataBehaviorTypeRenamer.h"
#include "GDCore/IDE/Project/FunctionParameterBehaviorTypeRenamer.h"
@@ -415,20 +411,32 @@ void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedBehavior(
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &sourceExtensionName) {
const EventBasedBehaviorBrowser eventBasedBehaviorExposer(
const EventBasedBehaviorBrowser eventBasedBehaviorBrowser(
eventsFunctionsExtension, eventsBasedBehavior);
WholeProjectRefactorer::RenameEventsFunctionsExtension(
project, eventsFunctionsExtension, sourceExtensionName,
eventsFunctionsExtension.GetName(), eventBasedBehaviorExposer);
eventsFunctionsExtension.GetName(), eventBasedBehaviorBrowser);
}
void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedObject &eventsBasedObject,
const gd::String &sourceExtensionName) {
const EventBasedObjectBrowser eventBasedObjectBrowser(
eventsFunctionsExtension, eventsBasedObject);
WholeProjectRefactorer::RenameEventsFunctionsExtension(
project, eventsFunctionsExtension, sourceExtensionName,
eventsFunctionsExtension.GetName(), eventBasedObjectBrowser);
}
void WholeProjectRefactorer::RenameEventsFunctionsExtension(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::String &oldName, const gd::String &newName) {
const WholeProjectBrowser wholeProjectExposer;
const WholeProjectBrowser wholeProjectBrowser;
RenameEventsFunctionsExtension(project, eventsFunctionsExtension, oldName,
newName, wholeProjectExposer);
newName, wholeProjectBrowser);
}
void WholeProjectRefactorer::RenameEventsFunctionsExtension(
@@ -1312,6 +1320,19 @@ bool WholeProjectRefactorer::FixInvalidRequiredBehaviorProperties(
return !invalidRequiredBehaviorProblems.empty();
}
void WholeProjectRefactorer::UpdateBehaviorNameInEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &sourceBehaviorName) {
const EventBasedBehaviorBrowser eventBasedBehaviorExposer(
eventsFunctionsExtension, eventsBasedBehavior);
WholeProjectRefactorer::RenameEventsBasedBehavior(
project, eventsFunctionsExtension, eventsBasedBehavior,
sourceBehaviorName, eventsBasedBehavior.GetName(),
eventBasedBehaviorExposer);
}
void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
@@ -1324,10 +1345,22 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
return;
}
auto &eventsBasedBehavior = eventsBasedBehaviors.Get(oldBehaviorName);
const WholeProjectBrowser projectBrowser;
WholeProjectRefactorer::RenameEventsBasedBehavior(
project, eventsFunctionsExtension, eventsBasedBehavior, oldBehaviorName,
newBehaviorName, projectBrowser);
}
void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &oldBehaviorName,
const gd::String &newBehaviorName,
const gd::ProjectBrowser &projectBrowser) {
auto renameBehaviorEventsFunction =
[&project, &eventsFunctionsExtension, &oldBehaviorName,
&newBehaviorName](const gd::EventsFunction &eventsFunction) {
&newBehaviorName, &projectBrowser](const gd::EventsFunction &eventsFunction) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the name of the
// behavior
@@ -1341,12 +1374,12 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newBehaviorName,
eventsFunction.GetName()));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, renamer);
projectBrowser.ExposeEvents(project, renamer);
}
};
auto renameBehaviorProperty = [&project, &eventsFunctionsExtension,
&oldBehaviorName, &newBehaviorName](
&oldBehaviorName, &newBehaviorName, &projectBrowser](
const gd::NamedPropertyDescriptor
&property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
@@ -1357,7 +1390,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newBehaviorName,
EventsBasedBehavior::GetPropertyActionName(property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, actionRenamer);
projectBrowser.ExposeEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1367,7 +1400,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newBehaviorName,
EventsBasedBehavior::GetPropertyConditionName(property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, conditionRenamer);
projectBrowser.ExposeEvents(project, conditionRenamer);
// Nothing to do for expression, expressions are not including the name of
// the behavior
@@ -1375,7 +1408,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
auto renameBehaviorSharedProperty =
[&project, &eventsFunctionsExtension, &oldBehaviorName,
&newBehaviorName](const gd::NamedPropertyDescriptor &property) {
&newBehaviorName, &projectBrowser](const gd::NamedPropertyDescriptor &property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
@@ -1386,7 +1419,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
eventsFunctionsExtension.GetName(), newBehaviorName,
EventsBasedBehavior::GetSharedPropertyActionName(
property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, actionRenamer);
projectBrowser.ExposeEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer =
gd::InstructionsTypeRenamer(
@@ -1399,8 +1432,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
eventsFunctionsExtension.GetName(), newBehaviorName,
EventsBasedBehavior::GetSharedPropertyConditionName(
property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
conditionRenamer);
projectBrowser.ExposeEvents(project, conditionRenamer);
// Nothing to do for expression, expressions are not including the name
// of the behavior
@@ -1435,13 +1467,25 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
renameBehaviorSharedProperty(*property);
}
const WholeProjectBrowser wholeProjectExposer;
DoRenameBehavior(project,
gd::PlatformExtension::GetBehaviorFullType(
eventsFunctionsExtension.GetName(), oldBehaviorName),
gd::PlatformExtension::GetBehaviorFullType(
eventsFunctionsExtension.GetName(), newBehaviorName),
wholeProjectExposer);
projectBrowser);
}
void WholeProjectRefactorer::UpdateObjectNameInEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedObject &eventsBasedObject,
const gd::String &sourceObjectName) {
const EventBasedObjectBrowser eventBasedObjectBrowser(
eventsFunctionsExtension, eventsBasedObject);
WholeProjectRefactorer::RenameEventsBasedObject(
project, eventsFunctionsExtension, eventsBasedObject,
sourceObjectName, eventsBasedObject.GetName(),
eventBasedObjectBrowser);
}
void WholeProjectRefactorer::RenameEventsBasedObject(
@@ -1455,10 +1499,21 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
return;
}
auto &eventsBasedObject = eventsBasedObjects.Get(oldObjectName);
const WholeProjectBrowser projectBrowser;
WholeProjectRefactorer::RenameEventsBasedObject(
project, eventsFunctionsExtension, eventsBasedObject, oldObjectName,
newObjectName, projectBrowser);
}
void WholeProjectRefactorer::RenameEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::String &oldObjectName, const gd::String &newObjectName,
const gd::ProjectBrowser &projectBrowser) {
auto renameObjectEventsFunction =
[&project, &eventsFunctionsExtension, &oldObjectName,
&newObjectName](const gd::EventsFunction &eventsFunction) {
[&project, &eventsFunctionsExtension, &oldObjectName, &newObjectName,
&projectBrowser](const gd::EventsFunction &eventsFunction) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the name of the
// object
@@ -1472,12 +1527,12 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newObjectName,
eventsFunction.GetName()));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, renamer);
projectBrowser.ExposeEvents(project, renamer);
}
};
auto renameObjectProperty = [&project, &eventsFunctionsExtension,
&oldObjectName, &newObjectName](
&oldObjectName, &newObjectName, &projectBrowser](
const gd::NamedPropertyDescriptor &property) {
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1487,7 +1542,7 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newObjectName,
EventsBasedObject::GetPropertyActionName(property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, actionRenamer);
projectBrowser.ExposeEvents(project, actionRenamer);
gd::InstructionsTypeRenamer conditionRenamer = gd::InstructionsTypeRenamer(
project,
@@ -1497,7 +1552,7 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
gd::PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(), newObjectName,
EventsBasedObject::GetPropertyConditionName(property.GetName())));
gd::ProjectBrowserHelper::ExposeProjectEvents(project, conditionRenamer);
projectBrowser.ExposeEvents(project, conditionRenamer);
// Nothing to do for expression, expressions are not including the name of
// the object
@@ -1528,13 +1583,12 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
renameObjectProperty(*property);
}
const WholeProjectBrowser wholeProjectExposer;
DoRenameObject(project,
gd::PlatformExtension::GetObjectFullType(
eventsFunctionsExtension.GetName(), oldObjectName),
gd::PlatformExtension::GetObjectFullType(
eventsFunctionsExtension.GetName(), newObjectName),
wholeProjectExposer);
projectBrowser);
}
void WholeProjectRefactorer::DoRenameEventsFunction(

View File

@@ -27,14 +27,7 @@ class ObjectsContainer;
class VariablesContainer;
class EventsBasedBehavior;
class EventsBasedObject;
class ArbitraryEventsWorker;
class ArbitraryObjectsWorker;
class ArbitraryEventsFunctionsWorker;
class ArbitraryEventsWorkerWithContext;
class ArbitraryEventBasedBehaviorsWorker;
class ArbitraryBehaviorSharedDataWorker;
class Behavior;
class BehaviorMetadata;
class UnfilledRequiredBehaviorPropertyProblem;
class ProjectBrowser;
class SerializerElement;
@@ -121,14 +114,24 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& newName);
/**
* \brief Refactor behavior events after the extension was placed in a new
* \brief Refactor behavior events after the behavior has been placed in a new
* extension.
*/
static void UpdateExtensionNameInEventsBasedBehavior(
gd::Project& project,
const gd::EventsFunctionsExtension& eventsFunctionsExtension,
gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::String& sourceExtensionName);
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &sourceExtensionName);
/**
* \brief Refactor object events after the object has been placed in a new
* extension.
*/
static void UpdateExtensionNameInEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedObject &eventsBasedObject,
const gd::String &sourceExtensionName);
/**
* \brief Refactor the project **before** an events function is renamed.
@@ -324,6 +327,16 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldBehaviorName,
const gd::String& newBehaviorName);
/**
* \brief Refactor events-based behavior events after the events-based
* behavior has been duplicated.
*/
static void UpdateBehaviorNameInEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &sourceBehaviorName);
/**
* \brief Refactor the project **before** an object is renamed.
*
@@ -337,6 +350,16 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldObjectName,
const gd::String& newObjectName);
/**
* \brief Refactor events-based object events after the events-based object
* has been duplicated.
*/
static void UpdateObjectNameInEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::EventsBasedObject &eventsBasedObject,
const gd::String &sourceObjectName);
/**
* \brief Refactor the project after a layout is renamed.
*/
@@ -654,6 +677,35 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& newName,
const gd::ProjectBrowser& projectBrowser);
/**
* \brief Refactor the project **before** a behavior is renamed.
*
* \warning Do the renaming of the specified behavior after calling this.
* This is because the behavior is expected to have its old name for the
* refactoring.
*/
static void RenameEventsBasedBehavior(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &oldBehaviorName,
const gd::String &newBehaviorName,
const gd::ProjectBrowser &projectBrowser);
/**
* \brief Refactor the project **before** an object is renamed.
*
* \warning Do the renaming of the specified object after calling this.
* This is because the object is expected to have its old name for the
* refactoring.
*/
static void RenameEventsBasedObject(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::String &oldObjectName, const gd::String &newObjectName,
const gd::ProjectBrowser &projectBrowser);
static void FindDependentBehaviorNames(
const gd::Project& project,
const gd::Object& object,

View File

@@ -8,8 +8,10 @@
#include <map>
#include <memory>
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Project/QuickCustomizationVisibilitiesContainer.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/String.h"
namespace gd {
@@ -32,12 +34,21 @@ namespace gd {
*/
class GD_CORE_API BehaviorConfigurationContainer {
public:
BehaviorConfigurationContainer() : folded(false), quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
BehaviorConfigurationContainer()
: folded(false),
quickCustomizationVisibility(QuickCustomization::Visibility::Default),
propertiesQuickCustomizationVisibilities() {};
BehaviorConfigurationContainer(const gd::String& name_,
const gd::String& type_)
: name(name_), type(type_), folded(false), quickCustomizationVisibility(QuickCustomization::Visibility::Default){};
: name(name_),
type(type_),
folded(false),
quickCustomizationVisibility(QuickCustomization::Visibility::Default),
propertiesQuickCustomizationVisibilities() {};
virtual ~BehaviorConfigurationContainer();
virtual BehaviorConfigurationContainer* Clone() const { return new BehaviorConfigurationContainer(*this); }
virtual BehaviorConfigurationContainer* Clone() const {
return new BehaviorConfigurationContainer(*this);
}
/**
* \brief Return the name identifying the behavior
@@ -68,7 +79,6 @@ class GD_CORE_API BehaviorConfigurationContainer {
*/
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
/**
* \brief Called when the IDE wants to update a custom property of the
* behavior
@@ -84,9 +94,7 @@ class GD_CORE_API BehaviorConfigurationContainer {
* \brief Called to initialize the content with the default properties
* for the behavior.
*/
virtual void InitializeContent() {
InitializeContent(content);
};
virtual void InitializeContent() { InitializeContent(content); };
/**
* \brief Serialize the behavior content.
@@ -115,15 +123,42 @@ class GD_CORE_API BehaviorConfigurationContainer {
*/
bool IsFolded() const { return folded; }
void SetQuickCustomizationVisibility(QuickCustomization::Visibility visibility) {
/**
* @brief Set if the whole behavior should be visible or not in the Quick
* Customization.
*/
void SetQuickCustomizationVisibility(
QuickCustomization::Visibility visibility) {
quickCustomizationVisibility = visibility;
}
/**
* @brief Get if the whole behavior should be visible or not in the Quick
* Customization.
*/
QuickCustomization::Visibility GetQuickCustomizationVisibility() const {
return quickCustomizationVisibility;
}
protected:
/**
* @brief Get the map of properties and their visibility in the Quick
* Customization.
*/
QuickCustomizationVisibilitiesContainer&
GetPropertiesQuickCustomizationVisibilities() {
return propertiesQuickCustomizationVisibilities;
}
/**
* @brief Get the map of properties and their visibility in the Quick
* Customization.
*/
const QuickCustomizationVisibilitiesContainer&
GetPropertiesQuickCustomizationVisibilities() const {
return propertiesQuickCustomizationVisibilities;
}
protected:
/**
* \brief Called when the IDE wants to know about the custom properties of the
* behavior.
@@ -159,7 +194,7 @@ protected:
* \brief Called to initialize the content with the default properties
* for the behavior.
*/
virtual void InitializeContent(gd::SerializerElement& behaviorContent){};
virtual void InitializeContent(gd::SerializerElement& behaviorContent) {};
private:
gd::String name; ///< Name of the behavior
@@ -169,6 +204,8 @@ protected:
gd::SerializerElement content; // Storage for the behavior properties
bool folded;
QuickCustomization::Visibility quickCustomizationVisibility;
QuickCustomizationVisibilitiesContainer
propertiesQuickCustomizationVisibilities;
};
} // namespace gd

View File

@@ -72,7 +72,7 @@ gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(
}
if (!eventsBasedObject->GetObjects().HasObjectNamed(objectName)) {
gd::LogError("Tried to get the configuration of a child-object:" + objectName
gd::LogError("Tried to get the configuration of a child-object: " + objectName
+ " that doesn't exist in the event-based object: " + GetType());
return badObjectConfiguration;
}

View File

@@ -9,6 +9,7 @@
#include <map>
#include <memory>
#include <unordered_set>
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/EventsBasedObject.h"
@@ -109,6 +110,27 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
static const gd::CustomObjectConfiguration::EdgeAnchor
GetEdgeAnchorFromString(const gd::String &value);
/**
* Check if a child object properties must be displayed as folded in the editor.
* This is only useful when the object can override its children configuration (which
* is something being deprecated).
*/
bool IsChildObjectFolded(const gd::String& childName) const {
return unfoldedChildren.find(childName) == unfoldedChildren.end();
}
/**
* Set if a child object properties must be displayed as folded in the editor.
* This is only useful when the object can override its children configuration (which
* is something being deprecated).
*/
void SetChildObjectFolded(const gd::String& childName, bool folded) {
if (!folded)
unfoldedChildren.insert(childName);
else
unfoldedChildren.erase(childName);
}
protected:
void DoSerializeTo(SerializerElement& element) const override;
void DoUnserializeFrom(Project& project, const SerializerElement& element) override;
@@ -118,11 +140,12 @@ protected:
bool IsOverridingEventsBasedObjectChildrenConfiguration() const;
const Project* project; ///< The project is used to get the
///< EventBasedObject from the fullType.
const Project* project = nullptr; ///< The project is used to get the
///< EventBasedObject from the fullType.
gd::SerializerElement objectContent;
bool isMarkedAsOverridingEventsBasedObjectChildrenConfiguration;
std::unordered_set<gd::String> unfoldedChildren;
bool isMarkedAsOverridingEventsBasedObjectChildrenConfiguration = false;
mutable std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
static gd::ObjectConfiguration badObjectConfiguration;

View File

@@ -12,6 +12,7 @@ namespace gd {
void Effect::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("effectType", GetEffectType());
if (IsFolded()) element.SetBoolAttribute("folded", true);
SerializerElement& doubleParametersElement =
element.AddChild("doubleParameters");
@@ -41,6 +42,7 @@ void Effect::UnserializeFrom(const SerializerElement& element) {
"effectName"
// end of compatibility code
));
SetFolded(element.GetBoolAttribute("folded", false));
doubleParameters.clear();
const SerializerElement& doubleParametersElement =

View File

@@ -10,6 +10,7 @@
#include "EventsFunction.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/MakeUnique.h"
#include "GDCore/Extensions/PlatformExtension.h"
namespace gd {
@@ -98,8 +99,11 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
void EventsFunctionsExtension::UnserializeFrom(
gd::Project& project, const SerializerElement& element) {
UnserializeExtensionDeclarationFrom(project, element);
UnserializeExtensionImplementationFrom(project, element);
// Unserialize first the "declaration" (everything but objects content)
// so that objects can be then unserialized in proper order (they can depend
// on each others)
UnserializeExtensionDeclarationFrom(project, element);
UnserializeExtensionImplementationFrom(project, element);
}
void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
@@ -185,11 +189,93 @@ void EventsFunctionsExtension::UnserializeExtensionImplementationFrom(
eventsBasedBehaviors.UnserializeElementsFrom(
"eventsBasedBehavior", project, element.GetChild("eventsBasedBehaviors"));
// It's important to load the objects without erasing them first as each object
// might reference other objects, and so need to know if a custom object exists
// in the project or not.
eventsBasedObjects.ProgressivelyUnserializeElementsFrom(
"eventsBasedObject", project, element.GetChild("eventsBasedObjects"));
auto &eventsBasedObjectsElement = element.GetChild("eventsBasedObjects");
eventsBasedObjectsElement.ConsiderAsArrayOf("eventsBasedObject");
for (gd::String &eventsBasedObjectName :
GetUnserializingOrderEventsBasedObjectNames(eventsBasedObjectsElement)) {
size_t extensionIndex = eventsBasedObjects.GetPosition(
eventsBasedObjects.Get(eventsBasedObjectName));
const SerializerElement &eventsBasedObjectElement =
eventsBasedObjectsElement.GetChild(extensionIndex);
eventsBasedObjects.at(extensionIndex)
.UnserializeFrom(project, eventsBasedObjectElement);
}
}
std::vector<gd::String>
EventsFunctionsExtension::GetUnserializingOrderEventsBasedObjectNames(
const gd::SerializerElement &eventsBasedObjectsElement) {
// Child-objects need the event-based objects they use to be loaded completely
// before they are unserialized.
// At the beginning, everything is yet to be loaded.
std::vector<gd::String> remainingEventsBasedObjectNames(
eventsBasedObjects.size());
for (std::size_t i = 0; i < eventsBasedObjects.size(); ++i) {
remainingEventsBasedObjectNames[i] = eventsBasedObjects.at(i).GetName();
}
// Helper allowing to find if an object depends on at least one other object from
// the extension that is not loaded yet.
auto &extensionName = name;
auto isDependentFromRemainingEventsBasedObjects =
[&remainingEventsBasedObjectNames,
&extensionName](const gd::SerializerElement &eventsBasedObjectElement) {
auto &objectsElement = eventsBasedObjectElement.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");
for (std::size_t objectIndex = 0;
objectIndex < objectsElement.GetChildrenCount(); ++objectIndex) {
const gd::String &objectType =
objectsElement.GetChild(objectIndex).GetStringAttribute("type");
gd::String usedExtensionName =
PlatformExtension::GetExtensionFromFullObjectType(objectType);
if (usedExtensionName != extensionName) {
// The object comes from another extension: the project is already responsible
// for loading extensions in the proper order.
continue;
}
gd::String eventsBasedObjectName =
gd::PlatformExtension::GetObjectNameFromFullObjectType(
objectType);
if (std::find(remainingEventsBasedObjectNames.begin(),
remainingEventsBasedObjectNames.end(),
eventsBasedObjectName) !=
remainingEventsBasedObjectNames.end()) {
return true;
}
}
return false;
};
// Find the order of loading so that the objects are loaded when all the objects
// they depend on are already loaded.
std::vector<gd::String> loadOrderEventsBasedObjectNames;
bool foundAnyEventsBasedObject = true;
while (foundAnyEventsBasedObject) {
foundAnyEventsBasedObject = false;
for (std::size_t i = 0; i < remainingEventsBasedObjectNames.size(); ++i) {
auto eventsBasedObjectName = remainingEventsBasedObjectNames[i];
size_t extensionIndex = eventsBasedObjects.GetPosition(
eventsBasedObjects.Get(eventsBasedObjectName));
const SerializerElement &eventsBasedObjectElement =
eventsBasedObjectsElement.GetChild(extensionIndex);
if (!isDependentFromRemainingEventsBasedObjects(
eventsBasedObjectElement)) {
loadOrderEventsBasedObjectNames.push_back(eventsBasedObjectName);
remainingEventsBasedObjectNames.erase(
remainingEventsBasedObjectNames.begin() + i);
i--;
foundAnyEventsBasedObject = true;
}
}
}
return loadOrderEventsBasedObjectNames;
}
bool EventsFunctionsExtension::IsExtensionLifecycleEventsFunction(

View File

@@ -314,6 +314,9 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
return dependency;
}
std::vector<gd::String> GetUnserializingOrderEventsBasedObjectNames(
const gd::SerializerElement &eventsBasedObjectsElement);
gd::String version;
gd::String extensionNamespace;
gd::String shortDescription;

View File

@@ -7,7 +7,7 @@
namespace gd {
ExternalEvents::ExternalEvents() : lastChangeTimeStamp(0) {
ExternalEvents::ExternalEvents() {
// ctor
}
@@ -24,14 +24,12 @@ ExternalEvents& ExternalEvents::operator=(const ExternalEvents& rhs) {
void ExternalEvents::Init(const ExternalEvents& externalEvents) {
name = externalEvents.GetName();
associatedScene = externalEvents.GetAssociatedLayout();
lastChangeTimeStamp = externalEvents.GetLastChangeTimeStamp();
events = externalEvents.events;
}
void ExternalEvents::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
element.SetAttribute("associatedLayout", associatedScene);
element.SetAttribute("lastChangeTimeStamp", (int)lastChangeTimeStamp);
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
}
@@ -41,8 +39,6 @@ void ExternalEvents::UnserializeFrom(gd::Project& project,
name = element.GetStringAttribute("name", "", "Name");
associatedScene =
element.GetStringAttribute("associatedLayout", "", "AssociatedScene");
lastChangeTimeStamp =
element.GetIntAttribute("lastChangeTimeStamp", 0, "LastChangeTimeStamp");
gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events"));
}

View File

@@ -67,24 +67,6 @@ class GD_CORE_API ExternalEvents {
associatedScene = name_;
};
/**
* Get the latest time of the build.
* Used when the IDE found that the external events can be compiled separately
* from scene's events.
*
* \todo This is specific to GD C++ Platform
*/
time_t GetLastChangeTimeStamp() const { return lastChangeTimeStamp; };
/**
* Change the latest time of the build of the external events.
*
* \todo This is specific to GD C++ Platform
*/
void SetLastChangeTimeStamp(time_t newTimeStamp) {
lastChangeTimeStamp = newTimeStamp;
};
/**
* \brief Get the events.
*/
@@ -109,7 +91,6 @@ class GD_CORE_API ExternalEvents {
private:
gd::String name;
gd::String associatedScene;
time_t lastChangeTimeStamp; ///< Time of the last build
gd::EventsList events; ///< List of events
/**
@@ -119,19 +100,6 @@ class GD_CORE_API ExternalEvents {
void Init(const ExternalEvents& externalEvents);
};
/**
* \brief Functor testing ExternalEvents' name
*/
struct ExternalEventsHasName
: public std::binary_function<std::unique_ptr<gd::ExternalEvents>,
gd::String,
bool> {
bool operator()(const std::unique_ptr<gd::ExternalEvents>& externalEvents,
gd::String name) const {
return externalEvents->GetName() == name;
}
};
} // namespace gd
#endif // GDCORE_EXTERNALEVENTS_H

View File

@@ -163,6 +163,20 @@ bool InitialInstancesContainer::HasInstancesOfObject(
});
}
bool InitialInstancesContainer::IsInstancesCountOfObjectGreaterThan(
const gd::String &objectName, const std::size_t minInstanceCount) const {
std::size_t count = 0;
for (const gd::InitialInstance &instance : initialInstances) {
if (instance.GetObjectName() == objectName) {
count++;
if (count > minInstanceCount) {
return true;
}
}
}
return false;
}
void InitialInstancesContainer::Create(
const InitialInstancesContainer& source) {
try {

View File

@@ -153,6 +153,13 @@ class GD_CORE_API InitialInstancesContainer {
*/
bool HasInstancesOfObject(const gd::String &objectName) const;
/**
* \brief Return true if there is at least N instances of the given object.
*/
bool
IsInstancesCountOfObjectGreaterThan(const gd::String &objectName,
const std::size_t minInstanceCount) const;
/**
* \brief Remove all instances
*/

View File

@@ -24,10 +24,11 @@
#include "GDCore/Project/ObjectGroup.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/PolymorphicClone.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Tools/PolymorphicClone.h"
using namespace std;
@@ -43,7 +44,7 @@ Layout& Layout::operator=(const Layout& other) {
return *this;
}
Layout::~Layout(){};
Layout::~Layout() {};
Layout::Layout()
: backgroundColorR(209),
@@ -52,9 +53,7 @@ Layout::Layout()
stopSoundsOnStartup(true),
standardSortMethod(true),
disableInputWhenNotFocused(true),
variables(gd::VariablesContainer::SourceType::Scene)
{
}
variables(gd::VariablesContainer::SourceType::Scene) {}
void Layout::SetName(const gd::String& name_) {
name = name_;
@@ -102,7 +101,9 @@ const gd::Layer& Layout::GetLayer(const gd::String& name) const {
return layers.GetLayer(name);
}
gd::Layer& Layout::GetLayer(std::size_t index) { return layers.GetLayer(index); }
gd::Layer& Layout::GetLayer(std::size_t index) {
return layers.GetLayer(index);
}
const gd::Layer& Layout::GetLayer(std::size_t index) const {
return layers.GetLayer(index);
@@ -125,9 +126,7 @@ void Layout::InsertLayer(const gd::Layer& layer, std::size_t position) {
layers.InsertLayer(layer, position);
}
void Layout::RemoveLayer(const gd::String& name) {
layers.RemoveLayer(name);
}
void Layout::RemoveLayer(const gd::String& name) { layers.RemoveLayer(name); }
void Layout::SwapLayers(std::size_t firstLayerIndex,
std::size_t secondLayerIndex) {
@@ -153,7 +152,7 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
allBehaviorsNames.push_back(behavior.GetName());
}
}
auto &globalObjects = project.GetObjects();
auto& globalObjects = project.GetObjects();
for (std::size_t i = 0; i < globalObjects.GetObjectsCount(); ++i) {
std::vector<gd::String> objectBehaviors =
globalObjects.GetObject(i).GetAllBehaviorNames();
@@ -173,7 +172,8 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
if (behaviorsSharedData.find(name) != behaviorsSharedData.end()) continue;
auto sharedData = CreateBehaviorsSharedData(project, name, allBehaviorsTypes[i]);
auto sharedData =
CreateBehaviorsSharedData(project, name, allBehaviorsTypes[i]);
if (sharedData) {
behaviorsSharedData[name] = std::move(sharedData);
}
@@ -196,37 +196,39 @@ void Layout::UpdateBehaviorsSharedData(gd::Project& project) {
}
std::unique_ptr<gd::BehaviorsSharedData> Layout::CreateBehaviorsSharedData(
gd::Project& project, const gd::String& name, const gd::String& behaviorsType) {
if (project.HasEventsBasedBehavior(behaviorsType)) {
auto sharedData =
gd::make_unique<gd::CustomBehaviorsSharedData>(name, project, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
}
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(
project.GetCurrentPlatform(),
behaviorsType);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning("Tried to create a behavior shared data with an unknown type: " +
behaviorsType + " on object " + GetName() + "!");
gd::Project& project,
const gd::String& name,
const gd::String& behaviorsType) {
if (project.HasEventsBasedBehavior(behaviorsType)) {
auto sharedData = gd::make_unique<gd::CustomBehaviorsSharedData>(
name, project, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
}
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(project.GetCurrentPlatform(),
behaviorsType);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning(
"Tried to create a behavior shared data with an unknown type: " +
behaviorsType + " on object " + GetName() + "!");
// It's probably an events-based behavior that was removed.
// Create a custom behavior shared data to preserve the properties values.
auto sharedData =
gd::make_unique<gd::CustomBehaviorsSharedData>(name, project, behaviorsType);
sharedData->InitializeContent();
return std::move(sharedData);
}
gd::BehaviorsSharedData* behaviorsSharedDataBluePrint =
behaviorMetadata.GetSharedDataInstance();
if (!behaviorsSharedDataBluePrint) return nullptr;
auto sharedData = behaviorsSharedDataBluePrint->Clone();
sharedData->SetName(name);
sharedData->SetTypeName(behaviorsType);
auto sharedData = gd::make_unique<gd::CustomBehaviorsSharedData>(
name, project, behaviorsType);
sharedData->InitializeContent();
return std::unique_ptr<gd::BehaviorsSharedData>(sharedData);
return std::move(sharedData);
}
gd::BehaviorsSharedData* behaviorsSharedDataBluePrint =
behaviorMetadata.GetSharedDataInstance();
if (!behaviorsSharedDataBluePrint) return nullptr;
auto sharedData = behaviorsSharedDataBluePrint->Clone();
sharedData->SetName(name);
sharedData->SetTypeName(behaviorsType);
sharedData->InitializeContent();
return std::unique_ptr<gd::BehaviorsSharedData>(sharedData);
}
void Layout::SerializeTo(SerializerElement& element) const {
@@ -243,11 +245,13 @@ void Layout::SerializeTo(SerializerElement& element) const {
editorSettings.SerializeTo(element.AddChild("uiSettings"));
objectsContainer.GetObjectGroups().SerializeTo(element.AddChild("objectsGroups"));
objectsContainer.GetObjectGroups().SerializeTo(
element.AddChild("objectsGroups"));
GetVariables().SerializeTo(element.AddChild("variables"));
GetInitialInstances().SerializeTo(element.AddChild("instances"));
objectsContainer.SerializeObjectsTo(element.AddChild("objects"));
objectsContainer.SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
objectsContainer.SerializeFoldersTo(
element.AddChild("objectsFolderStructure"));
gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events"));
@@ -257,15 +261,33 @@ void Layout::SerializeTo(SerializerElement& element) const {
element.AddChild("behaviorsSharedData");
behaviorDatasElement.ConsiderAsArrayOf("behaviorSharedData");
for (const auto& it : behaviorsSharedData) {
const gd::BehaviorsSharedData& sharedData = *it.second;
SerializerElement& dataElement =
behaviorDatasElement.AddChild("behaviorSharedData");
it.second->SerializeTo(dataElement);
sharedData.SerializeTo(dataElement);
dataElement.RemoveChild("type"); // The content can contain type or name
// properties, remove them.
dataElement.RemoveChild("name");
dataElement.SetAttribute("type", it.second->GetTypeName());
dataElement.SetAttribute("name", it.second->GetName());
dataElement.SetAttribute("type", sharedData.GetTypeName());
dataElement.SetAttribute("name", sharedData.GetName());
// Handle Quick Customization info.
dataElement.RemoveChild("propertiesQuickCustomizationVisibilities");
const QuickCustomizationVisibilitiesContainer&
propertiesQuickCustomizationVisibilities =
sharedData.GetPropertiesQuickCustomizationVisibilities();
if (!propertiesQuickCustomizationVisibilities.IsEmpty()) {
propertiesQuickCustomizationVisibilities.SerializeTo(
dataElement.AddChild("propertiesQuickCustomizationVisibilities"));
}
const QuickCustomization::Visibility visibility =
sharedData.GetQuickCustomizationVisibility();
if (visibility != QuickCustomization::Visibility::Default) {
dataElement.SetAttribute(
"quickCustomizationVisibility",
QuickCustomization::VisibilityAsString(visibility));
}
}
}
@@ -289,9 +311,11 @@ void Layout::UnserializeFrom(gd::Project& project,
gd::EventsListSerialization::UnserializeEventsFrom(
project, GetEvents(), element.GetChild("events", 0, "Events"));
objectsContainer.UnserializeObjectsFrom(project, element.GetChild("objects", 0, "Objets"));
objectsContainer.UnserializeObjectsFrom(
project, element.GetChild("objects", 0, "Objets"));
if (element.HasChild("objectsFolderStructure")) {
objectsContainer.UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
objectsContainer.UnserializeFoldersFrom(
project, element.GetChild("objectsFolderStructure", 0));
}
objectsContainer.AddMissingObjectsInRootFolder();
@@ -321,7 +345,6 @@ void Layout::UnserializeFrom(gd::Project& project,
"Behavior"); // Compatibility with GD <= 4
gd::String name = sharedDataElement.GetStringAttribute("name", "", "Name");
auto sharedData = CreateBehaviorsSharedData(project, name, type);
if (sharedData) {
// Compatibility with GD <= 4.0.98
@@ -336,6 +359,21 @@ void Layout::UnserializeFrom(gd::Project& project,
else {
sharedData->UnserializeFrom(sharedDataElement);
}
// Handle Quick Customization info.
if (sharedDataElement.HasChild(
"propertiesQuickCustomizationVisibilities")) {
sharedData->GetPropertiesQuickCustomizationVisibilities()
.UnserializeFrom(sharedDataElement.GetChild(
"propertiesQuickCustomizationVisibilities"));
}
if (sharedDataElement.HasChild("quickCustomizationVisibility")) {
sharedData->SetQuickCustomizationVisibility(
QuickCustomization::StringAsVisibility(
sharedDataElement.GetStringAttribute(
"quickCustomizationVisibility")));
}
behaviorsSharedData[name] = std::move(sharedData);
}
}
@@ -390,8 +428,9 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
type = project.GetObject(name).GetType();
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
else if (searchInGroups) {
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
if (layout.GetObjectGroups()[i].GetName() == name) {
@@ -448,11 +487,12 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
return type;
}
void GD_CORE_API FilterBehaviorNamesFromObject(
const gd::Object &object, const gd::String &behaviorType,
std::vector<gd::String> &behaviorNames) {
void GD_CORE_API
FilterBehaviorNamesFromObject(const gd::Object& object,
const gd::String& behaviorType,
std::vector<gd::String>& behaviorNames) {
for (size_t i = 0; i < behaviorNames.size();) {
auto &behaviorName = behaviorNames[i];
auto& behaviorName = behaviorNames[i];
if (!object.HasBehaviorNamed(behaviorName) ||
object.GetBehavior(behaviorName).GetTypeName() != behaviorType) {
behaviorNames.erase(behaviorNames.begin() + i);
@@ -462,19 +502,21 @@ void GD_CORE_API FilterBehaviorNamesFromObject(
}
}
std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
const gd::ObjectsContainer &project, const gd::ObjectsContainer &layout,
const gd::String &objectOrGroupName, const gd::String &behaviorType,
bool searchInGroups) {
std::vector<gd::String> GD_CORE_API
GetBehaviorNamesInObjectOrGroup(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::String& objectOrGroupName,
const gd::String& behaviorType,
bool searchInGroups) {
// Search in objects.
if (layout.HasObjectNamed(objectOrGroupName)) {
auto &object = layout.GetObject(objectOrGroupName);
auto& object = layout.GetObject(objectOrGroupName);
auto behaviorNames = object.GetAllBehaviorNames();
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
return behaviorNames;
}
if (project.HasObjectNamed(objectOrGroupName)) {
auto &object = project.GetObject(objectOrGroupName);
auto& object = project.GetObject(objectOrGroupName);
auto behaviorNames = object.GetAllBehaviorNames();
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
return behaviorNames;
@@ -486,9 +528,10 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
}
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
const gd::ObjectsContainer *container;
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
const gd::ObjectsContainer* container;
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
container = &layout;
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
@@ -497,7 +540,7 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
std::vector<gd::String> behaviorNames;
return behaviorNames;
}
const vector<gd::String> &groupsObjects =
const vector<gd::String>& groupsObjects =
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
// Empty groups don't contain any behavior.
@@ -510,15 +553,15 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
auto behaviorNames = GetBehaviorNamesInObjectOrGroup(
project, layout, groupsObjects[0], behaviorType, false);
for (size_t i = 1; i < groupsObjects.size(); i++) {
auto &objectName = groupsObjects[i];
auto& objectName = groupsObjects[i];
if (layout.HasObjectNamed(objectName)) {
auto &object = layout.GetObject(objectName);
auto& object = layout.GetObject(objectName);
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
return behaviorNames;
}
if (project.HasObjectNamed(objectName)) {
auto &object = project.GetObject(objectName);
auto& object = project.GetObject(objectName);
FilterBehaviorNamesFromObject(object, behaviorType, behaviorNames);
return behaviorNames;
}
@@ -529,10 +572,10 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
return behaviorNames;
}
bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
const gd::String &objectOrGroupName,
const gd::String &behaviorName,
bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::String& objectOrGroupName,
const gd::String& behaviorName,
bool searchInGroups) {
// Search in objects.
if (layout.HasObjectNamed(objectOrGroupName)) {
@@ -547,9 +590,10 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
}
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
const gd::ObjectsContainer *container;
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
const gd::ObjectsContainer* container;
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
container = &layout;
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
@@ -557,7 +601,7 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
} else {
return false;
}
const vector<gd::String> &groupsObjects =
const vector<gd::String>& groupsObjects =
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
// Empty groups don't contain any behavior.
@@ -566,9 +610,9 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
}
// Check that all objects have the behavior.
for (auto &&object : groupsObjects) {
if (!HasBehaviorInObjectOrGroup(project, layout, object, behaviorName,
false)) {
for (auto&& object : groupsObjects) {
if (!HasBehaviorInObjectOrGroup(
project, layout, object, behaviorName, false)) {
return false;
}
}
@@ -576,18 +620,18 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
}
bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::String objectOrGroupName,
gd::String behaviorName,
bool searchInGroups) {
const gd::ObjectsContainer& layout,
gd::String objectOrGroupName,
gd::String behaviorName,
bool searchInGroups) {
// Search in objects.
if (layout.HasObjectNamed(objectOrGroupName)) {
auto &object = layout.GetObject(objectOrGroupName);
auto& object = layout.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName) &&
object.GetBehavior(behaviorName).IsDefaultBehavior();
}
if (project.HasObjectNamed(objectOrGroupName)) {
auto &object = project.GetObject(objectOrGroupName);
auto& object = project.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName) &&
object.GetBehavior(behaviorName).IsDefaultBehavior();
}
@@ -597,9 +641,10 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
}
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
const gd::ObjectsContainer *container;
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
const gd::ObjectsContainer* container;
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
container = &layout;
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
@@ -607,7 +652,7 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
} else {
return false;
}
const vector<gd::String> &groupsObjects =
const vector<gd::String>& groupsObjects =
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
// Empty groups don't contain any behavior.
@@ -616,30 +661,32 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
}
// Check that all objects have the same type.
for (auto &&object : groupsObjects) {
if (!IsDefaultBehavior(project, layout, object, behaviorName,
false)) {
for (auto&& object : groupsObjects) {
if (!IsDefaultBehavior(project, layout, object, behaviorName, false)) {
return false;
}
}
return true;
}
gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::String& objectOrGroupName,
const gd::String& behaviorName,
bool searchInGroups) {
gd::String GD_CORE_API
GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
const gd::String& objectOrGroupName,
const gd::String& behaviorName,
bool searchInGroups) {
// Search in objects.
if (layout.HasObjectNamed(objectOrGroupName)) {
auto &object = layout.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName) ?
object.GetBehavior(behaviorName).GetTypeName() : "";
auto& object = layout.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName)
? object.GetBehavior(behaviorName).GetTypeName()
: "";
}
if (project.HasObjectNamed(objectOrGroupName)) {
auto &object = project.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName) ?
object.GetBehavior(behaviorName).GetTypeName() : "";
auto& object = project.GetObject(objectOrGroupName);
return object.HasBehaviorNamed(behaviorName)
? object.GetBehavior(behaviorName).GetTypeName()
: "";
}
if (!searchInGroups) {
@@ -647,9 +694,10 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
}
// Search in groups.
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
const gd::ObjectsContainer *container;
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
const gd::ObjectsContainer* container;
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
container = &layout;
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
@@ -657,7 +705,7 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
} else {
return "";
}
const vector<gd::String> &groupsObjects =
const vector<gd::String>& groupsObjects =
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
// Empty groups don't contain any behavior.
@@ -668,9 +716,9 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
// Check that all objects have the behavior with the same type.
auto behaviorType = GetTypeOfBehaviorInObjectOrGroup(
project, layout, groupsObjects[0], behaviorName, false);
for (auto &&object : groupsObjects) {
if (GetTypeOfBehaviorInObjectOrGroup(project, layout, object, behaviorName,
false) != behaviorType) {
for (auto&& object : groupsObjects) {
if (GetTypeOfBehaviorInObjectOrGroup(
project, layout, object, behaviorName, false) != behaviorType) {
return "";
}
}
@@ -682,14 +730,14 @@ gd::String GD_CORE_API GetTypeOfBehavior(const gd::ObjectsContainer& project,
gd::String name,
bool searchInGroups) {
for (std::size_t i = 0; i < layout.GetObjectsCount(); ++i) {
const auto &object = layout.GetObject(i);
const auto& object = layout.GetObject(i);
if (object.HasBehaviorNamed(name)) {
return object.GetBehavior(name).GetTypeName();
}
}
for (std::size_t i = 0; i < project.GetObjectsCount(); ++i) {
const auto &object = project.GetObject(i);
const auto& object = project.GetObject(i);
if (object.HasBehaviorNamed(name)) {
return object.GetBehavior(name).GetTypeName();
}
@@ -726,8 +774,9 @@ GetBehaviorsOfObject(const gd::ObjectsContainer& project,
}
// Search in groups
// Currently, a group is considered as the "intersection" of all of its objects.
// Search "groups is the intersection of its objects" in the codebase.
// Currently, a group is considered as the "intersection" of all of its
// objects. Search "groups is the intersection of its objects" in the
// codebase.
if (searchInGroups) {
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
if (layout.GetObjectGroups()[i].GetName() == name) {

View File

@@ -369,9 +369,9 @@ class GD_CORE_API Layout {
private:
gd::String name; ///< Scene name
gd::String mangledName; ///< The scene name mangled by SceneNameMangler
unsigned int backgroundColorR; ///< Background color Red component
unsigned int backgroundColorG; ///< Background color Green component
unsigned int backgroundColorB; ///< Background color Blue component
unsigned int backgroundColorR = 0; ///< Background color Red component
unsigned int backgroundColorG = 0; ///< Background color Green component
unsigned int backgroundColorB = 0; ///< Background color Blue component
gd::String title; ///< Title displayed in the window
gd::VariablesContainer variables; ///< Variables list
gd::ObjectsContainer objectsContainer;
@@ -379,12 +379,12 @@ class GD_CORE_API Layout {
gd::LayersContainer layers;
std::map<gd::String, std::unique_ptr<gd::BehaviorsSharedData>>
behaviorsSharedData; ///< Initial shared datas of behaviors
bool stopSoundsOnStartup; ///< True to make the scene stop all sounds at
///< startup.
bool standardSortMethod; ///< True to sort objects using standard sort.
bool disableInputWhenNotFocused; /// If set to true, the input must be
/// disabled when the window do not have the
/// focus.
bool stopSoundsOnStartup = true; ///< True to make the scene stop all sounds at
///< startup.
bool standardSortMethod = true; ///< True to sort objects using standard sort.
bool disableInputWhenNotFocused = true; /// If set to true, the input must be
/// disabled when the window do not have the
/// focus.
static gd::BehaviorsSharedData
badBehaviorSharedData; ///< Null object, returned when
///< GetBehaviorSharedData can not find the

View File

@@ -12,8 +12,9 @@
#include "GDCore/Project/CustomBehavior.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Tools/UUID/UUID.h"
@@ -27,8 +28,8 @@ Object::Object(const gd::String& name_,
: name(name_),
configuration(std::move(configuration_)),
objectVariables(gd::VariablesContainer::SourceType::Object) {
SetType(type_);
}
SetType(type_);
}
Object::Object(const gd::String& name_,
const gd::String& type_,
@@ -36,8 +37,8 @@ Object::Object(const gd::String& name_,
: name(name_),
configuration(configuration_),
objectVariables(gd::VariablesContainer::SourceType::Object) {
SetType(type_);
}
SetType(type_);
}
void Object::Init(const gd::Object& object) {
persistentUuid = object.persistentUuid;
@@ -54,9 +55,7 @@ void Object::Init(const gd::Object& object) {
configuration = object.configuration->Clone();
}
gd::ObjectConfiguration& Object::GetConfiguration() {
return *configuration;
}
gd::ObjectConfiguration& Object::GetConfiguration() { return *configuration; }
const gd::ObjectConfiguration& Object::GetConfiguration() const {
return *configuration;
@@ -77,8 +76,7 @@ bool Object::RenameBehavior(const gd::String& name, const gd::String& newName) {
behaviors.find(newName) != behaviors.end())
return false;
std::unique_ptr<Behavior> aut =
std::move(behaviors.find(name)->second);
std::unique_ptr<Behavior> aut = std::move(behaviors.find(name)->second);
behaviors.erase(name);
behaviors[newName] = std::move(aut);
behaviors[newName]->SetName(newName);
@@ -99,10 +97,10 @@ bool Object::HasBehaviorNamed(const gd::String& name) const {
}
gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
const gd::String& type,
const gd::String& name) {
auto initializeAndAdd =
[this, &name](std::unique_ptr<gd::Behavior> behavior) {
const gd::String& type,
const gd::String& name) {
auto initializeAndAdd = [this,
&name](std::unique_ptr<gd::Behavior> behavior) {
behavior->InitializeContent();
this->behaviors[name] = std::move(behavior);
return this->behaviors[name].get();
@@ -111,18 +109,17 @@ gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
if (project.HasEventsBasedBehavior(type)) {
return initializeAndAdd(
gd::make_unique<CustomBehavior>(name, project, type));
}
else {
} else {
const gd::BehaviorMetadata& behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(project.GetCurrentPlatform(),
type);
if (gd::MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning("Tried to create a behavior with an unknown type: " + type
+ " on object " + GetName() + "!");
// It's probably an events-based behavior that was removed.
// Create a custom behavior to preserve the properties values.
return initializeAndAdd(
gd::make_unique<CustomBehavior>(name, project, type));
gd::LogWarning("Tried to create a behavior with an unknown type: " +
type + " on object " + GetName() + "!");
// It's probably an events-based behavior that was removed.
// Create a custom behavior to preserve the properties values.
return initializeAndAdd(
gd::make_unique<CustomBehavior>(name, project, type));
}
std::unique_ptr<gd::Behavior> behavior(behaviorMetadata.Get().Clone());
behavior->SetName(name);
@@ -196,6 +193,23 @@ void Object::UnserializeFrom(gd::Project& project,
else {
behavior->UnserializeFrom(behaviorElement);
}
bool isFolded = behaviorElement.GetBoolAttribute("isFolded", false);
behavior->SetFolded(isFolded);
// Handle Quick Customization info.
if (behaviorElement.HasChild(
"propertiesQuickCustomizationVisibilities")) {
behavior->GetPropertiesQuickCustomizationVisibilities().UnserializeFrom(
behaviorElement.GetChild(
"propertiesQuickCustomizationVisibilities"));
}
if (behaviorElement.HasChild("quickCustomizationVisibility")) {
behavior->SetQuickCustomizationVisibility(
QuickCustomization::StringAsVisibility(
behaviorElement.GetStringAttribute(
"quickCustomizationVisibility")));
}
}
}
@@ -217,8 +231,8 @@ void Object::SerializeTo(SerializerElement& element) const {
std::vector<gd::String> allBehaviors = GetAllBehaviorNames();
for (std::size_t i = 0; i < allBehaviors.size(); ++i) {
const gd::Behavior& behavior = GetBehavior(allBehaviors[i]);
// Default behaviors are added at the object creation according to metadata.
// They don't need to be serialized.
// Default behaviors are added at the object creation according to
// metadata. They don't need to be serialized.
if (behavior.IsDefaultBehavior()) {
continue;
}
@@ -228,8 +242,27 @@ void Object::SerializeTo(SerializerElement& element) const {
behaviorElement.RemoveChild("type"); // The content can contain type or
// name properties, remove them.
behaviorElement.RemoveChild("name");
behaviorElement.RemoveChild("isFolded");
behaviorElement.SetAttribute("type", behavior.GetTypeName());
behaviorElement.SetAttribute("name", behavior.GetName());
if (behavior.IsFolded()) behaviorElement.SetAttribute("isFolded", true);
// Handle Quick Customization info.
behaviorElement.RemoveChild("propertiesQuickCustomizationVisibilities");
const QuickCustomizationVisibilitiesContainer&
propertiesQuickCustomizationVisibilities =
behavior.GetPropertiesQuickCustomizationVisibilities();
if (!propertiesQuickCustomizationVisibilities.IsEmpty()) {
propertiesQuickCustomizationVisibilities.SerializeTo(
behaviorElement.AddChild("propertiesQuickCustomizationVisibilities"));
}
const QuickCustomization::Visibility visibility =
behavior.GetQuickCustomizationVisibility();
if (visibility != QuickCustomization::Visibility::Default) {
behaviorElement.SetAttribute(
"quickCustomizationVisibility",
QuickCustomization::VisibilityAsString(visibility));
}
}
configuration->SerializeTo(element);

View File

@@ -193,7 +193,7 @@ class GD_CORE_API ObjectFolderOrObject {
static gd::ObjectFolderOrObject badObjectFolderOrObject;
gd::ObjectFolderOrObject*
parent; // nullptr if root folder, points to the parent folder otherwise.
parent = nullptr; // nullptr if root folder, points to the parent folder otherwise.
QuickCustomization::Visibility quickCustomizationVisibility;
// Representing an object:

View File

@@ -530,6 +530,32 @@ std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject(
*objectsContainers[0], *objectsContainers[1], objectName, searchInGroups);
}
std::vector<gd::String> ObjectsContainersList::GetBehaviorNamesInObjectOrGroup(
const gd::String &objectOrGroupName, const gd::String &behaviorType, bool searchInGroups) const {
if (objectsContainers.size() > 2) {
// TODO: rework forwarded methods so they can work with any number of
// containers.
gd::LogFatalError(
"ObjectsContainersList::GetBehaviorNamesInObjectOrGroup called with objectsContainers "
"not being exactly 2. This is a logical error and will crash.");
}
if (objectsContainers.size() == 0) {
gd::LogWarning("ObjectsContainersList::GetBehaviorNamesInObjectOrGroup called without any "
"objectsContainer");
std::vector<gd::String> behaviors;
return behaviors;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
return gd::GetBehaviorNamesInObjectOrGroup(emptyObjectsContainer,
*objectsContainers[0], objectOrGroupName, behaviorType,
searchInGroups);
}
return gd::GetBehaviorNamesInObjectOrGroup(
*objectsContainers[0], *objectsContainers[1], objectOrGroupName, behaviorType, searchInGroups);
}
std::vector<gd::String> ObjectsContainersList::GetAnimationNamesOfObject(
const gd::String &objectOrGroupName) const {
std::vector<gd::String> animationNames;

View File

@@ -50,6 +50,11 @@ class GD_CORE_API ObjectsContainersList {
*/
bool HasObjectOrGroupNamed(const gd::String& name) const;
/**
* \brief Check if the specified object exists ignoring groups.
*/
bool HasObjectNamed(const gd::String& name) const;
enum VariableExistence {
DoesNotExist,
Exists,
@@ -128,6 +133,18 @@ class GD_CORE_API ObjectsContainersList {
std::vector<gd::String> GetBehaviorsOfObject(
const gd::String& objectName, bool searchInGroups = true) const;
/**
* \brief Get behaviors of an object/group of a given behavior type.
* \note The behaviors of a group are the behaviors which are found in common
* when looking all the objects of the group.
*
* @return Vector containing names of behaviors
*/
std::vector<gd::String>
GetBehaviorNamesInObjectOrGroup(const gd::String &objectOrGroupName,
const gd::String &behaviorType,
bool searchInGroups = true) const;
/**
* \brief Get the animation names of an object/group.
* \note The animation names of a group are the animation names common to
@@ -188,8 +205,6 @@ class GD_CORE_API ObjectsContainersList {
ObjectsContainersList(){};
private:
bool HasObjectNamed(const gd::String& name) const;
const gd::Object* GetObject(const gd::String& name) const;
bool HasObjectWithVariableNamed(const gd::String& objectName,

View File

@@ -71,7 +71,7 @@ Project::Project()
isPlayableWithKeyboard(false),
isPlayableWithGamepad(false),
isPlayableWithMobile(false),
currentPlatform(NULL),
currentPlatform(nullptr),
gdMajorVersion(gd::VersionWrapper::Major()),
gdMinorVersion(gd::VersionWrapper::Minor()),
gdBuildVersion(gd::VersionWrapper::Build()),
@@ -830,7 +830,7 @@ void Project::UnserializeFrom(const SerializerElement& element) {
eventsFunctionsExtensionsElement.ConsiderAsArrayOf(
"eventsFunctionsExtension");
// First, only unserialize behaviors and objects names.
// As event based objects can contains CustomObject and Custom Object,
// As event based objects can contains custom behaviors and custom objects,
// this allows them to reference EventBasedBehavior and EventBasedObject
// respectively.
for (std::size_t i = 0;
@@ -845,15 +845,17 @@ void Project::UnserializeFrom(const SerializerElement& element) {
newEventsFunctionsExtension.UnserializeExtensionDeclarationFrom(
*this, eventsFunctionsExtensionElement);
}
// Then unserialize functions, behaviors and objects content.
for (std::size_t i = 0;
i < eventsFunctionsExtensionsElement.GetChildrenCount();
++i) {
const SerializerElement& eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(i);
eventsFunctionsExtensions.at(i)->UnserializeExtensionImplementationFrom(
*this, eventsFunctionsExtensionElement);
// Then unserialize functions, behaviors and objects content.
for (gd::String &extensionName :
GetUnserializingOrderExtensionNames(eventsFunctionsExtensionsElement)) {
size_t extensionIndex = GetEventsFunctionsExtensionPosition(extensionName);
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
eventsFunctionsExtensions.at(extensionIndex)
->UnserializeExtensionImplementationFrom(
*this, eventsFunctionsExtensionElement);
}
objectsContainer.GetObjectGroups().UnserializeFrom(
@@ -922,6 +924,83 @@ void Project::UnserializeFrom(const SerializerElement& element) {
}
}
std::vector<gd::String> Project::GetUnserializingOrderExtensionNames(
const gd::SerializerElement &eventsFunctionsExtensionsElement) {
// Some extension have custom objects, which have child objects coming from other extension.
// These child objects must be loaded completely before the parent custom obejct can be unserialized.
// This implies: an order on the extension unserialization (and no cycles).
// At the beginning, everything is yet to be loaded.
std::vector<gd::String> remainingExtensionNames(
eventsFunctionsExtensions.size());
for (std::size_t i = 0; i < eventsFunctionsExtensions.size(); ++i) {
remainingExtensionNames[i] = eventsFunctionsExtensions.at(i)->GetName();
}
// Helper allowing to find if an extension has an object that depends on
// at least one other object from another extension that is not loaded yet.
auto isDependentFromRemainingExtensions =
[&remainingExtensionNames](
const gd::SerializerElement &eventsFunctionsExtensionElement) {
auto &eventsBasedObjectsElement =
eventsFunctionsExtensionElement.GetChild("eventsBasedObjects");
eventsBasedObjectsElement.ConsiderAsArrayOf("eventsBasedObject");
for (std::size_t eventsBasedObjectsIndex = 0;
eventsBasedObjectsIndex <
eventsBasedObjectsElement.GetChildrenCount();
++eventsBasedObjectsIndex) {
auto &objectsElement =
eventsBasedObjectsElement.GetChild(eventsBasedObjectsIndex)
.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");
for (std::size_t objectIndex = 0;
objectIndex < objectsElement.GetChildrenCount(); ++objectIndex) {
const gd::String &objectType =
objectsElement.GetChild(objectIndex).GetStringAttribute("type");
gd::String extensionName =
eventsFunctionsExtensionElement.GetStringAttribute("name");
gd::String usedExtensionName =
gd::PlatformExtension::GetExtensionFromFullObjectType(objectType);
if (usedExtensionName != extensionName &&
std::find(remainingExtensionNames.begin(),
remainingExtensionNames.end(),
usedExtensionName) != remainingExtensionNames.end()) {
return true;
}
}
}
return false;
};
// Find the order of loading so that the extensions are loaded when all the other
// extensions they depend on are already loaded.
std::vector<gd::String> loadOrderExtensionNames;
bool foundAnyExtension = true;
while (foundAnyExtension) {
foundAnyExtension = false;
for (std::size_t i = 0; i < remainingExtensionNames.size(); ++i) {
auto extensionName = remainingExtensionNames[i];
size_t extensionIndex =
GetEventsFunctionsExtensionPosition(extensionName);
const SerializerElement &eventsFunctionsExtensionElement =
eventsFunctionsExtensionsElement.GetChild(extensionIndex);
if (!isDependentFromRemainingExtensions(
eventsFunctionsExtensionElement)) {
loadOrderExtensionNames.push_back(extensionName);
remainingExtensionNames.erase(remainingExtensionNames.begin() + i);
i--;
foundAnyExtension = true;
}
}
}
return loadOrderExtensionNames;
}
void Project::SerializeTo(SerializerElement& element) const {
SerializerElement& versionElement = element.AddChild("gdVersion");
versionElement.SetAttribute("major", gd::VersionWrapper::Major());

View File

@@ -990,16 +990,12 @@ class GD_CORE_API Project {
/**
* \brief return the objects of the project.
*/
gd::ObjectsContainer& GetObjects() {
return objectsContainer;
}
gd::ObjectsContainer& GetObjects() { return objectsContainer; }
/**
* \brief Return the objects of the project.
*/
const gd::ObjectsContainer& GetObjects() const {
return objectsContainer;
}
const gd::ObjectsContainer& GetObjects() const { return objectsContainer; }
///@}
/** \name Identifier names
@@ -1080,32 +1076,43 @@ class GD_CORE_API Project {
*/
void Init(const gd::Project& project);
gd::String name; ///< Game name
gd::String description; ///< Game description
gd::String version; ///< Game version number (used for some exports)
unsigned int windowWidth; ///< Window default width
unsigned int windowHeight; ///< Window default height
int maxFPS; ///< Maximum Frame Per Seconds, -1 for unlimited
unsigned int minFPS; ///< Minimum Frame Per Seconds ( slow down game if FPS
///< are below this number )
bool verticalSync; ///< If true, must activate vertical synchronization.
/**
* @brief Get the project extensions names in the order they have to be unserialized.
*
* Child-objects need the event-based objects they use to be loaded completely
* before they are unserialized.
*/
std::vector<gd::String> GetUnserializingOrderExtensionNames(const gd::SerializerElement &eventsFunctionsExtensionsElement);
gd::String name; ///< Game name
gd::String description; ///< Game description
gd::String version; ///< Game version number (used for some exports)
unsigned int windowWidth = 0; ///< Window default width
unsigned int windowHeight = 0; ///< Window default height
int maxFPS = 0; ///< Maximum Frame Per Seconds, -1 for unlimited
unsigned int minFPS = 0; ///< Minimum Frame Per Seconds ( slow down game if
///< FPS are below this number )
bool verticalSync =
false; ///< If true, must activate vertical synchronization.
gd::String scaleMode;
bool pixelsRounding; ///< If true, the rendering should stop pixel
///< interpolation of rendered objects.
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
///< to the window size at runtime
bool pixelsRounding = false; ///< If true, the rendering should stop pixel
///< interpolation of rendered objects.
bool adaptGameResolutionAtRuntime =
true; ///< Should the game resolution be adapted
///< to the window size at runtime
gd::String
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
///< "adaptWidth", "adaptHeight" or empty
gd::String antialiasingMode;
bool isAntialisingEnabledOnMobile;
bool isAntialisingEnabledOnMobile = false;
gd::String projectUuid; ///< UUID useful to identify the game in online
///< services or database that would require it.
bool useDeprecatedZeroAsDefaultZOrder; ///< If true, objects created from
///< events will have 0 as Z order,
///< instead of the highest Z order
///< found on the layer at the scene
///< startup.
bool useDeprecatedZeroAsDefaultZOrder =
false; ///< If true, objects created from
///< events will have 0 as Z order,
///< instead of the highest Z order
///< found on the layer at the scene
///< startup.
std::vector<std::unique_ptr<gd::Layout> > scenes; ///< List of all scenes
gd::VariablesContainer variables; ///< Initial global variables
gd::ObjectsContainer objectsContainer;
@@ -1118,7 +1125,8 @@ class GD_CORE_API Project {
std::vector<gd::Platform*>
platforms; ///< Pointers to the platforms this project supports.
gd::String firstLayout;
bool useExternalSourceFiles; ///< True if game used external source files.
bool useExternalSourceFiles =
false; ///< True if game used external source files.
std::vector<std::unique_ptr<gd::SourceFile> >
externalSourceFiles; ///< List of external source files used.
gd::String author; ///< Game author name, for publishing purpose.
@@ -1127,35 +1135,40 @@ class GD_CORE_API Project {
std::vector<gd::String>
authorUsernames; ///< Game author usernames, from GDevelop users DB.
std::vector<gd::String> categories; ///< Game categories
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
bool isPlayableWithMobile; ///< The project is playable on a mobile.
gd::String packageName; ///< Game package name
bool isPlayableWithKeyboard =
false; ///< The project is playable with a keyboard.
bool isPlayableWithGamepad =
false; ///< The project is playable with a gamepad.
bool isPlayableWithMobile = false; ///< The project is playable on a mobile.
gd::String packageName; ///< Game package name
gd::String templateSlug; ///< The slug of the template from which the game is
///< created.
gd::String orientation; ///< Lock game orientation (on mobile devices).
///< "default", "landscape" or "portrait".
bool
folderProject; ///< True if folder project, false if single file project.
bool folderProject =
false; ///< True if folder project, false if single file project.
gd::String
projectFile; ///< Path to the project file - when editing a local file.
gd::String latestCompilationDirectory;
gd::Platform*
currentPlatform; ///< The platform being used to edit the project.
gd::Platform* currentPlatform =
nullptr; ///< The platform being used to edit the project.
gd::PlatformSpecificAssets platformSpecificAssets;
gd::LoadingScreen loadingScreen;
gd::Watermark watermark;
std::vector<std::unique_ptr<gd::ExternalEvents> >
externalEvents; ///< List of all externals events
ExtensionProperties
extensionProperties; ///< The properties of the extensions.
extensionProperties; ///< The properties of the extensions.
gd::WholeProjectDiagnosticReport wholeProjectDiagnosticReport;
mutable unsigned int gdMajorVersion; ///< The GD major version used the last
///< time the project was saved.
mutable unsigned int gdMinorVersion; ///< The GD minor version used the last
///< time the project was saved.
mutable unsigned int gdBuildVersion; ///< The GD build version used the last
///< time the project was saved.
mutable unsigned int gdMajorVersion =
0; ///< The GD major version used the last
///< time the project was saved.
mutable unsigned int gdMinorVersion =
0; ///< The GD minor version used the last
///< time the project was saved.
mutable unsigned int gdBuildVersion =
0; ///< The GD build version used the last
///< time the project was saved.
};
} // namespace gd

View File

@@ -1,16 +1,37 @@
#pragma once
#include "GDCore/String.h"
namespace gd {
class QuickCustomization {
public:
enum Visibility {
/** Visibility based on the parent or editor heuristics (probably visible). */
/** Visibility based on the parent or editor heuristics (probably visible).
*/
Default,
/** Visible in the quick customization editor. */
Visible,
/** Not visible in the quick customization editor. */
Hidden
};
static Visibility StringAsVisibility(const gd::String& str) {
if (str == "visible")
return Visibility::Visible;
else if (str == "hidden")
return Visibility::Hidden;
return Visibility::Default;
}
static gd::String VisibilityAsString(Visibility visibility) {
if (visibility == Visibility::Visible)
return "visible";
else if (visibility == Visibility::Hidden)
return "hidden";
return "default";
}
};
} // namespace gd

View File

@@ -0,0 +1,58 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/QuickCustomizationVisibilitiesContainer.h"
#include <algorithm>
#include <iostream>
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/UUID/UUID.h"
using namespace std;
namespace gd {
QuickCustomizationVisibilitiesContainer::
QuickCustomizationVisibilitiesContainer() {}
bool QuickCustomizationVisibilitiesContainer::IsEmpty() const {
return visibilities.empty();
}
void QuickCustomizationVisibilitiesContainer::Set(
const gd::String& name, QuickCustomization::Visibility visibility) {
visibilities[name] = visibility;
}
QuickCustomization::Visibility QuickCustomizationVisibilitiesContainer::Get(
const gd::String& name) const {
auto it = visibilities.find(name);
if (it != visibilities.end()) return it->second;
return QuickCustomization::Visibility::Default;
}
void QuickCustomizationVisibilitiesContainer::SerializeTo(
SerializerElement& element) const {
for (auto& visibility : visibilities) {
element.SetStringAttribute(
visibility.first,
QuickCustomization::VisibilityAsString(visibility.second));
}
}
void QuickCustomizationVisibilitiesContainer::UnserializeFrom(
const SerializerElement& element) {
visibilities.clear();
for (auto& child : element.GetAllChildren()) {
visibilities[child.first] =
QuickCustomization::StringAsVisibility(child.second->GetStringValue());
}
}
} // namespace gd

View File

@@ -0,0 +1,29 @@
#pragma once
#include <memory>
#include <vector>
#include "GDCore/Project/QuickCustomization.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
namespace gd {
class QuickCustomizationVisibilitiesContainer {
public:
QuickCustomizationVisibilitiesContainer();
void Set(const gd::String& name, QuickCustomization::Visibility visibility);
QuickCustomization::Visibility Get(const gd::String& name) const;
bool IsEmpty() const;
void SerializeTo(SerializerElement& element) const;
void UnserializeFrom(const SerializerElement& element);
private:
std::map<gd::String, QuickCustomization::Visibility> visibilities;
};
} // namespace gd

View File

@@ -152,8 +152,8 @@ class GD_CORE_API Resource {
gd::String metadata;
gd::String originName;
gd::String originIdentifier;
bool userAdded; ///< True if the resource was added by the user, and not
///< automatically by GDevelop.
bool userAdded = false; ///< True if the resource was added by the user, and not
///< automatically by GDevelop.
static gd::String badStr;
};

View File

@@ -33,7 +33,7 @@ class GD_CORE_API Variable {
Unknown,
/** Used when objects of a group have different types for a variable. */
MixedTypes,
// Primitive types
String,
Number,
@@ -393,11 +393,11 @@ class GD_CORE_API Variable {
*/
static Type StringAsType(const gd::String& str);
bool folded;
bool folded = false;
mutable Type type;
mutable gd::String str;
mutable double value;
mutable bool boolVal;
mutable bool boolVal = false;
mutable bool hasMixedValues;
mutable std::map<gd::String, std::shared_ptr<Variable>>
children; ///< Children, when the variable is considered as a structure.

View File

@@ -187,7 +187,7 @@ class GD_CORE_API VariablesContainer {
///@}
private:
SourceType sourceType;
SourceType sourceType = Unknown;
std::vector<std::pair<gd::String, std::shared_ptr<gd::Variable>>> variables;
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
///< useful for computing changesets.

View File

@@ -456,13 +456,13 @@ class GD_CORE_API SerializerElement {
*/
void Init(const gd::SerializerElement &other);
bool valueUndefined; ///< If true, the element does not have a value.
bool valueUndefined = true; ///< If true, the element does not have a value.
SerializerValue elementValue;
std::map<gd::String, SerializerValue> attributes;
std::vector<std::pair<gd::String, std::shared_ptr<SerializerElement> > >
children;
mutable bool isArray; ///< true if element is considered as an array
mutable bool isArray = false; ///< true if element is considered as an array
mutable gd::String arrayOf; ///< The name of the children (was useful for XML
///< parsed elements).
mutable gd::String deprecatedArrayOf; ///< Alternate name for children

View File

@@ -0,0 +1,2 @@
# Disable all checks in this folder.
Checks: '-*'

View File

@@ -4,6 +4,8 @@
* This project is released under the MIT License.
*/
// NOLINTBEGIN
#include "GDCore/String.h"
#include <algorithm>
@@ -825,3 +827,5 @@ bool GD_CORE_API CaseInsensitiveEquiv( const String &lhs, const String &rhs, boo
}
}
// NOLINTEND

View File

@@ -4,6 +4,8 @@
* This project is released under the MIT License.
*/
// NOLINTBEGIN
#ifndef GDCORE_UTF8_STRING_H
#define GDCORE_UTF8_STRING_H
@@ -898,3 +900,5 @@ namespace std
* In Unicode, uppercasing/lowercasing strings to compare them in a case-insensitive way is not recommended.
* That's why the function gd::CaseInsensitiveEquiv exists to compare two strings in a case-insensitive way.
*/
// NOLINTEND

View File

@@ -2,6 +2,7 @@
#define GD_CORE_POLYMORPHICCLONE_H
#include <memory>
#include <vector>
namespace gd {

View File

@@ -3,6 +3,7 @@
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include <algorithm>
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"

View File

@@ -3,6 +3,9 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
// NOLINTBEGIN
#include "SystemStats.h"
#include "stdio.h"
#include "stdlib.h"
@@ -49,3 +52,5 @@ size_t SystemStats::GetUsedVirtualMemory() {
}
} // namespace gd
// NOLINTEND

View File

@@ -0,0 +1,2 @@
# Disable all checks in this folder.
Checks: '-*'

View File

@@ -0,0 +1,2 @@
# Disable all checks in this folder.
Checks: '-*'

View File

@@ -262,4 +262,134 @@ TEST_CASE("ObjectSerialization", "[common]") {
auto &behaviorElement = behaviorsElement.GetChild(0);
REQUIRE(behaviorElement.GetStringAttribute("name") == "MyBehavior");
}
}
// Event-based object dependency cycles are not tested because they are forbidden by the editor.
SECTION("Save and load a project with custom object dependencies from different extensions") {
gd::Platform platform;
gd::Project writtenProject;
SetupProjectWithDummyPlatform(writtenProject, platform);
{
// The extension with the dependency is added first to make the
// implementation change the order in which extensions are loaded.
auto &eventsExtensionWithDependency =
writtenProject.InsertNewEventsFunctionsExtension(
"MyEventsExtensionWithDependency", 0);
auto &eventsExtension = writtenProject.InsertNewEventsFunctionsExtension(
"MyEventsExtension", 1);
{
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObject", 0);
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
writtenProject, "MyExtension::Sprite", "MyChildSprite", 0);
}
// An event-based object with a custom object child that overrides its
// configuration.
{
auto &eventsBasedObject =
eventsExtensionWithDependency.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObjectWithDependency", 0);
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
writtenProject, "MyEventsExtension::MyEventsBasedObject",
"MyChildCustomObject", 0);
auto *customObjectConfiguration =
dynamic_cast<gd::CustomObjectConfiguration *>(
&childObject.GetConfiguration());
auto &spriteConfiguration =
customObjectConfiguration->GetChildObjectConfiguration(
"MyChildSprite");
SetupSpriteConfiguration(spriteConfiguration);
}
}
SerializerElement projectElement;
writtenProject.SerializeTo(projectElement);
gd::Project readProject;
readProject.AddPlatform(platform);
readProject.UnserializeFrom(projectElement);
REQUIRE(readProject.GetEventsFunctionsExtensionsCount() == 2);
auto &eventsExtensionWithDependency =
readProject.GetEventsFunctionsExtension(0);
REQUIRE(eventsExtensionWithDependency.GetEventsBasedObjects().GetCount() ==
1);
auto &eventsBasedObject =
eventsExtensionWithDependency.GetEventsBasedObjects().Get(0);
REQUIRE(eventsBasedObject.GetObjects().GetObjectsCount() == 1);
auto &childObject = eventsBasedObject.GetObjects().GetObject(0);
REQUIRE(childObject.GetName() == "MyChildCustomObject");
REQUIRE(childObject.GetType() == "MyEventsExtension::MyEventsBasedObject");
auto *customObjectConfiguration =
dynamic_cast<gd::CustomObjectConfiguration *>(
&childObject.GetConfiguration());
REQUIRE(customObjectConfiguration != nullptr);
auto &spriteConfiguration =
customObjectConfiguration->GetChildObjectConfiguration("MyChildSprite");
CheckSpriteConfiguration(spriteConfiguration);
}
SECTION("Save and load a project with custom object dependencies inside an extension") {
gd::Platform platform;
gd::Project writtenProject;
SetupProjectWithDummyPlatform(writtenProject, platform);
{
auto &eventsExtension = writtenProject.InsertNewEventsFunctionsExtension(
"MyEventsExtension", 0);
{
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObject", 0);
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
writtenProject, "MyExtension::Sprite", "MyChildSprite", 0);
}
// An event-based object with a custom object child that overrides its
// configuration.
// The extension with the dependency is added first to make the
// implementation change the order in which extensions are loaded.
{
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().InsertNew(
"MyEventsBasedObjectWithDependency", 0);
auto &childObject = eventsBasedObject.GetObjects().InsertNewObject(
writtenProject, "MyEventsExtension::MyEventsBasedObject",
"MyChildCustomObject", 0);
auto *customObjectConfiguration =
dynamic_cast<gd::CustomObjectConfiguration *>(
&childObject.GetConfiguration());
auto &spriteConfiguration =
customObjectConfiguration->GetChildObjectConfiguration(
"MyChildSprite");
SetupSpriteConfiguration(spriteConfiguration);
}
}
SerializerElement projectElement;
writtenProject.SerializeTo(projectElement);
gd::Project readProject;
readProject.AddPlatform(platform);
readProject.UnserializeFrom(projectElement);
REQUIRE(readProject.GetEventsFunctionsExtensionsCount() == 1);
auto &eventsExtension =
readProject.GetEventsFunctionsExtension(0);
REQUIRE(eventsExtension.GetEventsBasedObjects().GetCount() ==
2);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get(0);
REQUIRE(eventsBasedObject.GetObjects().GetObjectsCount() == 1);
auto &childObject = eventsBasedObject.GetObjects().GetObject(0);
REQUIRE(childObject.GetName() == "MyChildCustomObject");
REQUIRE(childObject.GetType() == "MyEventsExtension::MyEventsBasedObject");
auto *customObjectConfiguration =
dynamic_cast<gd::CustomObjectConfiguration *>(
&childObject.GetConfiguration());
REQUIRE(customObjectConfiguration != nullptr);
auto &spriteConfiguration =
customObjectConfiguration->GetChildObjectConfiguration("MyChildSprite");
CheckSpriteConfiguration(spriteConfiguration);
}
}

View File

@@ -1992,7 +1992,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
// A behavior is copied from one extension to another.
// An events-based behavior is copied from one extension to another.
auto &destinationExtension =
project.InsertNewEventsFunctionsExtension("DestinationExtension", 0);
@@ -2001,21 +2001,19 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// instruction keeps pointing to the old extension.
destinationExtension.InsertNewEventsFunction("MyEventsFunction", 0);
auto &copiedBehavior =
auto &duplicatedBehavior =
destinationExtension.GetEventsBasedBehaviors().InsertNew(
"MyOtherEventsBasedBehavior", 0);
copiedBehavior.SetFullName("My events based behavior");
copiedBehavior.SetDescription("An events based behavior for test");
copiedBehavior.SetObjectType("MyEventsExtension::MyEventsBasedObject");
duplicatedBehavior.SetObjectType("MyEventsExtension::MyEventsBasedObject");
// Add the copied events.
auto &behaviorEventsFunctions = copiedBehavior.GetEventsFunctions();
auto &behaviorAction = behaviorEventsFunctions.InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
auto &eventsFunctions = duplicatedBehavior.GetEventsFunctions();
auto &behaviorAction = eventsFunctions.InsertNewEventsFunction(
"MyObjectEventsFunction", 0);
SetupEvents(behaviorAction.GetEvents());
gd::WholeProjectRefactorer::UpdateExtensionNameInEventsBasedBehavior(
project, destinationExtension, copiedBehavior, "MyEventsExtension");
project, destinationExtension, duplicatedBehavior, "MyEventsExtension");
// Check that events function calls in instructions have been renamed
REQUIRE(GetEventFirstActionType(behaviorAction.GetEvents().GetEvent(
@@ -2023,13 +2021,123 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in instructions have NOT been renamed
// outside of the copied behavior.
// outside of the copied events-based behavior.
REQUIRE(
GetEventFirstActionType(eventsList->GetEvent(FreeFunctionAction)) ==
"MyEventsExtension::MyEventsFunction");
}
}
SECTION("Events extension renamed in instructions scoped to one object") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
// An events-based object is copied from one extension to another.
auto &destinationExtension =
project.InsertNewEventsFunctionsExtension("DestinationExtension", 0);
// Add the function used by the instruction that is checked in this test.
// When the function doesn't exist the destination extension, the
// instruction keeps pointing to the old extension.
destinationExtension.InsertNewEventsFunction("MyEventsFunction", 0);
auto &duplicatedObject =
destinationExtension.GetEventsBasedObjects().InsertNew(
"MyDuplicatedEventsBasedObject", 0);
// Add the copied events.
auto &eventsFunctions = duplicatedObject.GetEventsFunctions();
auto &objectAction = eventsFunctions.InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
SetupEvents(objectAction.GetEvents());
gd::WholeProjectRefactorer::UpdateExtensionNameInEventsBasedObject(
project, destinationExtension, duplicatedObject, "MyEventsExtension");
// Check that events function calls in instructions have been renamed
REQUIRE(GetEventFirstActionType(objectAction.GetEvents().GetEvent(
FreeFunctionAction)) == "DestinationExtension::MyEventsFunction");
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in instructions have NOT been renamed
// outside of the copied events-based object.
REQUIRE(
GetEventFirstActionType(eventsList->GetEvent(FreeFunctionAction)) ==
"MyEventsExtension::MyEventsFunction");
}
}
SECTION("Events-based behavior renamed in instructions scoped to one behavior") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
// An events-based behavior is duplicated in the same extension.
auto &duplicatedBehavior =
eventsExtension.GetEventsBasedBehaviors().InsertNew(
"MyDuplicatedEventsBasedBehavior", 0);
duplicatedBehavior.SetObjectType("MyEventsExtension::MyEventsBasedObject");
// Add the copied events.
auto &eventsFunctions = duplicatedBehavior.GetEventsFunctions();
auto &behaviorAction = eventsFunctions.InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
SetupEvents(behaviorAction.GetEvents());
gd::WholeProjectRefactorer::UpdateBehaviorNameInEventsBasedBehavior(
project, eventsExtension, duplicatedBehavior, "MyEventsBasedBehavior");
// Check that events function calls in instructions have been renamed
REQUIRE(GetEventFirstActionType(
behaviorAction.GetEvents().GetEvent(BehaviorAction)) ==
"MyEventsExtension::MyDuplicatedEventsBasedBehavior::MyBehaviorEventsFunction");
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in instructions have NOT been renamed
// outside of the duplicated events-based behavior.
REQUIRE(
GetEventFirstActionType(eventsList->GetEvent(BehaviorAction)) ==
"MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunction");
}
}
SECTION("Events-based object renamed in instructions scoped to one object") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
// An events-based object is duplicated in the same extension.
auto &duplicatedObject =
eventsExtension.GetEventsBasedObjects().InsertNew(
"MyDuplicatedEventsBasedObject", 0);
// Add the copied events.
auto &eventsFunctions = duplicatedObject.GetEventsFunctions();
auto &objectAction = eventsFunctions.InsertNewEventsFunction(
"MyObjectEventsFunction", 0);
SetupEvents(objectAction.GetEvents());
gd::WholeProjectRefactorer::UpdateObjectNameInEventsBasedObject(
project, eventsExtension, duplicatedObject, "MyEventsBasedObject");
// Check that events function calls in instructions have been renamed
REQUIRE(GetEventFirstActionType(
objectAction.GetEvents().GetEvent(ObjectAction)) ==
"MyEventsExtension::MyDuplicatedEventsBasedObject::MyObjectEventsFunction");
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in instructions have NOT been renamed
// outside of the duplicated events-based object.
REQUIRE(
GetEventFirstActionType(eventsList->GetEvent(ObjectAction)) ==
"MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunction");
}
}
SECTION("Events extension renamed in parameters") {
gd::Project project;
gd::Platform platform;

View File

@@ -73,9 +73,7 @@ namespace gdjs {
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.light.color.setHex(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
);
this.light.color.setHex(gdjs.rgbOrHexStringToNumber(value));
}
}
updateColorParameter(parameterName: string, value: number): void {

View File

@@ -94,7 +94,7 @@ namespace gdjs {
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.light.color = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
gdjs.rgbOrHexStringToNumber(value)
);
}
if (parameterName === 'top') {

View File

@@ -71,7 +71,7 @@ namespace gdjs {
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.fog.color = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
gdjs.rgbOrHexStringToNumber(value)
);
}
}

View File

@@ -95,12 +95,12 @@ namespace gdjs {
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'skyColor') {
this.light.color = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
gdjs.rgbOrHexStringToNumber(value)
);
}
if (parameterName === 'groundColor') {
this.light.groundColor = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
gdjs.rgbOrHexStringToNumber(value)
);
}
if (parameterName === 'top') {

View File

@@ -803,11 +803,8 @@ module.exports = {
}
const Cube3DObject = new gd.ObjectJsImplementation();
Cube3DObject.updateProperty = function (
objectContent,
propertyName,
newValue
) {
Cube3DObject.updateProperty = function (propertyName, newValue) {
const objectContent = this.content;
if (
propertyName === 'width' ||
propertyName === 'height' ||
@@ -851,8 +848,9 @@ module.exports = {
return false;
};
Cube3DObject.getProperties = function (objectContent) {
Cube3DObject.getProperties = function () {
const objectProperties = new gd.MapStringPropertyDescriptor();
const objectContent = this.content;
objectProperties
.getOrCreate('enableTextureTransparency')
@@ -878,7 +876,8 @@ module.exports = {
'The top of each image can touch the **top face** (Y) or the **front face** (Z).'
)
)
.setGroup(_('Face orientation'));
.setGroup(_('Face orientation'))
.setAdvanced(true);
objectProperties
.getOrCreate('width')
@@ -909,7 +908,7 @@ module.exports = {
.setValue(objectContent.frontFaceResourceName || '')
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Front face image'))
.setLabel(_('Front face'))
.setGroup(_('Textures'));
objectProperties
@@ -917,7 +916,7 @@ module.exports = {
.setValue(objectContent.backFaceResourceName || '')
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Back face image'))
.setLabel(_('Back face'))
.setGroup(_('Textures'));
objectProperties
@@ -932,14 +931,15 @@ module.exports = {
'The top of the image can touch the **top face** (Y) or the **bottom face** (X).'
)
)
.setGroup(_('Textures'));
.setGroup(_('Face orientation'))
.setAdvanced(true);
objectProperties
.getOrCreate('leftFaceResourceName')
.setValue(objectContent.leftFaceResourceName || '')
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Left face image'))
.setLabel(_('Left face'))
.setGroup(_('Textures'));
objectProperties
@@ -947,7 +947,7 @@ module.exports = {
.setValue(objectContent.rightFaceResourceName || '')
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Right face image'))
.setLabel(_('Right face'))
.setGroup(_('Textures'));
objectProperties
@@ -955,7 +955,7 @@ module.exports = {
.setValue(objectContent.topFaceResourceName || '')
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Top face image'))
.setLabel(_('Top face'))
.setGroup(_('Textures'));
objectProperties
@@ -963,92 +963,98 @@ module.exports = {
.setValue(objectContent.bottomFaceResourceName || '')
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Bottom face image'))
.setLabel(_('Bottom face'))
.setGroup(_('Textures'));
objectProperties
.getOrCreate('frontFaceResourceRepeat')
.setValue(objectContent.frontFaceResourceRepeat ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Tile front face image'))
.setLabel(_('Tile'))
.setGroup(_('Textures'));
objectProperties
.getOrCreate('backFaceResourceRepeat')
.setValue(objectContent.backFaceResourceRepeat ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Tile back face image'))
.setLabel(_('Tile'))
.setGroup(_('Textures'));
objectProperties
.getOrCreate('leftFaceResourceRepeat')
.setValue(objectContent.leftFaceResourceRepeat ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Tile left face image'))
.setLabel(_('Tile'))
.setGroup(_('Textures'));
objectProperties
.getOrCreate('rightFaceResourceRepeat')
.setValue(objectContent.rightFaceResourceRepeat ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Tile right face image'))
.setLabel(_('Tile'))
.setGroup(_('Textures'));
objectProperties
.getOrCreate('topFaceResourceRepeat')
.setValue(objectContent.topFaceResourceRepeat ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Tile top face image'))
.setLabel(_('Tile'))
.setGroup(_('Textures'));
objectProperties
.getOrCreate('bottomFaceResourceRepeat')
.setValue(objectContent.bottomFaceResourceRepeat ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Tile bottom face image'))
.setLabel(_('Tile'))
.setGroup(_('Textures'));
objectProperties
.getOrCreate('frontFaceVisible')
.setValue(objectContent.frontFaceVisible ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Show front face'))
.setGroup(_('Face visibility'));
.setLabel(_('Front face'))
.setGroup(_('Face visibility'))
.setAdvanced(true);
objectProperties
.getOrCreate('backFaceVisible')
.setValue(objectContent.backFaceVisible ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Show back face'))
.setGroup(_('Face visibility'));
.setLabel(_('Back face'))
.setGroup(_('Face visibility'))
.setAdvanced(true);
objectProperties
.getOrCreate('leftFaceVisible')
.setValue(objectContent.leftFaceVisible ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Show left face'))
.setGroup(_('Face visibility'));
.setLabel(_('Left face'))
.setGroup(_('Face visibility'))
.setAdvanced(true);
objectProperties
.getOrCreate('rightFaceVisible')
.setValue(objectContent.rightFaceVisible ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Show right face'))
.setGroup(_('Face visibility'));
.setLabel(_('Right face'))
.setGroup(_('Face visibility'))
.setAdvanced(true);
objectProperties
.getOrCreate('topFaceVisible')
.setValue(objectContent.topFaceVisible ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Show top face'))
.setGroup(_('Face visibility'));
.setLabel(_('Top face'))
.setGroup(_('Face visibility'))
.setAdvanced(true);
objectProperties
.getOrCreate('bottomFaceVisible')
.setValue(objectContent.bottomFaceVisible ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Show bottom face'))
.setGroup(_('Face visibility'));
.setLabel(_('Bottom face'))
.setGroup(_('Face visibility'))
.setAdvanced(true);
objectProperties
.getOrCreate('materialType')
@@ -1060,38 +1066,35 @@ module.exports = {
return objectProperties;
};
Cube3DObject.setRawJSONContent(
JSON.stringify({
width: 100,
height: 100,
depth: 100,
enableTextureTransparency: false,
facesOrientation: 'Y',
frontFaceResourceName: '',
backFaceResourceName: '',
backFaceUpThroughWhichAxisRotation: 'X',
leftFaceResourceName: '',
rightFaceResourceName: '',
topFaceResourceName: '',
bottomFaceResourceName: '',
frontFaceVisible: true,
backFaceVisible: false,
leftFaceVisible: true,
rightFaceVisible: true,
topFaceVisible: true,
bottomFaceVisible: true,
frontFaceResourceRepeat: false,
backFaceResourceRepeat: false,
leftFaceResourceRepeat: false,
rightFaceResourceRepeat: false,
topFaceResourceRepeat: false,
bottomFaceResourceRepeat: false,
materialType: 'Basic',
})
);
Cube3DObject.content = {
width: 100,
height: 100,
depth: 100,
enableTextureTransparency: false,
facesOrientation: 'Y',
frontFaceResourceName: '',
backFaceResourceName: '',
backFaceUpThroughWhichAxisRotation: 'X',
leftFaceResourceName: '',
rightFaceResourceName: '',
topFaceResourceName: '',
bottomFaceResourceName: '',
frontFaceVisible: true,
backFaceVisible: false,
leftFaceVisible: true,
rightFaceVisible: true,
topFaceVisible: true,
bottomFaceVisible: true,
frontFaceResourceRepeat: false,
backFaceResourceRepeat: false,
leftFaceResourceRepeat: false,
rightFaceResourceRepeat: false,
topFaceResourceRepeat: false,
bottomFaceResourceRepeat: false,
materialType: 'Basic',
};
Cube3DObject.updateInitialInstanceProperty = function (
objectContent,
instance,
propertyName,
newValue
@@ -1099,7 +1102,7 @@ module.exports = {
return false;
};
Cube3DObject.getInitialInstanceProperties = function (content, instance) {
Cube3DObject.getInitialInstanceProperties = function (instance) {
const instanceProperties = new gd.MapStringPropertyDescriptor();
return instanceProperties;
};
@@ -2060,7 +2063,10 @@ module.exports = {
};
const getFirstVisibleFaceResourceName = (objectConfiguration) => {
const properties = objectConfiguration.getProperties();
const object = gd.castObject(
objectConfiguration,
gd.ObjectJsImplementation
);
const orderedFaces = [
['frontFaceVisible', 'frontFaceResourceName'],
@@ -2075,10 +2081,8 @@ module.exports = {
faceVisibleProperty,
faceResourceNameProperty,
] of orderedFaces) {
if (properties.get(faceVisibleProperty).getValue() === 'true') {
const textureResource = properties
.get(faceResourceNameProperty)
.getValue();
if (object.content[faceVisibleProperty]) {
const textureResource = object.content[faceResourceNameProperty];
if (textureResource) return textureResource;
}
}
@@ -2124,10 +2128,14 @@ module.exports = {
// Name of the resource that is rendered.
// If no face is visible, this will be null.
this._renderedResourceName = undefined;
const properties = associatedObjectConfiguration.getProperties();
this._defaultWidth = parseFloat(properties.get('width').getValue());
this._defaultHeight = parseFloat(properties.get('height').getValue());
this._defaultDepth = parseFloat(properties.get('depth').getValue());
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.ObjectJsImplementation
);
this._defaultWidth = object.content.width;
this._defaultHeight = object.content.height;
this._defaultDepth = object.content.depth;
this._pixiObject = new PIXI.Container();
this._pixiFallbackObject = new PIXI.Graphics();
@@ -2195,6 +2203,8 @@ module.exports = {
if (!texture.baseTexture.valid) {
// Post pone texture update if texture is not loaded.
texture.once('update', () => {
if (this._wasDestroyed) return;
this.updateTexture();
this.updatePIXISprite();
});
@@ -2315,70 +2325,68 @@ module.exports = {
pixiResourcesLoader
);
const properties = associatedObjectConfiguration.getProperties();
this._defaultWidth = parseFloat(properties.get('width').getValue());
this._defaultHeight = parseFloat(properties.get('height').getValue());
this._defaultDepth = parseFloat(properties.get('depth').getValue());
this._defaultWidth = 1;
this._defaultHeight = 1;
this._defaultDepth = 1;
this._pixiObject = new PIXI.Graphics();
this._pixiContainer.addChild(this._pixiObject);
this._faceResourceNames = [
properties.get('frontFaceResourceName').getValue(),
properties.get('backFaceResourceName').getValue(),
properties.get('leftFaceResourceName').getValue(),
properties.get('rightFaceResourceName').getValue(),
properties.get('topFaceResourceName').getValue(),
properties.get('bottomFaceResourceName').getValue(),
];
this._faceVisibilities = [
properties.get('frontFaceVisible').getValue() === 'true',
properties.get('backFaceVisible').getValue() === 'true',
properties.get('leftFaceVisible').getValue() === 'true',
properties.get('rightFaceVisible').getValue() === 'true',
properties.get('topFaceVisible').getValue() === 'true',
properties.get('bottomFaceVisible').getValue() === 'true',
];
this._shouldRepeatTextureOnFace = [
properties.get('frontFaceResourceRepeat').getValue() === 'true',
properties.get('backFaceResourceRepeat').getValue() === 'true',
properties.get('leftFaceResourceRepeat').getValue() === 'true',
properties.get('rightFaceResourceRepeat').getValue() === 'true',
properties.get('topFaceResourceRepeat').getValue() === 'true',
properties.get('bottomFaceResourceRepeat').getValue() === 'true',
];
this._facesOrientation = properties.get('facesOrientation').getValue();
this._backFaceUpThroughWhichAxisRotation = properties
.get('backFaceUpThroughWhichAxisRotation')
.getValue();
this._shouldUseTransparentTexture =
properties.get('enableTextureTransparency').getValue() === 'true';
this._faceResourceNames = ['', '', '', '', '', ''];
this._faceVisibilities = [true, true, true, true, true, true];
this._shouldRepeatTextureOnFace = [true, true, true, true, true, true];
this._facesOrientation = 'Y';
this._backFaceUpThroughWhichAxisRotation = 'X';
this._shouldUseTransparentTexture = false;
const geometry = new THREE.BoxGeometry(1, 1, 1);
const materials = [
this._getFaceMaterial(project, materialIndexToFaceIndex[0]),
this._getFaceMaterial(project, materialIndexToFaceIndex[1]),
this._getFaceMaterial(project, materialIndexToFaceIndex[2]),
this._getFaceMaterial(project, materialIndexToFaceIndex[3]),
this._getFaceMaterial(project, materialIndexToFaceIndex[4]),
this._getFaceMaterial(project, materialIndexToFaceIndex[5]),
getTransparentMaterial(),
getTransparentMaterial(),
getTransparentMaterial(),
getTransparentMaterial(),
getTransparentMaterial(),
getTransparentMaterial(),
];
this._threeObject = new THREE.Mesh(geometry, materials);
this._threeObject.rotation.order = 'ZYX';
this._threeGroup.add(this._threeObject);
this.updateThreeObject();
}
_getFaceMaterial(project, faceIndex) {
if (!this._faceVisibilities[faceIndex]) return getTransparentMaterial();
async _updateThreeObjectMaterials() {
const getFaceMaterial = async (project, faceIndex) => {
if (!this._faceVisibilities[faceIndex])
return getTransparentMaterial();
return this._pixiResourcesLoader.getThreeMaterial(
project,
this._faceResourceNames[faceIndex],
{
useTransparentTexture: this._shouldUseTransparentTexture,
}
);
return await this._pixiResourcesLoader.getThreeMaterial(
project,
this._faceResourceNames[faceIndex],
{
useTransparentTexture: this._shouldUseTransparentTexture,
}
);
};
const materials = await Promise.all([
getFaceMaterial(this._project, materialIndexToFaceIndex[0]),
getFaceMaterial(this._project, materialIndexToFaceIndex[1]),
getFaceMaterial(this._project, materialIndexToFaceIndex[2]),
getFaceMaterial(this._project, materialIndexToFaceIndex[3]),
getFaceMaterial(this._project, materialIndexToFaceIndex[4]),
getFaceMaterial(this._project, materialIndexToFaceIndex[5]),
]);
if (this._wasDestroyed) return;
this._threeObject.material[0] = materials[0];
this._threeObject.material[1] = materials[1];
this._threeObject.material[2] = materials[2];
this._threeObject.material[3] = materials[3];
this._threeObject.material[4] = materials[4];
this._threeObject.material[5] = materials[5];
this._updateTextureUvMapping();
}
static _getResourceNameToDisplay(objectConfiguration) {
@@ -2386,6 +2394,15 @@ module.exports = {
}
updateThreeObject() {
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.ObjectJsImplementation
);
this._defaultWidth = object.content.width;
this._defaultHeight = object.content.height;
this._defaultDepth = object.content.depth;
const width = this.getWidth();
const height = this.getHeight();
const depth = this.getDepth();
@@ -2402,18 +2419,107 @@ module.exports = {
RenderedInstance.toRad(this._instance.getAngle())
);
let materialsDirty = false;
let uvMappingDirty = false;
const shouldUseTransparentTexture =
object.content.enableTextureTransparency;
if (this._shouldUseTransparentTexture !== shouldUseTransparentTexture) {
this._shouldUseTransparentTexture = shouldUseTransparentTexture;
materialsDirty = true;
}
const faceResourceNames = [
object.content.frontFaceResourceName,
object.content.backFaceResourceName,
object.content.leftFaceResourceName,
object.content.rightFaceResourceName,
object.content.topFaceResourceName,
object.content.bottomFaceResourceName,
];
if (
this._faceResourceNames[0] !== faceResourceNames[0] ||
this._faceResourceNames[1] !== faceResourceNames[1] ||
this._faceResourceNames[2] !== faceResourceNames[2] ||
this._faceResourceNames[3] !== faceResourceNames[3] ||
this._faceResourceNames[4] !== faceResourceNames[4] ||
this._faceResourceNames[5] !== faceResourceNames[5]
) {
this._faceResourceNames = faceResourceNames;
materialsDirty = true;
}
const faceVisibilities = [
object.content.frontFaceVisible,
object.content.backFaceVisible,
object.content.leftFaceVisible,
object.content.rightFaceVisible,
object.content.topFaceVisible,
object.content.bottomFaceVisible,
];
if (
this._faceVisibilities[0] !== faceVisibilities[0] ||
this._faceVisibilities[1] !== faceVisibilities[1] ||
this._faceVisibilities[2] !== faceVisibilities[2] ||
this._faceVisibilities[3] !== faceVisibilities[3] ||
this._faceVisibilities[4] !== faceVisibilities[4] ||
this._faceVisibilities[5] !== faceVisibilities[5]
) {
this._faceVisibilities = faceVisibilities;
materialsDirty = true;
uvMappingDirty = true;
}
const shouldRepeatTextureOnFace = [
object.content.frontFaceResourceRepeat,
object.content.backFaceResourceRepeat,
object.content.leftFaceResourceRepeat,
object.content.rightFaceResourceRepeat,
object.content.topFaceResourceRepeat,
object.content.bottomFaceResourceRepeat,
];
if (
this._shouldRepeatTextureOnFace[0] !== shouldRepeatTextureOnFace[0] ||
this._shouldRepeatTextureOnFace[1] !== shouldRepeatTextureOnFace[1] ||
this._shouldRepeatTextureOnFace[2] !== shouldRepeatTextureOnFace[2] ||
this._shouldRepeatTextureOnFace[3] !== shouldRepeatTextureOnFace[3] ||
this._shouldRepeatTextureOnFace[4] !== shouldRepeatTextureOnFace[4] ||
this._shouldRepeatTextureOnFace[5] !== shouldRepeatTextureOnFace[5]
) {
this._shouldRepeatTextureOnFace = shouldRepeatTextureOnFace;
uvMappingDirty = true;
}
const backFaceUpThroughWhichAxisRotation =
object.content.backFaceUpThroughWhichAxisRotation;
if (
backFaceUpThroughWhichAxisRotation !==
this._backFaceUpThroughWhichAxisRotation
) {
this._backFaceUpThroughWhichAxisRotation = backFaceUpThroughWhichAxisRotation;
uvMappingDirty = true;
}
const facesOrientation = object.content.facesOrientation;
if (facesOrientation !== this._facesOrientation) {
this._facesOrientation = facesOrientation;
uvMappingDirty = true;
}
const scaleX = width * (this._instance.isFlippedX() ? -1 : 1);
const scaleY = height * (this._instance.isFlippedY() ? -1 : 1);
const scaleZ = depth * (this._instance.isFlippedZ() ? -1 : 1);
if (
scaleX !== this._threeObject.scale.width ||
scaleY !== this._threeObject.scale.height ||
scaleZ !== this._threeObject.scale.depth
scaleX !== this._threeObject.scale.x ||
scaleY !== this._threeObject.scale.y ||
scaleZ !== this._threeObject.scale.z
) {
this._threeObject.scale.set(scaleX, scaleY, scaleZ);
this.updateTextureUvMapping();
uvMappingDirty = true;
}
if (materialsDirty) this._updateThreeObjectMaterials();
if (uvMappingDirty) this._updateTextureUvMapping();
}
/**
@@ -2422,7 +2528,7 @@ module.exports = {
* The mesh must be configured with a list of materials in order
* for the method to work.
*/
updateTextureUvMapping() {
_updateTextureUvMapping() {
// @ts-ignore - position is stored as a Float32BufferAttribute
/** @type {THREE.BufferAttribute} */
const pos = this._threeObject.geometry.getAttribute('position');
@@ -2696,25 +2802,23 @@ module.exports = {
pixiContainer,
pixiResourcesLoader
);
const properties = associatedObjectConfiguration.getProperties();
this._defaultWidth = parseFloat(properties.get('width').getValue());
this._defaultHeight = parseFloat(properties.get('height').getValue());
this._defaultDepth = parseFloat(properties.get('depth').getValue());
const rotationX = parseFloat(properties.get('rotationX').getValue());
const rotationY = parseFloat(properties.get('rotationY').getValue());
const rotationZ = parseFloat(properties.get('rotationZ').getValue());
const keepAspectRatio =
properties.get('keepAspectRatio').getValue() === 'true';
const modelResourceName = properties
.get('modelResourceName')
.getValue();
this._originPoint = getPointForLocation(
properties.get('originLocation').getValue()
);
this._centerPoint = getPointForLocation(
properties.get('centerLocation').getValue()
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.Model3DObjectConfiguration
);
this._defaultWidth = object.getWidth();
this._defaultHeight = object.getHeight();
this._defaultDepth = object.getDepth();
const rotationX = object.getRotationX();
const rotationY = object.getRotationY();
const rotationZ = object.getRotationZ();
const keepAspectRatio = object.shouldKeepAspectRatio();
const modelResourceName = object.getModelResourceName();
this._originPoint = getPointForLocation(object.getOriginLocation());
this._centerPoint = getPointForLocation(object.getCenterLocation());
// This renderer shows a placeholder for the object:
this._pixiObject = new PIXI.Graphics();
this._pixiContainer.addChild(this._pixiObject);
@@ -2722,6 +2826,7 @@ module.exports = {
this._pixiResourcesLoader
.get3DModel(project, modelResourceName)
.then((model3d) => {
if (this._wasDestroyed) return;
const clonedModel3D = THREE_ADDONS.SkeletonUtils.clone(
model3d.scene
);
@@ -2923,6 +3028,17 @@ module.exports = {
}
}
const isSamePoint = (point1, point2) => {
if (!point1 && !point2) return true;
if (point1 && !point2) return false;
if (!point1 && point2) return false;
return (
point1[0] === point2[0] &&
point1[1] === point2[1] &&
point1[2] === point2[2]
);
};
const getPointForLocation = (location) => {
switch (location) {
case 'ModelOrigin':
@@ -2959,24 +3075,20 @@ module.exports = {
threeGroup,
pixiResourcesLoader
);
const properties = associatedObjectConfiguration.getProperties();
this._defaultWidth = parseFloat(properties.get('width').getValue());
this._defaultHeight = parseFloat(properties.get('height').getValue());
this._defaultDepth = parseFloat(properties.get('depth').getValue());
const rotationX = parseFloat(properties.get('rotationX').getValue());
const rotationY = parseFloat(properties.get('rotationY').getValue());
const rotationZ = parseFloat(properties.get('rotationZ').getValue());
const keepAspectRatio =
properties.get('keepAspectRatio').getValue() === 'true';
const modelResourceName = properties
.get('modelResourceName')
.getValue();
this._originPoint = getPointForLocation(
properties.get('originLocation').getValue()
);
this._centerPoint = getPointForLocation(
properties.get('centerLocation').getValue()
);
this._defaultWidth = 1;
this._defaultHeight = 1;
this._defaultDepth = 1;
this._originalWidth = 1;
this._originalHeight = 1;
this._originalDepth = 1;
this._rotationX = 0;
this._rotationY = 0;
this._rotationZ = 0;
this._keepAspectRatio = false;
this._originPoint = null;
this._centerPoint = null;
this._pixiObject = new PIXI.Graphics();
this._pixiContainer.addChild(this._pixiObject);
@@ -2985,28 +3097,8 @@ module.exports = {
this._threeObject.rotation.order = 'ZYX';
this._threeGroup.add(this._threeObject);
this._pixiResourcesLoader
.get3DModel(project, modelResourceName)
.then((model3d) => {
const clonedModel3D = THREE_ADDONS.SkeletonUtils.clone(
model3d.scene
);
// This group hold the rotation defined by properties.
const threeObject = new THREE.Group();
threeObject.rotation.order = 'ZYX';
threeObject.add(clonedModel3D);
this._updateDefaultTransformation(
threeObject,
rotationX,
rotationY,
rotationZ,
this._defaultWidth,
this._defaultHeight,
this._defaultDepth,
keepAspectRatio
);
this._threeObject.add(threeObject);
});
this._threeModelGroup = null;
this._clonedModel3D = null;
}
getOriginX() {
@@ -3047,23 +3139,28 @@ module.exports = {
return this._centerPoint || this._modelOriginPoint;
}
_updateDefaultTransformation(
threeObject,
rotationX,
rotationY,
rotationZ,
originalWidth,
originalHeight,
originalDepth,
keepAspectRatio
) {
threeObject.rotation.set(
(rotationX * Math.PI) / 180,
(rotationY * Math.PI) / 180,
(rotationZ * Math.PI) / 180
_updateDefaultTransformation() {
if (!this._clonedModel3D) return; // Model is not ready - nothing to do.
if (this._threeModelGroup) {
// Remove any previous container as we will recreate it just below
this._threeObject.clear();
}
// This group hold the rotation defined by properties.
// Always restart from a new group to avoid miscomputing bounding boxes/sizes.
this._threeModelGroup = new THREE.Group();
this._threeModelGroup.rotation.order = 'ZYX';
this._threeModelGroup.add(this._clonedModel3D);
this._threeModelGroup.rotation.set(
(this._rotationX * Math.PI) / 180,
(this._rotationY * Math.PI) / 180,
(this._rotationZ * Math.PI) / 180
);
this._threeModelGroup.updateMatrixWorld(true);
const boundingBox = new THREE.Box3().setFromObject(
this._threeModelGroup
);
threeObject.updateMatrixWorld(true);
const boundingBox = new THREE.Box3().setFromObject(threeObject);
const shouldKeepModelOrigin = !this._originPoint;
if (shouldKeepModelOrigin) {
@@ -3090,7 +3187,7 @@ module.exports = {
// Center the model.
const centerPoint = this._centerPoint;
if (centerPoint) {
threeObject.position.set(
this._threeModelGroup.position.set(
-(boundingBox.min.x + modelWidth * centerPoint[0]),
// The model is flipped on Y axis.
-(boundingBox.min.y + modelHeight * (1 - centerPoint[1])),
@@ -3099,11 +3196,11 @@ module.exports = {
}
// Rotate the model.
threeObject.scale.set(1, 1, 1);
threeObject.rotation.set(
(rotationX * Math.PI) / 180,
(rotationY * Math.PI) / 180,
(rotationZ * Math.PI) / 180
this._threeModelGroup.scale.set(1, 1, 1);
this._threeModelGroup.rotation.set(
(this._rotationX * Math.PI) / 180,
(this._rotationY * Math.PI) / 180,
(this._rotationZ * Math.PI) / 180
);
// Stretch the model in a 1x1x1 cube.
@@ -3115,23 +3212,23 @@ module.exports = {
// Flip on Y because the Y axis is on the opposite side of direct basis.
// It avoids models to be like a mirror refection.
scaleMatrix.makeScale(scaleX, -scaleY, scaleZ);
threeObject.updateMatrix();
threeObject.applyMatrix4(scaleMatrix);
this._threeModelGroup.updateMatrix();
this._threeModelGroup.applyMatrix4(scaleMatrix);
if (keepAspectRatio) {
if (this._keepAspectRatio) {
// Reduce the object dimensions to keep aspect ratio.
const widthRatio =
modelWidth < epsilon
? Number.POSITIVE_INFINITY
: originalWidth / modelWidth;
: this._originalWidth / modelWidth;
const heightRatio =
modelHeight < epsilon
? Number.POSITIVE_INFINITY
: originalHeight / modelHeight;
: this._originalHeight / modelHeight;
const depthRatio =
modelDepth < epsilon
? Number.POSITIVE_INFINITY
: originalDepth / modelDepth;
: this._originalDepth / modelDepth;
const minScaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
if (!Number.isFinite(minScaleRatio)) {
this._defaultWidth = modelWidth;
@@ -3139,48 +3236,125 @@ module.exports = {
this._defaultDepth = modelDepth;
} else {
if (widthRatio === minScaleRatio) {
this._defaultWidth = originalWidth;
this._defaultWidth = this._originalWidth;
this._defaultHeight = Rendered3DInstance.applyRatio({
oldReferenceValue: modelWidth,
newReferenceValue: originalWidth,
newReferenceValue: this._originalWidth,
valueToApplyTo: modelHeight,
});
this._defaultDepth = Rendered3DInstance.applyRatio({
oldReferenceValue: modelWidth,
newReferenceValue: originalWidth,
newReferenceValue: this._originalWidth,
valueToApplyTo: modelDepth,
});
} else if (heightRatio === minScaleRatio) {
this._defaultWidth = Rendered3DInstance.applyRatio({
oldReferenceValue: modelHeight,
newReferenceValue: originalHeight,
newReferenceValue: this._originalHeight,
valueToApplyTo: modelWidth,
});
this._defaultHeight = originalHeight;
this._defaultHeight = this._originalHeight;
this._defaultDepth = Rendered3DInstance.applyRatio({
oldReferenceValue: modelHeight,
newReferenceValue: originalHeight,
newReferenceValue: this._originalHeight,
valueToApplyTo: modelDepth,
});
} else {
this._defaultWidth = Rendered3DInstance.applyRatio({
oldReferenceValue: modelDepth,
newReferenceValue: originalDepth,
newReferenceValue: this._originalDepth,
valueToApplyTo: modelWidth,
});
this._defaultHeight = Rendered3DInstance.applyRatio({
oldReferenceValue: modelDepth,
newReferenceValue: originalDepth,
newReferenceValue: this._originalDepth,
valueToApplyTo: modelHeight,
});
this._defaultDepth = originalDepth;
this._defaultDepth = this._originalDepth;
}
}
}
this._threeObject.add(this._threeModelGroup);
}
updateThreeObject() {
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.Model3DObjectConfiguration
);
let defaultTransformationDirty = false;
const originalWidth = object.getWidth();
const originalHeight = object.getHeight();
const originalDepth = object.getDepth();
if (
this._originalWidth !== originalWidth ||
this._originalHeight !== originalHeight ||
this._originalDepth !== originalDepth
) {
this._originalWidth = originalWidth;
this._originalHeight = originalHeight;
this._originalDepth = originalDepth;
defaultTransformationDirty = true;
}
const rotationX = object.getRotationX();
const rotationY = object.getRotationY();
const rotationZ = object.getRotationZ();
if (
this._rotationX !== rotationX ||
this._rotationY !== rotationY ||
this._rotationZ !== rotationZ
) {
this._rotationX = rotationX;
this._rotationY = rotationY;
this._rotationZ = rotationZ;
defaultTransformationDirty = true;
}
const keepAspectRatio = object.shouldKeepAspectRatio();
if (this._keepAspectRatio !== keepAspectRatio) {
this._keepAspectRatio = keepAspectRatio;
defaultTransformationDirty = true;
}
const originPoint = getPointForLocation(object.getOriginLocation());
if (!isSamePoint(originPoint, this._originPoint)) {
this._originPoint = originPoint;
defaultTransformationDirty = true;
}
const centerPoint = getPointForLocation(object.getCenterLocation());
if (!isSamePoint(centerPoint, this._centerPoint)) {
this._centerPoint = centerPoint;
defaultTransformationDirty = true;
}
if (defaultTransformationDirty) this._updateDefaultTransformation();
const modelResourceName = object.getModelResourceName();
if (this._modelResourceName !== modelResourceName) {
this._modelResourceName = modelResourceName;
this._pixiResourcesLoader
.get3DModel(this._project, modelResourceName)
.then((model3d) => {
if (this._wasDestroyed) return;
this._clonedModel3D = THREE_ADDONS.SkeletonUtils.clone(
model3d.scene
);
this._updateDefaultTransformation();
});
}
this._updateThreeObjectPosition();
}
_updateThreeObjectPosition() {
const width = this.getWidth();
const height = this.getHeight();
const depth = this.getDepth();
@@ -3204,9 +3378,9 @@ module.exports = {
const scaleZ = depth * (this._instance.isFlippedZ() ? -1 : 1);
if (
scaleX !== this._threeObject.scale.width ||
scaleY !== this._threeObject.scale.height ||
scaleZ !== this._threeObject.scale.depth
scaleX !== this._threeObject.scale.x ||
scaleY !== this._threeObject.scale.y ||
scaleZ !== this._threeObject.scale.z
) {
this._threeObject.scale.set(scaleX, scaleY, scaleZ);
}

View File

@@ -76,7 +76,7 @@ namespace gdjs {
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.fog.color = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
gdjs.rgbOrHexStringToNumber(value)
);
}
}

View File

@@ -109,23 +109,26 @@ Model3DObjectConfiguration::GetProperties() const {
objectProperties["rotationX"]
.SetValue(gd::String::From(rotationX))
.SetType("number")
.SetLabel(_("Rotation around X axis"))
.SetLabel(_("X"))
.SetDescription(_("Rotation around X axis"))
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetGroup(_("Default orientation"));
.SetGroup(_("Default rotation"));
objectProperties["rotationY"]
.SetValue(gd::String::From(rotationY))
.SetType("number")
.SetLabel(_("Rotation around Y axis"))
.SetLabel(_("Y"))
.SetDescription(_("Rotation around Y axis"))
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetGroup(_("Default orientation"));
.SetGroup(_("Default rotation"));
objectProperties["rotationZ"]
.SetValue(gd::String::From(rotationZ))
.SetType("number")
.SetLabel(_("Rotation around Z axis"))
.SetLabel(_("Z"))
.SetDescription(_("Rotation around Z axis"))
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetGroup(_("Default orientation"));
.SetGroup(_("Default rotation"));
objectProperties["modelResourceName"]
.SetValue(modelResourceName)
@@ -139,7 +142,7 @@ Model3DObjectConfiguration::GetProperties() const {
.AddExtraInfo("Basic")
.AddExtraInfo("StandardWithoutMetalness")
.AddExtraInfo("KeepOriginal")
.SetLabel(_("Material modifier"));
.SetLabel(_("Material"));
objectProperties["originLocation"]
.SetValue(originLocation.empty() ? "TopLeft" : originLocation)
@@ -149,7 +152,9 @@ Model3DObjectConfiguration::GetProperties() const {
.AddExtraInfo("ObjectCenter")
.AddExtraInfo("BottomCenterZ")
.AddExtraInfo("BottomCenterY")
.SetLabel(_("Origin point"));
.SetLabel(_("Origin point"))
.SetGroup(_("Points"))
.SetAdvanced(true);
objectProperties["centerLocation"]
.SetValue(centerLocation.empty() ? "ObjectCenter" : centerLocation)
@@ -158,7 +163,9 @@ Model3DObjectConfiguration::GetProperties() const {
.AddExtraInfo("ObjectCenter")
.AddExtraInfo("BottomCenterZ")
.AddExtraInfo("BottomCenterY")
.SetLabel(_("Center point"));
.SetLabel(_("Center point"))
.SetGroup(_("Points"))
.SetAdvanced(true);
return objectProperties;
}

View File

@@ -140,7 +140,25 @@ public:
const std::vector<Model3DAnimation> &GetAllAnimations() const {
return animations;
}
///@}
/** \name Getters
* Fast access for rendering instances.
*/
///@{
double GetWidth() const { return width; };
double GetHeight() const { return height; };
double GetDepth() const { return depth; };
double GetRotationX() const { return rotationX; };
double GetRotationY() const { return rotationY; };
double GetRotationZ() const { return rotationZ; };
const gd::String& GetModelResourceName() const { return modelResourceName; };
const gd::String& GetMaterialType() const { return materialType; };
const gd::String& GetOriginLocation() const { return originLocation; };
const gd::String& GetCenterLocation() const { return centerLocation; };
bool shouldKeepAspectRatio() const { return keepAspectRatio; };
///@}
protected:

View File

@@ -376,6 +376,9 @@ namespace gdjs {
setAnimationElapsedTime(time: float): void {
this._renderer.setAnimationElapsedTime(time);
if (!this._animationPaused) {
this._renderer.resumeAnimation();
}
}
getAnimationDuration(): float {

View File

@@ -7,15 +7,18 @@ namespace gdjs {
export namespace evtTools {
export namespace advancedWindow {
const getElectronBrowserWindow = (runtimeScene: gdjs.RuntimeScene) => {
const electronRemote = runtimeScene
.getGame()
.getRenderer()
.getElectronRemote();
if (electronRemote) {
return electronRemote.getCurrentWindow();
try {
const electronRemote = runtimeScene
.getGame()
.getRenderer()
.getElectronRemote();
if (electronRemote) {
return electronRemote.getCurrentWindow();
}
return null;
} catch (error) {
return null;
}
return null;
};
export const focus = function (

View File

@@ -75,7 +75,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
.AddExtraInfo(_("Window center"))
.AddExtraInfo(_("Window right"))
.AddExtraInfo(_("Proportional"))
.SetLabel(_("Left edge anchor"))
.SetLabel(_("Left edge"))
.SetDescription(_("Anchor the left edge of the object on X axis."));
properties["rightEdgeAnchor"]
@@ -87,7 +87,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
.AddExtraInfo(_("Window center"))
.AddExtraInfo(_("Window right"))
.AddExtraInfo(_("Proportional"))
.SetLabel(_("Right edge anchor"))
.SetLabel(_("Right edge"))
.SetDescription(_("Anchor the right edge of the object on X axis."));
properties["topEdgeAnchor"]
@@ -99,7 +99,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
.AddExtraInfo(_("Window center"))
.AddExtraInfo(_("Window bottom"))
.AddExtraInfo(_("Proportional"))
.SetLabel(_("Top edge anchor"))
.SetLabel(_("Top edge"))
.SetDescription(_("Anchor the top edge of the object on Y axis."));
properties["bottomEdgeAnchor"]
@@ -111,7 +111,7 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
.AddExtraInfo(_("Window center"))
.AddExtraInfo(_("Window bottom"))
.AddExtraInfo(_("Proportional"))
.SetLabel(_("Bottom edge anchor"))
.SetLabel(_("Bottom edge"))
.SetDescription(_("Anchor the bottom edge of the object on Y axis."));
properties["useLegacyBottomAndRightAnchors"]
@@ -119,12 +119,12 @@ std::map<gd::String, gd::PropertyDescriptor> AnchorBehavior::GetProperties(
"Stretch object when anchoring right or bottom edge (deprecated, "
"it's recommended to leave this unchecked and anchor both sides if "
"you want Sprite to stretch instead.)"))
.SetGroup(_("Deprecated options (advanced)"))
.SetValue(behaviorContent.GetBoolAttribute(
"useLegacyBottomAndRightAnchors", true)
? "true"
: "false")
.SetType("Boolean");
.SetType("Boolean")
.SetDeprecated(true);
return properties;
}

View File

@@ -88,7 +88,6 @@ namespace gdjs {
const workingPoint: FloatPoint = gdjs.staticArray(
gdjs.AnchorRuntimeBehavior.prototype.doStepPreEvents
) as FloatPoint;
// TODO EBO Make it work with event based objects or hide this behavior for them.
let parentMinX = instanceContainer.getUnrotatedViewportMinX();
let parentMinY = instanceContainer.getUnrotatedViewportMinY();
let parentMaxX = instanceContainer.getUnrotatedViewportMaxX();
@@ -111,10 +110,11 @@ namespace gdjs {
}
//Calculate the distances from the window's bounds.
const topLeftPixel = layer.convertCoords(
const topLeftPixel = this._convertCoords(
instanceContainer,
layer,
this.owner.getDrawableX(),
this.owner.getDrawableY(),
0,
workingPoint
);
@@ -141,10 +141,11 @@ namespace gdjs {
}
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const bottomRightPixel = layer.convertCoords(
const bottomRightPixel = this._convertCoords(
instanceContainer,
layer,
this.owner.getDrawableX() + this.owner.getWidth(),
this.owner.getDrawableY() + this.owner.getHeight(),
0,
workingPoint
);
@@ -225,19 +226,21 @@ namespace gdjs {
}
// It's fine to reuse workingPoint as topLeftPixel is no longer used.
const topLeftCoord = layer.convertInverseCoords(
const topLeftCoord = this._convertInverseCoords(
instanceContainer,
layer,
leftPixel,
topPixel,
0,
workingPoint
);
const left = topLeftCoord[0];
const top = topLeftCoord[1];
const bottomRightCoord = layer.convertInverseCoords(
const bottomRightCoord = this._convertInverseCoords(
instanceContainer,
layer,
rightPixel,
bottomPixel,
0,
workingPoint
);
const right = bottomRightCoord[0];
@@ -270,8 +273,20 @@ namespace gdjs {
this._rightEdgeAnchor !== HorizontalAnchor.None &&
this._leftEdgeAnchor !== HorizontalAnchor.None
) {
this.owner.setWidth(right - left);
this.owner.setX(left);
const width = right - left;
this.owner.setX(
this.owner.getX() === this.owner.getDrawableX()
? left
: // It uses the position of the origin relatively to the object
// size to apply it with the new size.
// This is the same as doing:
// lerp(left, right, (this.owner.getX() - this.owner.getDrawableX() / this.owner.getWidth())
// But, the division is done at the end to avoid rounding errors.
left +
((this.owner.getX() - this.owner.getDrawableX()) * width) /
this.owner.getWidth()
);
this.owner.setWidth(width);
} else {
if (this._leftEdgeAnchor !== HorizontalAnchor.None) {
this.owner.setX(
@@ -292,8 +307,15 @@ namespace gdjs {
this._bottomEdgeAnchor !== VerticalAnchor.None &&
this._topEdgeAnchor !== VerticalAnchor.None
) {
this.owner.setHeight(bottom - top);
this.owner.setY(top);
const height = bottom - top;
this.owner.setY(
this.owner.getY() === this.owner.getDrawableY()
? top
: top +
((this.owner.getY() - this.owner.getDrawableY()) * height) /
this.owner.getHeight()
);
this.owner.setHeight(height);
} else {
if (this._topEdgeAnchor !== VerticalAnchor.None) {
this.owner.setY(
@@ -314,6 +336,40 @@ namespace gdjs {
}
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
private _convertCoords(
instanceContainer: gdjs.RuntimeInstanceContainer,
layer: gdjs.RuntimeLayer,
x: float,
y: float,
result: FloatPoint
) {
const isParentACustomObject =
instanceContainer !== instanceContainer.getScene();
if (isParentACustomObject) {
result[0] = x;
result[1] = y;
return result;
}
return layer.convertCoords(x, y, 0, result);
}
private _convertInverseCoords(
instanceContainer: gdjs.RuntimeInstanceContainer,
layer: gdjs.RuntimeLayer,
x: float,
y: float,
result: FloatPoint
) {
const isParentACustomObject =
instanceContainer !== instanceContainer.getScene();
if (isParentACustomObject) {
result[0] = x;
result[1] = y;
return result;
}
return layer.convertInverseCoords(x, y, 0, result);
}
}
gdjs.registerBehavior(
'AnchorBehavior::AnchorBehavior',

View File

@@ -70,6 +70,50 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
return object;
}
const createSpriteWithOriginAtCenter = (behaviorProperties) => {
const object = new gdjs.TestSpriteRuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
name: anchorBehaviorName,
type: 'AnchorBehavior::AnchorBehavior',
// @ts-ignore - properties are not typed
rightEdgeAnchor: 0,
leftEdgeAnchor: 0,
topEdgeAnchor: 0,
bottomEdgeAnchor: 0,
relativeToOriginalWindowSize: false,
useLegacyBottomAndRightAnchors: false,
...behaviorProperties,
},
],
effects: [],
animations: [
{
name: 'animation',
directions: [
{
sprites: [
{
originPoint: { x: 50, y: 50 },
centerPoint: { x: 50, y: 50 },
points: [],
hasCustomCollisionMask: false,
customCollisionMask: [],
},
],
},
],
},
],
});
object.setUnscaledWidthAndHeight(100, 100);
object.setCustomWidthAndHeight(10, 10);
runtimeScene.addObject(object);
return object;
};
describe('(anchor horizontal edge)', function () {
['rightEdgeAnchor', 'leftEdgeAnchor'].forEach((objectEdge) => {
it(`anchors the ${objectEdge} edge of object to window left (fixed)`, function () {
@@ -200,5 +244,47 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
expect(object.getY()).to.equal(1000);
expect(object.getWidth()).to.equal(10);
});
it('can fill the screen with an object (with custom origin)', function () {
setGameResolutionSizeAndStep(1000, 500);
const object = createSpriteWithOriginAtCenter({
leftEdgeAnchor: 1,
topEdgeAnchor: 1,
rightEdgeAnchor: 2,
bottomEdgeAnchor: 2,
});
object.setCustomWidthAndHeight(1000, 500);
object.setPosition(500, 250);
runtimeScene.renderAndStep(1000 / 60);
setGameResolutionSizeAndStep(2000, 3000);
expect(object.getX()).to.equal(1000);
expect(object.getY()).to.equal(1500);
expect(object.getWidth()).to.equal(2000);
expect(object.getHeight()).to.equal(3000);
});
it('can fill the screen with an object using proportional anchors (with custom origin)', () => {
setGameResolutionSizeAndStep(1000, 500);
const object = createSpriteWithOriginAtCenter({
leftEdgeAnchor: 3,
topEdgeAnchor: 3,
rightEdgeAnchor: 3,
bottomEdgeAnchor: 3,
});
object.setCustomWidthAndHeight(1000, 500);
object.setPosition(500, 250);
runtimeScene.renderAndStep(1000 / 60);
setGameResolutionSizeAndStep(2000, 3000);
expect(object.getX()).to.equal(1000);
expect(object.getY()).to.equal(1500);
expect(object.getWidth()).to.equal(2000);
expect(object.getHeight()).to.equal(3000);
});
});
});

View File

@@ -34,11 +34,8 @@ module.exports = {
.setIcon('JsPlatform/Extensions/bbcode32.png');
var objectBBText = new gd.ObjectJsImplementation();
objectBBText.updateProperty = function (
objectContent,
propertyName,
newValue
) {
objectBBText.updateProperty = function (propertyName, newValue) {
const objectContent = this.content;
if (propertyName in objectContent) {
if (typeof objectContent[propertyName] === 'boolean')
objectContent[propertyName] = newValue === '1';
@@ -50,8 +47,9 @@ module.exports = {
return false;
};
objectBBText.getProperties = function (objectContent) {
objectBBText.getProperties = function () {
const objectProperties = new gd.MapStringPropertyDescriptor();
const objectContent = this.content;
objectProperties
.getOrCreate('text')
@@ -107,29 +105,26 @@ module.exports = {
return objectProperties;
};
objectBBText.setRawJSONContent(
JSON.stringify({
text:
'[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
opacity: 255,
fontSize: 20,
visible: true,
color: '0;0;0',
fontFamily: 'Arial',
align: 'left',
wordWrap: true,
})
);
objectBBText.content = {
text:
'[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
opacity: 255,
fontSize: 20,
visible: true,
color: '0;0;0',
fontFamily: 'Arial',
align: 'left',
wordWrap: true,
};
objectBBText.updateInitialInstanceProperty = function (
objectContent,
instance,
propertyName,
newValue
) {
return false;
};
objectBBText.getInitialInstanceProperties = function (content, instance) {
objectBBText.getInitialInstanceProperties = function (instance) {
var instanceProperties = new gd.MapStringPropertyDescriptor();
return instanceProperties;
};
@@ -531,22 +526,33 @@ module.exports = {
* This is called to update the PIXI object on the scene editor
*/
update() {
const properties = this._associatedObjectConfiguration.getProperties();
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.ObjectJsImplementation
);
const rawText = properties.get('text').getValue();
const rawText = object.content.text;
if (rawText !== this._pixiObject.text) {
this._pixiObject.text = rawText;
}
const color = properties.get('color').getValue();
this._pixiObject.textStyles.default.fill = objectsRenderingService.rgbOrHexToHexNumber(
color
);
const color = object.content.color;
const newColor = objectsRenderingService.rgbOrHexToHexNumber(color);
if (newColor !== this._pixiObject.textStyles.default.fill) {
this._pixiObject.textStyles.default.fill = newColor;
this._pixiObject.dirty = true;
}
const fontSize = properties.get('fontSize').getValue();
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
const fontSize = object.content.fontSize;
const newDefaultFontsize = `${fontSize}px`;
if (
newDefaultFontsize !== this._pixiObject.textStyles.default.fontSize
) {
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
this._pixiObject.dirty = true;
}
const fontResourceName = properties.get('fontFamily').getValue();
const fontResourceName = object.content.fontFamily;
if (this._fontResourceName !== fontResourceName) {
this._fontResourceName = fontResourceName;
@@ -567,13 +573,13 @@ module.exports = {
});
}
const wordWrap = properties.get('wordWrap').getValue() === 'true';
const wordWrap = object.content.wordWrap;
if (wordWrap !== this._pixiObject._style.wordWrap) {
this._pixiObject._style.wordWrap = wordWrap;
this._pixiObject.dirty = true;
}
const align = properties.get('align').getValue();
const align = object.content.align;
if (align !== this._pixiObject._style.align) {
this._pixiObject._style.align = align;
this._pixiObject.dirty = true;

View File

@@ -34,11 +34,8 @@ module.exports = {
.setIcon('JsPlatform/Extensions/bitmapfont32.png');
const bitmapTextObject = new gd.ObjectJsImplementation();
bitmapTextObject.updateProperty = function (
objectContent,
propertyName,
newValue
) {
bitmapTextObject.updateProperty = function (propertyName, newValue) {
const objectContent = this.content;
if (propertyName in objectContent) {
if (typeof objectContent[propertyName] === 'boolean')
objectContent[propertyName] = newValue === '1';
@@ -50,8 +47,9 @@ module.exports = {
return false;
};
bitmapTextObject.getProperties = function (objectContent) {
bitmapTextObject.getProperties = function () {
const objectProperties = new gd.MapStringPropertyDescriptor();
const objectContent = this.content;
objectProperties
.getOrCreate('text')
@@ -66,7 +64,7 @@ module.exports = {
.addExtraInfo('left')
.addExtraInfo('center')
.addExtraInfo('right')
.setLabel(_('Alignment, when multiple lines are displayed'))
.setLabel(_('Alignment'))
.setGroup(_('Appearance'));
objectProperties
@@ -82,7 +80,7 @@ module.exports = {
.setValue(objectContent.textureAtlasResourceName)
.setType('resource')
.addExtraInfo('image')
.setLabel(_('Bitmap atlas image'))
.setLabel(_('Bitmap Atlas'))
.setGroup(_('Font'));
objectProperties
@@ -108,33 +106,27 @@ module.exports = {
return objectProperties;
};
bitmapTextObject.setRawJSONContent(
JSON.stringify({
text:
'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
opacity: 255,
scale: 1,
fontSize: 20,
tint: '255;255;255',
bitmapFontResourceName: '',
textureAtlasResourceName: '',
align: 'left',
wordWrap: true,
})
);
bitmapTextObject.content = {
text:
'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
opacity: 255,
scale: 1,
fontSize: 20,
tint: '255;255;255',
bitmapFontResourceName: '',
textureAtlasResourceName: '',
align: 'left',
wordWrap: true,
};
bitmapTextObject.updateInitialInstanceProperty = function (
objectContent,
instance,
propertyName,
newValue
) {
return false;
};
bitmapTextObject.getInitialInstanceProperties = function (
content,
instance
) {
bitmapTextObject.getInitialInstanceProperties = function (instance) {
var instanceProperties = new gd.MapStringPropertyDescriptor();
return instanceProperties;
};
@@ -659,31 +651,31 @@ module.exports = {
}
update() {
const properties = this._associatedObjectConfiguration.getProperties();
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.ObjectJsImplementation
);
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).
const rawText = properties.get('text').getValue();
const rawText = object.content.text;
this._pixiObject.text = rawText;
const align = properties.get('align').getValue();
const align = object.content.align;
this._pixiObject.align = align;
const color = properties.get('tint').getValue();
const color = object.content.tint;
this._pixiObject.tint = objectsRenderingService.rgbOrHexToHexNumber(
color
);
const scale = +(properties.get('scale').getValue() || 1);
const scale = object.content.scale;
this._pixiObject.scale.set(scale);
// Track the changes in font to load the new requested font.
const bitmapFontResourceName = properties
.get('bitmapFontResourceName')
.getValue();
const textureAtlasResourceName = properties
.get('textureAtlasResourceName')
.getValue();
const bitmapFontResourceName = object.content.bitmapFontResourceName;
const textureAtlasResourceName =
object.content.textureAtlasResourceName;
if (
this._currentBitmapFontResourceName !== bitmapFontResourceName ||
@@ -705,6 +697,8 @@ module.exports = {
this._currentBitmapFontResourceName,
this._currentTextureAtlasResourceName
).then((bitmapFont) => {
if (this._wasDestroyed) return;
this._pixiObject.fontName = bitmapFont.font;
this._pixiObject.fontSize = bitmapFont.size;
this._pixiObject.dirty = true;
@@ -712,7 +706,7 @@ module.exports = {
}
// Set up the wrapping width if enabled.
const wordWrap = properties.get('wordWrap').getValue() === 'true';
const wordWrap = object.content.wordWrap;
if (wordWrap && this._instance.hasCustomSize()) {
this._pixiObject.maxWidth =
this.getCustomWidth() / this._pixiObject.scale.x;

View File

@@ -26,7 +26,8 @@ DestroyOutsideBehavior::GetProperties(
behaviorContent.GetDoubleAttribute("extraBorder", 0)))
.SetType("Number")
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetLabel(_("Margin before deleting the object, in pixels"));
.SetLabel(_("Deletion margin"))
.SetDescription(_("Margin before deleting the object, in pixels"));
return properties;
}

View File

@@ -26,7 +26,7 @@ std::map<gd::String, gd::PropertyDescriptor> DraggableBehavior::GetProperties(
? "true"
: "false")
.SetType("Boolean")
.SetLabel(_("Do a precision check against the object's collision mask"))
.SetLabel(_("Precise check"))
.SetDescription(
_("Use the object (custom) collision mask instead of the bounding "
"box, making the behavior more precise at the cost of "

View File

@@ -67,14 +67,10 @@ namespace gdjs {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter &
BevelFilterExtra;
if (parameterName === 'lightColor') {
bevelFilter.lightColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
bevelFilter.lightColor = gdjs.rgbOrHexStringToNumber(value);
}
if (parameterName === 'shadowColor') {
bevelFilter.shadowColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
bevelFilter.shadowColor = gdjs.rgbOrHexStringToNumber(value);
}
}
updateColorParameter(

View File

@@ -45,13 +45,9 @@ namespace gdjs {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter &
ColorReplaceFilterExtra;
if (parameterName === 'originalColor') {
colorReplaceFilter.originalColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
colorReplaceFilter.originalColor = gdjs.rgbOrHexStringToNumber(value);
} else if (parameterName === 'newColor') {
colorReplaceFilter.newColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
colorReplaceFilter.newColor = gdjs.rgbOrHexStringToNumber(value);
}
}
updateColorParameter(

View File

@@ -66,9 +66,7 @@ namespace gdjs {
) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'color') {
dropShadowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
dropShadowFilter.color = gdjs.rgbOrHexStringToNumber(value);
}
}
updateColorParameter(

View File

@@ -53,7 +53,7 @@ namespace gdjs {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter &
GlowFilterExtra;
if (parameterName === 'color') {
glowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
glowFilter.color = gdjs.rgbOrHexStringToNumber(value);
}
}
updateColorParameter(

View File

@@ -41,9 +41,7 @@ namespace gdjs {
) {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
if (parameterName === 'color') {
outlineFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
outlineFilter.color = gdjs.rgbOrHexStringToNumber(value);
}
}
updateColorParameter(

View File

@@ -287,11 +287,9 @@ module.exports = {
// Everything that is stored inside the object is in "content" and is automatically
// saved/loaded to JSON.
var dummyObject = new gd.ObjectJsImplementation();
dummyObject.updateProperty = function (
objectContent,
propertyName,
newValue
) {
dummyObject.updateProperty = function (propertyName, newValue) {
const objectContent = this.content;
if (propertyName === 'My first property') {
objectContent.property1 = newValue;
return true;
@@ -311,8 +309,9 @@ module.exports = {
return false;
};
dummyObject.getProperties = function (objectContent) {
dummyObject.getProperties = function () {
var objectProperties = new gd.MapStringPropertyDescriptor();
const objectContent = this.content;
objectProperties
.getOrCreate('My first property')
@@ -336,17 +335,14 @@ module.exports = {
return objectProperties;
};
dummyObject.setRawJSONContent(
JSON.stringify({
property1: 'Hello world',
property2: true,
property3: 123,
myImage: '',
})
);
dummyObject.content = {
property1: 'Hello world',
property2: true,
property3: 123,
myImage: '',
};
dummyObject.updateInitialInstanceProperty = function (
objectContent,
instance,
propertyName,
newValue
@@ -362,7 +358,7 @@ module.exports = {
return false;
};
dummyObject.getInitialInstanceProperties = function (content, instance) {
dummyObject.getInitialInstanceProperties = function (instance) {
var instanceProperties = new gd.MapStringPropertyDescriptor();
instanceProperties
@@ -507,12 +503,13 @@ module.exports = {
* This is called to update the PIXI object on the scene editor
*/
update() {
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.ObjectJsImplementation
);
// Read a property from the object
const property1Value = this._associatedObjectConfiguration
.getProperties()
.get('My first property')
.getValue();
this._pixiObject.text = property1Value;
this._pixiObject.text = object.content.property1;
// Read position and angle from the instance
this._pixiObject.position.x =

View File

@@ -14,9 +14,12 @@ class RenderedInstance {
_associatedObjectConfiguration: gd.ObjectConfiguration;
_pixiContainer: PIXI.Container;
_pixiResourcesLoader: Class<PixiResourcesLoader>;
_pixiObject: PIXI.DisplayObject;
_pixiObject: PIXI.DisplayObject | null;
wasUsed: boolean;
/** Set to true when onRemovedFromScene is called. Allows to cancel promises/asynchronous operations (notably: waiting for a resource load). */
_wasDestroyed: boolean;
constructor(
project: gdProject,
instance: gdInitialInstance,
@@ -89,10 +92,13 @@ class Rendered3DInstance {
_pixiContainer: PIXI.Container;
_threeGroup: THREE.Group;
_pixiResourcesLoader: Class<PixiResourcesLoader>;
_pixiObject: PIXI.DisplayObject;
_pixiObject: PIXI.DisplayObject | null;
_threeObject: THREE.Object3D | null;
wasUsed: boolean;
/** Set to true when onRemovedFromScene is called. Allows to cancel promises/asynchronous operations (notably: waiting for a resource load). */
_wasDestroyed: boolean;
constructor(
project: gdProject,
instance: gdInitialInstance,

View File

@@ -588,7 +588,7 @@ namespace gdjs {
break;
case 'openPlayerAuthentication':
gdjs.playerAuthentication
.openAuthenticationWindow(runtimeScene)
.openAuthenticationWindow(runtimeScene, event.data.options)
.promise.then(({ status }) => {
if (
!_leaderboardViewIframe ||

View File

@@ -68,11 +68,8 @@ module.exports = {
const lightObject = new gd.ObjectJsImplementation();
lightObject.updateProperty = function (
objectContent,
propertyName,
newValue
) {
lightObject.updateProperty = function (propertyName, newValue) {
const objectContent = this.content;
if (propertyName === 'radius') {
objectContent.radius = parseFloat(newValue);
return true;
@@ -96,8 +93,9 @@ module.exports = {
return false;
};
lightObject.getProperties = function (objectContent) {
lightObject.getProperties = function () {
const objectProperties = new gd.MapStringPropertyDescriptor();
const objectContent = this.content;
objectProperties.set(
'radius',
@@ -140,17 +138,14 @@ module.exports = {
return objectProperties;
};
lightObject.setRawJSONContent(
JSON.stringify({
radius: 50,
color: '255;255;255',
debugMode: false,
texture: '',
})
);
lightObject.content = {
radius: 50,
color: '255;255;255',
debugMode: false,
texture: '',
};
lightObject.updateInitialInstanceProperty = function (
objectContent,
instance,
propertyName,
newValue
@@ -158,7 +153,7 @@ module.exports = {
return false;
};
lightObject.getInitialInstanceProperties = function (content, instance) {
lightObject.getInitialInstanceProperties = function (instance) {
const instanceProperties = new gd.MapStringPropertyDescriptor();
return instanceProperties;
@@ -238,6 +233,10 @@ module.exports = {
* Renderer for instances of LightObject inside the IDE.
*/
class RenderedLightObjectInstance extends RenderedInstance {
_radius = 0;
_color = 0;
_radiusGraphics = null;
constructor(
project,
instance,
@@ -252,19 +251,6 @@ module.exports = {
pixiContainer,
pixiResourcesLoader
);
this._radius = parseFloat(
this._associatedObjectConfiguration
.getProperties()
.get('radius')
.getValue()
);
if (this._radius <= 0) this._radius = 1;
const color = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.getProperties()
.get('color')
.getValue()
);
// The icon in the middle.
const lightIconSprite = new PIXI.Sprite(
@@ -274,18 +260,11 @@ module.exports = {
lightIconSprite.anchor.y = 0.5;
// The circle to show the radius of the light.
const radiusBorderWidth = 2;
const radiusGraphics = new PIXI.Graphics();
radiusGraphics.lineStyle(radiusBorderWidth, color, 0.8);
radiusGraphics.drawCircle(
0,
0,
Math.max(1, this._radius - radiusBorderWidth)
);
this._radiusGraphics = new PIXI.Graphics();
this._pixiObject = new PIXI.Container();
this._pixiObject.addChild(lightIconSprite);
this._pixiObject.addChild(radiusGraphics);
this._pixiObject.addChild(this._radiusGraphics);
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
@@ -307,8 +286,41 @@ module.exports = {
* This is called to update the PIXI object on the scene editor
*/
update() {
const object = gd.castObject(
this._associatedObjectConfiguration,
gd.ObjectJsImplementation
);
this._pixiObject.position.x = this._instance.getX();
this._pixiObject.position.y = this._instance.getY();
let radiusGraphicsDirty = false;
let radius = object.content.radius;
if (radius <= 0) radius = 1;
if (radius !== this._radius) {
this._radius = radius;
radiusGraphicsDirty = true;
}
const color = objectsRenderingService.rgbOrHexToHexNumber(
object.content.color
);
if (color !== this._color) {
this._color = color;
radiusGraphicsDirty = true;
}
if (radiusGraphicsDirty) {
const radiusBorderWidth = 2;
this._radiusGraphics.clear();
this._radiusGraphics.lineStyle(radiusBorderWidth, color, 0.8);
this._radiusGraphics.drawCircle(
0,
0,
Math.max(1, this._radius - radiusBorderWidth)
);
}
}
/**

View File

@@ -505,8 +505,9 @@ namespace gdjs {
closestVertices.sort(
LightRuntimeObjectPixiRenderer._verticesWithAngleComparator
);
const filteredVerticesResult = [closestVertices[0].vertex];
const closestVerticesCount = closestVertices.length;
if (closestVerticesCount === 0) return [];
const filteredVerticesResult = [closestVertices[0].vertex];
for (let i = 1; i < closestVerticesCount; i++) {
if (closestVertices[i].angle !== closestVertices[i - 1].angle) {
filteredVerticesResult.push(closestVertices[i].vertex);

View File

@@ -120,10 +120,11 @@ namespace gdjs {
private _isOwnerAsPlayerOrHost() {
const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
const isHost = gdjs.multiplayer.isCurrentPlayerHost();
const isOwnerOfObject =
currentPlayerNumber === this.playerNumber || // Player as owner.
(currentPlayerNumber === 1 && this.playerNumber === 0); // Host as owner.
(isHost && this.playerNumber === 0); // Host as owner.
return isOwnerOfObject;
}
@@ -455,8 +456,8 @@ namespace gdjs {
// Before sending the destroy message, we set up the object representing the peers
// that we need an acknowledgment from.
// If we are player 1, we are connected to everyone, so we expect an acknowledgment from everyone.
// If we are another player, we are only connected to player 1, so we expect an acknowledgment from player 1.
// If we are the host, we are connected to everyone, so we expect an acknowledgment from everyone.
// If we are another player, we are only connected to the host, so we expect an acknowledgment from the host.
// In both cases, this represents the list of peers the current user is connected to.
const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
const {
@@ -508,7 +509,7 @@ namespace gdjs {
// Update the ownership locally, so the object can be used immediately.
// This is a prediction to allow snappy interactions.
// If we are player 1 or host, we will have the ownership immediately anyway.
// If we are host, we will have the ownership immediately anyway.
// If we are another player, we will have the ownership as soon as the host acknowledges the change.
// If the host does not send an acknowledgment, we will revert the ownership.
const previousObjectPlayerNumber = this.playerNumber;
@@ -564,8 +565,8 @@ namespace gdjs {
});
// Before sending the changeOwner message, if we are becoming the new owner,
// we want to ensure this message is acknowledged, by everyone we're connected to.
// If we are player 1, we are connected to everyone, so we expect an acknowledgment from everyone.
// If we are another player, we are only connected to player 1, so we expect an acknowledgment from player 1.
// If we are the host, we are connected to everyone, so we expect an acknowledgment from everyone.
// If we are another player, we are only connected to the host, so we expect an acknowledgment from the host.
// In both cases, this represents the list of peers the current user is connected to.
if (newObjectPlayerNumber === currentPlayerNumber) {
const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();
@@ -581,7 +582,7 @@ namespace gdjs {
expectedMessageName: changeOwnerAcknowledgedMessageName,
otherPeerIds,
// If we are not the host, we should revert the ownership if the host does not acknowledge the change.
shouldCancelMessageIfTimesOut: currentPlayerNumber !== 1,
shouldCancelMessageIfTimesOut: !gdjs.multiplayer.isCurrentPlayerHost(),
});
}

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