Compare commits

...

134 Commits

Author SHA1 Message Date
github-actions[bot]
528b8f4e6c Update translations [skip ci] (#6663)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-06-17 15:06:36 +02:00
Clément Pasteau
7212d56a1b Add folders for multiplayer extension (#6667)
Do not show in changelog
2024-06-17 14:53:12 +02:00
Clément Pasteau
5cba43335c Fix sentence for taking ownership (#6666)
Do not show in changelog
2024-06-17 14:28:08 +02:00
Clément Pasteau
324698e269 Fix parsing wrongly the variableNetworkId (#6665)
Do not show in changelog
2024-06-17 14:19:40 +02:00
Clément Pasteau
d219bf05d3 Fix the animation frame update for a Sprite when receiving new animation from the network (#6664) 2024-06-17 13:20:26 +02:00
D8H
9cbc421d74 Generate a default sentence for event-based functions (#6638) 2024-06-16 23:18:54 +02:00
Florian Rival
f63d9d1b5c Bump version 2024-06-16 10:30:32 +02:00
github-actions[bot]
dd5d0669b1 Update translations [skip ci] (#6661)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-06-16 10:29:40 +02:00
Clément Pasteau
e681632e60 Fix correctly parsing custom messages (#6662)
Do not show in changelog
2024-06-14 18:45:16 +02:00
AlexandreS
0bcb219e00 Inform lobbies iframe if opened from preview in native mobile app (#6659)
Don't show in changelog
2024-06-14 17:09:55 +02:00
D8H
6d5e9c1676 Fix regression on activate behavior action parameter (typo) (#6660)
- Don't show in changelog
2024-06-14 17:06:26 +02:00
github-actions[bot]
5ded5648bf Update translations [skip ci] (#6658)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-06-14 16:39:48 +02:00
AlexandreS
687f926bc5 Add logic to copy lobby configuration when opening an example (#6657)
Don't show in changelog
2024-06-14 15:41:58 +02:00
github-actions[bot]
4a2d573956 Update translations [skip ci] (#6641)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-06-14 15:39:12 +02:00
Clément Pasteau
ce77414f85 Allow assigning variable owners for multiplayer games (#6651)
* Actions can be used at the beginning of the game or a scene, to define scene and global variables as synchronised by another player, or not synchronised at all
2024-06-14 15:37:37 +02:00
D8H
298ff6311d Fix game properties editor to avoid empty values to make project saving fail (#6656) 2024-06-14 13:04:47 +02:00
Clément Pasteau
ca0a000ae1 Use a unique key for root extension configuration row (#6655)
Do not show in changelog
2024-06-14 11:21:58 +02:00
D8H
dc87f138a6 Fix expression validation for variables with brackets (#6652) 2024-06-13 18:38:35 +02:00
Aurélien Vivet
28af860aff Add a new guided lesson: Top-down movement for pixel perfect game (#6650) 2024-06-13 16:12:50 +02:00
AlexandreS
4dbc607314 Support boolean expected value in in-app tutorial (#6654)
Only show in developer changelog
2024-06-13 14:10:02 +02:00
Clément Pasteau
bfefe9baf4 Synchronize 3D objects properties properly over network (#6649) 2024-06-11 12:31:36 +02:00
Florian Rival
2332359c19 Fetch in-app tutorial descriptions/titles instead of hardcoding them (#6647)
Only show in developer changelog
2024-06-10 15:55:27 +02:00
D8H
68b6c443c3 Fix variables editor shortcut from events tabs (#6646) 2024-06-10 15:36:27 +02:00
D8H
37e113baaa Fix a regression on the expression autocompletion layout (#6645)
Don't show in changelog
2024-06-10 10:42:21 +02:00
D8H
a8a8c142a3 Fix format (#6642)
Don't show in changelog
2024-06-08 12:38:33 +02:00
D8H
012e625a47 Fix regression on activate behavior action parameter (#6639)
Don't show in changelog
2024-06-07 18:03:29 +02:00
github-actions[bot]
c0e6d98e4c Update translations [skip ci] (#6637) 2024-06-07 17:35:17 +02:00
D8H
ab63e02862 Fill empty behavior parameters in actions/conditions when a behavior is attached to an object (#6633) 2024-06-07 17:09:58 +02:00
D8H
1b13127a2f Display errors on objects with missing behaviors in the event sheet (#6632) 2024-06-07 16:52:13 +02:00
Florian Rival
3de0861172 Bump version (#6636) 2024-06-07 16:42:16 +02:00
github-actions[bot]
795417c915 Update translations [skip ci] (#6634)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-06-07 16:32:01 +02:00
Clément Pasteau
e8540dd720 Fix multiplayer tests (#6635) 2024-06-07 16:31:37 +02:00
Clément Pasteau
3c5fee0535 Add experimental support for multiplayer games ("GDevelop Multiplayer") (#6613) 2024-06-07 14:12:58 +02:00
github-actions[bot]
7f2806bd89 Update translations [skip ci] (#6618)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-06-07 09:54:34 +02:00
D8H
886fdb464e Fix a regression on the 1st scene loading (#6630)
- Don't show in changelog
2024-06-06 13:15:59 +02:00
AlexandreS
6aaeda6c49 UI improvements (#6631)
- Use red color for notifications badge
- Make snackbars have the same theme as the interface
2024-06-06 13:14:32 +02:00
AlexandreS
830e35b368 Add saving suggestions (#6627)
- Displays a message to remind you to save your project after significant changes
- Adds a dot badge to the Save icon to remind you you didn't save
2024-06-06 10:58:18 +02:00
D8H
fa4a621888 Fix variable autocompletion scope icon (#6628)
- Don't show in changelog
2024-06-05 18:22:22 +02:00
D8H
8756932c12 Fix the variable editor global tab initial opening (#6626)
- Don't show in changelog
2024-06-05 14:23:31 +02:00
D8H
0b5f929773 Display expression errors on repeat loops (#6625) 2024-06-05 13:49:14 +02:00
Aurélien Vivet
183fba4720 Add a new guided lesson: Platformer (#6623) 2024-06-05 10:27:01 +02:00
D8H
9d608a0e93 Fix project manager shortcuts from triggering when a dialog is open (#6624) 2024-06-04 19:06:44 +02:00
D8H
5bbfd81d89 Allow object variables to take the same name as an object (#6622)
- It also fixes the variable instance dialog cancellation.
- Don't show in changelog
2024-06-04 16:18:26 +02:00
Florian Rival
a441bb04ef Add "pip install setuptools" for Windows to readme (#6611)
Only show in developer changelog
2024-06-04 13:40:24 +02:00
D8H
ccf5e390d6 Fix 2D custom objects Z-order in the editor (#6620) 2024-06-03 23:05:02 +02:00
D8H
e633aa77c2 Remove unused import (#6619)
- Don't show in changelog
2024-06-03 19:16:31 +02:00
D8H
12275d0814 Always keep events when an object is deleted (#6615) 2024-06-03 18:04:11 +02:00
AlexandreS
4ed1889856 Add asset from asset store next to the selected item in the objects list (#6614)
- Also : hides variables list when multiple instances selected
2024-06-03 18:01:30 +02:00
D8H
ae6ac2068b Show object thumbnails in "for each object" loops (#6617) 2024-06-03 17:59:56 +02:00
github-actions[bot]
a31c5cfac3 Update translations [skip ci] (#6581)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-06-03 14:39:46 +02:00
D8H
1917d5aedd Rename child-variable occurencies in events (#6602) 2024-06-03 13:26:18 +02:00
D8H
d0c85cd3bc Remove extension metadata from exported data.json (#6612)
- Don't show in changelog
2024-06-03 11:51:56 +02:00
D8H
afed90ccef Show extension variables and properties in the tree (#6610)
- Don't show in changelog
2024-06-03 10:52:26 +02:00
D8H
ff9c6e5cf3 Display missing objects in the diagnostic report (#6608) 2024-06-03 10:48:45 +02:00
D8H
199d315e29 Add an icon for variables, parameters and properties in expression autocompletions (#6609) 2024-06-02 17:14:22 +02:00
AlexandreS
ddf426c2fd Handle Diagnostic report dialog when in app tutorial running (#6607) 2024-05-31 14:28:43 +02:00
AlexandreS
43648b0a18 Fix issues around local in-app tutorial development (#6606)
Don't show in changelog
2024-05-31 12:02:34 +02:00
AlexandreS
96e5d6966a Add possibility to load WIP in-app tutorial from local file (#6603) 2024-05-31 10:44:24 +02:00
Florian Rival
eb256b3412 Display some badges to earn on the Get Started page (#6604) 2024-05-31 09:13:34 +02:00
AlexandreS
d89bfd27f8 Fix: Improve analytics panel display (#6601) 2024-05-29 15:56:05 +02:00
D8H
7cef3b3a41 Fix local structure variable declaration generation (#6599)
- Don't show in changelog
2024-05-28 18:48:34 +02:00
AlexandreS
f3a87a2159 Fix local network preview url opening (#6598) 2024-05-28 16:55:19 +02:00
D8H
4df91f3e77 Fix rename refactoring from replacing variable occurrences from a smaller scope (#6596) 2024-05-28 13:49:12 +02:00
D8H
8ca82ef457 Add a button to add local variables (#6593)
- Don't show in changelog
2024-05-28 13:18:14 +02:00
D8H
0c3719ff0c Fix boolean value shown in local variable declarations (#6594)
- Don't show in changelog
2024-05-28 13:17:16 +02:00
D8H
a76ba988b2 Fix variable renaming to avoid to take an object name (#6597) 2024-05-28 13:05:17 +02:00
AlexandreS
8f002541ef Maintain subscriptions bought with PayPal until the end of the paid period (#6592) 2024-05-27 11:52:21 +02:00
D8H
6fd82376a8 Fix group variables autocompletion (#6595)
- Don't show in changelog
2024-05-27 11:20:13 +02:00
D8H
db2d6f90a3 Allow to use any variable in some actions (#6589)
- Don't show in changelog
2024-05-25 00:31:10 +02:00
Florian Rival
f32180d923 Allow to enter Twitter username in profile (#6591)
* If you follow the GDevelop Twitter account, a badge and some credits will be given to your account as a thank you!
2024-05-24 19:20:59 +02:00
D8H
4886cf4fd3 Fix the event height after adding local variables (#6590)
- Also fix local variable dialog cancel.
- Don't show in changelog
2024-05-24 16:39:11 +02:00
D8H
0fd25c381a Allow event-based extensions to declare unified variable parameters (#6588)
- Don't show in changelog
2024-05-24 12:27:47 +02:00
D8H
916e642465 Avoid to change a variable type when checking its type (#6587)
- Don't show in changelog
2024-05-24 10:56:12 +02:00
DavidMLPalma
eaebc16f49 Clean multi-file project folders at each save to avoid stale files (#6501) 2024-05-24 09:25:56 +02:00
D8H
231d92a2fa Allow any variables in for each child loops (#6585)
- Don't show in changelog
2024-05-23 18:30:11 +02:00
AlexandreS
e3cc01e902 Add form in Classroom tab to send education resources samples (#6584) 2024-05-23 16:21:31 +02:00
Florian Rival
fe68286eaf Allow to enter your Tiktok username (#6583)
* If you follow GDevelop page, a badge and some credits will be given to your account as a thank you for following us!
2024-05-22 18:06:26 +02:00
D8H
7d95e44b51 Fix renaming of local variables in sub-events (#6582)
- Don't show in changelog
2024-05-21 19:53:56 +02:00
Florian Rival
9b448f1ddf Update wording when changing subscription
Don't show in changelog
2024-05-21 12:56:08 +02:00
D8H
8f34a100e5 Fix extracted external layout name (#6580)
- Don't show in changelog
2024-05-21 10:10:06 +02:00
github-actions[bot]
cc3a9af8ce Update translations [skip ci] (#6545)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-05-21 08:17:25 +02:00
D8H
75bfa5cb45 Unify variable instructions and handle local variables (#6459)
- Global or scene variables can be used with a unique action and condition.
- Object variables can be used with a unique action and condition.
- Variables need to be declared following the same logic as the new expression syntax.
- Local variable can be declared on events
- Extensions have their own variables
- Show a diagnostic report when a preview is launched and there are missing scene variables, object variables or behaviors.
  - This is especially useful if external events are shared between several scenes.
2024-05-20 23:14:07 +02:00
D8H
5ac8f411fe Fix resources with empty name from being used or created (#6578) 2024-05-20 19:38:16 +02:00
D8H
870247a3df Add the "destroy when finished" parameter in events for opacity tweens (#6579) 2024-05-20 17:48:31 +02:00
D8H
80b68d3bb5 Add a hover message on linked external events when they come from another scene (#6563) 2024-05-20 14:22:46 +02:00
D8H
6e8a8d6868 Fix shared properties not being add to the scene when adding a behavior from the instruction editor (#6577) 2024-05-20 14:15:25 +02:00
AlexandreS
2310f2a618 Fix example opening not working from the example dialog (#6564) 2024-05-15 16:38:51 +02:00
D8H
f8bd3fca00 Add a drop-down menu action to extract selected instances as an external layout (#6561) 2024-05-14 19:34:39 +02:00
Clément Pasteau
92b9203ce8 Hide game templates when searching in asset store (#6556) 2024-05-13 09:18:43 +02:00
Florian Rival
4ca859c8b9 Allow player authentication in a leaderboard (#6552)
* Enable leaderboards to launch authentication and claim an anonymous score that was just sent
* Also automatically attach a score sent to the logged in player (unless deactivated)
* Make various fixes for authentication on iOS
2024-05-10 10:38:33 +02:00
AlexandreS
701b5a3250 Fix platform icons generation for cloud projects from desktop app (#6551) 2024-05-06 10:38:43 +02:00
Arthur Pacaud (arthuro555)
1e87b74d48 Disable caching in preview server (#6553)
* Useful in case the preview is proxied through a CDN

Only show in developer changelog
2024-05-05 16:48:00 +02:00
AlexandreS
b7a4bab53c Few fixes for the compact instance properties editor (#6547)
- Improve panel spacing
- Remove Restore icon on dimension fields if the instance uses the default size.
- When locking/unlocking multiple instances, apply same value to every instances
2024-05-02 11:48:42 +02:00
Clément Pasteau
2223b6516b Improve Marketing plan logic (#6546)
Do not show in changelog
2024-04-29 17:14:29 +02:00
AlexandreS
7d89208e7b Fix cloud project autosave crashing on Firefox versions <=125 (#6544) 2024-04-29 11:56:24 +02:00
AlexandreS
70508292e4 Bump newIDE version (#6541) 2024-04-25 11:38:53 +02:00
github-actions[bot]
c899db1dba Update translations [skip ci] (#6540) 2024-04-25 11:38:34 +02:00
D8H
2fb27f430e Fix "is on platform" condition that was no longer do anything (#6538) 2024-04-25 11:12:33 +02:00
AlexandreS
6cd2400848 Fix animation placeholder vertical centering (#6539) 2024-04-25 09:57:40 +02:00
github-actions[bot]
fadccba757 Update translations [skip ci] (#6533)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-04-24 15:14:35 +02:00
Florian Rival
29a43514ce Add a banner to encourage to fill the github username in the profile (#6536) 2024-04-24 13:35:51 +02:00
Florian Rival
e64ab94f76 Remove dead code (#6535)
Don't show in changelog
2024-04-23 18:22:57 +02:00
Florian Rival
ed969d2909 Add a field to enter the GitHub username in the user profile (#6534) 2024-04-23 17:48:15 +02:00
AlexandreS
77bf67fdf0 Bump newIDE version (#6532) 2024-04-23 10:38:42 +02:00
github-actions[bot]
580c7a370c Update translations [skip ci] (#6520) 2024-04-23 10:24:37 +02:00
Florian Rival
266f0f2b6e Disable rating of feedbacks that have no text 2024-04-22 10:40:34 +02:00
Florian Rival
58af6aacb4 Add tutorials in the Learn page for education subscribers (#6526) 2024-04-21 21:58:08 +02:00
D8H
1c4ee1c928 Fix some crashes when conditions with objectList are used without any behavior (#6529) 2024-04-21 11:16:31 +02:00
AlexandreS
0275476bec Fix cloud project autosave crash happening in particular situations (#6527) 2024-04-19 17:03:30 +02:00
AlexandreS
3f0194acf0 Increase lower toolbars size and padding on mobile (#6525) 2024-04-19 15:47:32 +02:00
AlexandreS
017d8b28c2 Fix copy (#6524)
Don't show in changelog
2024-04-19 11:21:21 +02:00
AlexandreS
4b518ba0fc Fix undefined resources when opening cloud project autosave after a time of inactivity (#6523) 2024-04-18 11:08:16 +02:00
AlexandreS
e4938b25b5 Fix dimensions set to 0 0 0 on instance when switching layer (#6522) 2024-04-18 10:35:38 +02:00
AlexandreS
c56ad2d277 Bump newIDE version (#6521) 2024-04-17 10:10:02 +02:00
github-actions[bot]
f069f9d942 Update translations [skip ci] (#6515)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-04-17 09:35:34 +02:00
D8H
9f7c60a69e Fix the particles emitter not to delete itself when the emission is paused (#6505)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-04-17 09:34:06 +02:00
AlexandreS
774bf0db61 Update limits for pro subscribers in stories (#6519)
Don't show in changelog
2024-04-16 16:49:14 +02:00
AlexandreS
ccd9c5c0ff Fix: Remove useless horizontal scroll in tree view (#6518) 2024-04-16 11:40:04 +02:00
AlexandreS
5b130a34e3 Fix: Holding space now prevents from resize/rotating an instance (#6516)
---------

Co-authored-by: Leonardo Alves <leonardo.alves@tecnico.ulisboa.pt>
2024-04-15 17:34:18 +02:00
AlexandreS
3e6d147660 Instance properties panel rework
The instance properties panel of the scene editor has been reworked into a more compact and user-friendly panel.
2024-04-15 15:29:27 +02:00
github-actions[bot]
433d85db94 Update translations [skip ci] (#6499)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2024-04-15 11:15:04 +02:00
D8H
b75f707b30 Use new icons for variable parameters in events (#6512) 2024-04-09 16:58:25 +02:00
Florian Rival
67799eb3eb Improve user profiles by showing games, asset packs and game templates made by the user (#6511)
* The app now shows the same profiles as those you can see on gd.games
2024-04-09 13:31:31 +02:00
AlexandreS
1173210386 Fix cloud projects autosave feature (#6510) 2024-04-08 19:12:19 +02:00
D8H
007cc48291 Fix missing object name field in some object editors (#6506) 2024-04-05 17:31:47 +02:00
D8H
12813c9ad9 Fix Sprite collision mask updating when animation frames don't share the same one (#6504) 2024-04-05 14:56:44 +02:00
Clément Pasteau
e503e34059 Fix asset pack & game template licenses being correctly purchased (#6502)
* An issue was always selecting the first license when the purchase dialog opened
2024-04-04 10:40:45 +02:00
Clément Pasteau
4bf576a889 Bump to 5.3.198 (#6500) 2024-04-03 17:57:57 +02:00
D8H
eff1c1bcb6 Fix a regression on "animation by index" action when the index is not an integer (#6498) 2024-04-03 15:00:40 +02:00
Clément Pasteau
abce34f2b1 Bump to 5.3.197 (#6497)
Do not show in changelog
2024-04-03 10:08:23 +02:00
github-actions[bot]
07276d5e16 Update translations [skip ci] (#6496)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-04-03 10:07:26 +02:00
Arthur Pacaud (arthuro555)
1fdd8cc792 Add an action to allow disabling P2P direct connections (#6475) 2024-04-02 22:38:41 +02:00
AlexandreS
79e40605d5 Fix collapse/expand arrow on event sheet for light themes (#6495) 2024-04-02 22:36:00 +02:00
D8H
fcc91e3fea Fix issues with Sprite animation frame updates (#6493) 2024-04-02 21:14:48 +02:00
707 changed files with 48691 additions and 8119 deletions

View File

@@ -49,7 +49,8 @@ vector<pair<gd::Expression*, gd::ParameterMetadata> >
ForEachChildVariableEvent::GetAllExpressionsWithMetadata() { ForEachChildVariableEvent::GetAllExpressionsWithMetadata() {
vector<pair<gd::Expression*, gd::ParameterMetadata> > vector<pair<gd::Expression*, gd::ParameterMetadata> >
allExpressionsWithMetadata; allExpressionsWithMetadata;
auto metadata = gd::ParameterMetadata().SetType("scenevar"); auto metadata = gd::ParameterMetadata().SetType("variable");
metadata.SetExtraInfo("AllowUndeclaredVariable");
allExpressionsWithMetadata.push_back( allExpressionsWithMetadata.push_back(
std::make_pair(&iterableVariableName, metadata)); std::make_pair(&iterableVariableName, metadata));
allExpressionsWithMetadata.push_back( allExpressionsWithMetadata.push_back(
@@ -63,7 +64,8 @@ vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
ForEachChildVariableEvent::GetAllExpressionsWithMetadata() const { ForEachChildVariableEvent::GetAllExpressionsWithMetadata() const {
vector<pair<const gd::Expression*, const gd::ParameterMetadata> > vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
allExpressionsWithMetadata; allExpressionsWithMetadata;
auto metadata = gd::ParameterMetadata().SetType("scenevar"); auto metadata = gd::ParameterMetadata().SetType("variable");
metadata.SetExtraInfo("AllowUndeclaredVariable");
allExpressionsWithMetadata.push_back( allExpressionsWithMetadata.push_back(
std::make_pair(&iterableVariableName, metadata)); std::make_pair(&iterableVariableName, metadata));
allExpressionsWithMetadata.push_back( allExpressionsWithMetadata.push_back(

View File

@@ -4,8 +4,8 @@
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_REPEATEVENT_H #pragma once
#define GDCORE_REPEATEVENT_H
#include "GDCore/Events/Event.h" #include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h" #include "GDCore/Events/EventsList.h"
namespace gd { namespace gd {
@@ -36,10 +36,10 @@ class GD_CORE_API RepeatEvent : public gd::BaseEvent {
const gd::InstructionsList& GetActions() const { return actions; }; const gd::InstructionsList& GetActions() const { return actions; };
gd::InstructionsList& GetActions() { return actions; }; gd::InstructionsList& GetActions() { return actions; };
const gd::String& GetRepeatExpression() const { const gd::Expression& GetRepeatExpression() const {
return repeatNumberExpression.GetPlainString(); return repeatNumberExpression;
}; };
void SetRepeatExpression(gd::String repeatNumberExpression_) { void SetRepeatExpressionPlainString(gd::String repeatNumberExpression_) {
repeatNumberExpression = gd::Expression(repeatNumberExpression_); repeatNumberExpression = gd::Expression(repeatNumberExpression_);
}; };
@@ -68,5 +68,3 @@ class GD_CORE_API RepeatEvent : public gd::BaseEvent {
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_REPEATEVENT_H

View File

@@ -15,7 +15,8 @@ using namespace std;
namespace gd { namespace gd {
StandardEvent::StandardEvent() : BaseEvent() {} StandardEvent::StandardEvent()
: BaseEvent(), variables(gd::VariablesContainer::SourceType::Local) {}
StandardEvent::~StandardEvent(){}; StandardEvent::~StandardEvent(){};
@@ -57,6 +58,9 @@ void StandardEvent::SerializeTo(SerializerElement& element) const {
if (!events.IsEmpty()) if (!events.IsEmpty())
gd::EventsListSerialization::SerializeEventsTo(events, gd::EventsListSerialization::SerializeEventsTo(events,
element.AddChild("events")); element.AddChild("events"));
if (HasVariables()) {
variables.SerializeTo(element.AddChild("variables"));
}
} }
void StandardEvent::UnserializeFrom(gd::Project& project, void StandardEvent::UnserializeFrom(gd::Project& project,
@@ -71,6 +75,11 @@ void StandardEvent::UnserializeFrom(gd::Project& project,
gd::EventsListSerialization::UnserializeEventsFrom( gd::EventsListSerialization::UnserializeEventsFrom(
project, events, element.GetChild("events", 0, "Events")); project, events, element.GetChild("events", 0, "Events"));
} }
variables.Clear();
if (element.HasChild("variables")) {
variables.UnserializeFrom(element.GetChild("variables"));
}
} }
} // namespace gd } // namespace gd

View File

@@ -4,13 +4,13 @@
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#if defined(GD_IDE_ONLY) #pragma once
#ifndef GDCORE_STANDARDEVENT_H
#define GDCORE_STANDARDEVENT_H
#include "GDCore/Events/Event.h" #include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h" #include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Instruction.h" #include "GDCore/Events/Instruction.h"
#include "GDCore/Events/InstructionsList.h" #include "GDCore/Events/InstructionsList.h"
#include "GDCore/Project/VariablesContainer.h"
namespace gd { namespace gd {
class Instruction; class Instruction;
class Project; class Project;
@@ -33,6 +33,10 @@ class GD_CORE_API StandardEvent : public gd::BaseEvent {
virtual const gd::EventsList& GetSubEvents() const { return events; }; virtual const gd::EventsList& GetSubEvents() const { return events; };
virtual gd::EventsList& GetSubEvents() { return events; }; virtual gd::EventsList& GetSubEvents() { return events; };
virtual bool CanHaveVariables() const { return true; }
virtual const gd::VariablesContainer& GetVariables() const { return variables; };
virtual gd::VariablesContainer& GetVariables() { return variables; };
const gd::InstructionsList& GetConditions() const { return conditions; }; const gd::InstructionsList& GetConditions() const { return conditions; };
gd::InstructionsList& GetConditions() { return conditions; }; gd::InstructionsList& GetConditions() { return conditions; };
@@ -53,9 +57,7 @@ class GD_CORE_API StandardEvent : public gd::BaseEvent {
gd::InstructionsList conditions; gd::InstructionsList conditions;
gd::InstructionsList actions; gd::InstructionsList actions;
EventsList events; EventsList events;
VariablesContainer variables;
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_STANDARDEVENT_H
#endif

View File

@@ -0,0 +1,117 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <memory>
#include <vector>
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"
namespace gd {
/**
* \brief
*/
class GD_CORE_API ProjectDiagnostic {
public:
enum ErrorType {
UndeclaredVariable,
MissingBehavior,
UnknownObject,
MismatchedObjectType,
};
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(){};
ErrorType GetType() const { return type; };
const gd::String &GetMessage() const { return message; }
const gd::String &GetObjectName() const { return objectName; }
const gd::String &GetActualValue() const { return actualValue; }
const gd::String &GetExpectedValue() const { return expectedValue; }
private:
ErrorType type;
gd::String message;
gd::String objectName;
gd::String actualValue;
gd::String expectedValue;
};
/**
* \brief
*/
class GD_CORE_API DiagnosticReport {
public:
DiagnosticReport(){};
virtual ~DiagnosticReport(){};
void Add(const gd::ProjectDiagnostic &projectDiagnostic) {
projectDiagnostics.push_back(
gd::make_unique<gd::ProjectDiagnostic>(projectDiagnostic));
};
const ProjectDiagnostic &Get(std::size_t index) const {
return *projectDiagnostics[index].get();
};
std::size_t Count() const { return projectDiagnostics.size(); };
const gd::String &GetSceneName() const { return sceneName; }
void SetSceneName(const gd::String &sceneName_) {
sceneName = sceneName_;
}
private:
std::vector<std::unique_ptr<gd::ProjectDiagnostic>> projectDiagnostics;
gd::String sceneName;
};
/**
* \brief
*/
class GD_CORE_API WholeProjectDiagnosticReport {
public:
WholeProjectDiagnosticReport(){};
virtual ~WholeProjectDiagnosticReport(){};
const DiagnosticReport &Get(std::size_t index) const {
return *diagnosticReports[index].get();
};
void Clear() {
diagnosticReports.clear();
};
DiagnosticReport& AddNewDiagnosticReportForScene(const gd::String &sceneName) {
auto diagnosticReport = gd::make_unique<gd::DiagnosticReport>();
diagnosticReport->SetSceneName(sceneName);
diagnosticReports.push_back(std::move(diagnosticReport));
return *diagnosticReports[diagnosticReports.size() - 1].get();
};
std::size_t Count() const { return diagnosticReports.size(); };
bool HasAnyIssue() {
for (auto& diagnosticReport : diagnosticReports) {
if (diagnosticReport->Count() > 0) {
return true;
}
}
return false;
}
private:
std::vector<std::unique_ptr<gd::DiagnosticReport>> diagnosticReports;
};
} // namespace gd

View File

@@ -3,8 +3,8 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef EVENTSCODEGENERATIONCONTEXT_H #pragma once
#define EVENTSCODEGENERATIONCONTEXT_H
#include <map> #include <map>
#include <memory> #include <memory>
#include <set> #include <set>
@@ -325,4 +325,3 @@ class GD_CORE_API EventsCodeGenerationContext {
}; };
} // namespace gd } // namespace gd
#endif // EVENTSCODEGENERATIONCONTEXT_H

View File

@@ -14,6 +14,7 @@
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h" #include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Extensions/Platform.h" #include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h" #include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ExpressionVariableNameFinder.h"
#include "GDCore/Project/Layout.h" #include "GDCore/Project/Layout.h"
#include "GDCore/Project/ObjectsContainer.h" #include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ObjectsContainersList.h" #include "GDCore/Project/ObjectsContainersList.h"
@@ -254,12 +255,12 @@ gd::String EventsCodeGenerator::GenerateMutatorCall(
} }
gd::String operatorStr = arguments[operatorIndex]; gd::String operatorStr = arguments[operatorIndex];
if (operatorStr.size() > 2) if (operatorStr.size() > 2 && operatorStr[0] == '\"') {
operatorStr = operatorStr.substr( operatorStr = operatorStr.substr(
1, 1,
operatorStr.length() - 1 - operatorStr.length() - 1 -
1); // Operator contains quote which must be removed. 1); // Operator contains quote which must be removed.
}
auto mutators = instrInfos.codeExtraInformation.optionalMutators; auto mutators = instrInfos.codeExtraInformation.optionalMutators;
auto mutator = mutators.find(operatorStr); auto mutator = mutators.find(operatorStr);
if (mutator == mutators.end()) { if (mutator == mutators.end()) {
@@ -279,6 +280,9 @@ gd::String EventsCodeGenerator::GenerateMutatorCall(
argumentsStr += arguments[i]; argumentsStr += arguments[i];
} }
} }
if (instrInfos.GetManipulatedType() == "boolean") {
return callStartString + "(" + argumentsStr + ")." + mutator->second;
}
return callStartString + "(" + argumentsStr + ")." + mutator->second + "(" + return callStartString + "(" + argumentsStr + ")." + mutator->second + "(" +
rhs + ")"; rhs + ")";
@@ -318,17 +322,30 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
condition.SetParameters(parameters); condition.SetParameters(parameters);
} }
gd::EventsCodeGenerator::CheckBehaviorParameters(condition, instrInfos);
// Verify that there are no mismatches between object type in parameters. // Verify that there are no mismatches between object type in parameters.
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) { for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) { if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
gd::String objectInParameter = gd::String objectInParameter =
condition.GetParameter(pNb).GetPlainString(); condition.GetParameter(pNb).GetPlainString();
if (!GetObjectsContainersList().HasObjectOrGroupNamed(objectInParameter)) { const auto &expectedObjectType =
instrInfos.parameters[pNb].GetExtraInfo();
const auto &actualObjectType =
GetObjectsContainersList().GetTypeOfObject(objectInParameter);
if (!GetObjectsContainersList().HasObjectOrGroupNamed(
objectInParameter)) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
objectInParameter, "");
diagnosticReport->Add(projectDiagnostic);
return "/* Unknown object - skipped. */"; return "/* Unknown object - skipped. */";
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() && } else if (!expectedObjectType.empty() &&
GetObjectsContainersList().GetTypeOfObject(objectInParameter) != actualObjectType != expectedObjectType) {
instrInfos.parameters[pNb].GetExtraInfo()) { gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
actualObjectType, expectedObjectType, objectInParameter);
diagnosticReport->Add(projectDiagnostic);
return "/* Mismatched object type - skipped. */"; return "/* Mismatched object type - skipped. */";
} }
} }
@@ -364,15 +381,22 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
} }
} }
} else if (instrInfos.IsBehaviorInstruction()) { } else if (instrInfos.IsBehaviorInstruction()) {
gd::String objectName = condition.GetParameter(0).GetPlainString();
gd::String behaviorType = GetObjectsContainersList().GetTypeOfBehavior(condition.GetParameter(1).GetPlainString());
if (instrInfos.parameters.size() >= 2) { if (instrInfos.parameters.size() >= 2) {
const gd::String &objectName = condition.GetParameter(0).GetPlainString();
const gd::String &behaviorName =
condition.GetParameter(1).GetPlainString();
const gd::String &actualBehaviorType =
GetObjectsContainersList().GetTypeOfBehavior(behaviorName);
std::vector<gd::String> realObjects = std::vector<gd::String> realObjects =
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject()); GetObjectsContainersList().ExpandObjectName(
objectName, context.GetCurrentObject());
const BehaviorMetadata &autoInfo =
MetadataProvider::GetBehaviorMetadata(platform, actualBehaviorType);
for (std::size_t i = 0; i < realObjects.size(); ++i) { for (std::size_t i = 0; i < realObjects.size(); ++i) {
// Setup context // Setup context
const BehaviorMetadata& autoInfo =
MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
AddIncludeFiles(autoInfo.includeFiles); AddIncludeFiles(autoInfo.includeFiles);
context.SetCurrentObject(realObjects[i]); context.SetCurrentObject(realObjects[i]);
context.ObjectsListNeeded(realObjects[i]); context.ObjectsListNeeded(realObjects[i]);
@@ -382,7 +406,7 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
condition.GetParameters(), instrInfos.parameters, context); condition.GetParameters(), instrInfos.parameters, context);
conditionCode += GenerateBehaviorCondition( conditionCode += GenerateBehaviorCondition(
realObjects[i], realObjects[i],
condition.GetParameter(1).GetPlainString(), behaviorName,
autoInfo, autoInfo,
arguments, arguments,
instrInfos, instrInfos,
@@ -457,6 +481,33 @@ gd::String EventsCodeGenerator::GenerateConditionsListCode(
return outputCode; return outputCode;
} }
void EventsCodeGenerator::CheckBehaviorParameters(
const gd::Instruction &instruction,
const gd::InstructionMetadata &instrInfos) {
gd::ParameterMetadataTools::IterateOverParameters(
instruction.GetParameters(), instrInfos.parameters,
[this](const gd::ParameterMetadata &parameterMetadata,
const gd::Expression &parameterValue,
const gd::String &lastObjectName) {
if (ParameterMetadata::IsBehavior(parameterMetadata.GetType())) {
const gd::String &behaviorName = parameterValue.GetPlainString();
const gd::String &actualBehaviorType =
GetObjectsContainersList().GetTypeOfBehaviorInObjectOrGroup(
lastObjectName, behaviorName);
const gd::String &expectedBehaviorType =
parameterMetadata.GetExtraInfo();
if (!expectedBehaviorType.empty() &&
actualBehaviorType != expectedBehaviorType) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MissingBehavior, "",
actualBehaviorType, expectedBehaviorType, lastObjectName);
diagnosticReport->Add(projectDiagnostic);
}
}
});
}
/** /**
* Generate code for an action. * Generate code for an action.
*/ */
@@ -494,15 +545,29 @@ gd::String EventsCodeGenerator::GenerateActionCode(
action.SetParameters(parameters); action.SetParameters(parameters);
} }
gd::EventsCodeGenerator::CheckBehaviorParameters(action, instrInfos);
// Verify that there are no mismatches between object type in parameters. // Verify that there are no mismatches between object type in parameters.
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) { for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) { if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
gd::String objectInParameter = action.GetParameter(pNb).GetPlainString(); gd::String objectInParameter = action.GetParameter(pNb).GetPlainString();
if (!GetObjectsContainersList().HasObjectOrGroupNamed(objectInParameter)) {
const auto &expectedObjectType =
instrInfos.parameters[pNb].GetExtraInfo();
const auto &actualObjectType =
GetObjectsContainersList().GetTypeOfObject(objectInParameter);
if (!GetObjectsContainersList().HasObjectOrGroupNamed(
objectInParameter)) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
objectInParameter, "");
diagnosticReport->Add(projectDiagnostic);
return "/* Unknown object - skipped. */"; return "/* Unknown object - skipped. */";
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() && } else if (!expectedObjectType.empty() &&
GetObjectsContainersList().GetTypeOfObject(objectInParameter) != actualObjectType != expectedObjectType) {
instrInfos.parameters[pNb].GetExtraInfo()) { gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
actualObjectType, expectedObjectType, objectInParameter);
diagnosticReport->Add(projectDiagnostic);
return "/* Mismatched object type - skipped. */"; return "/* Mismatched object type - skipped. */";
} }
} }
@@ -540,17 +605,22 @@ gd::String EventsCodeGenerator::GenerateActionCode(
} }
} }
} else if (instrInfos.IsBehaviorInstruction()) { } else if (instrInfos.IsBehaviorInstruction()) {
gd::String objectName = action.GetParameter(0).GetPlainString();
gd::String behaviorType = GetObjectsContainersList().GetTypeOfBehavior(action.GetParameter(1).GetPlainString());
if (instrInfos.parameters.size() >= 2) { if (instrInfos.parameters.size() >= 2) {
const gd::String &objectName = action.GetParameter(0).GetPlainString();
const gd::String &behaviorName = action.GetParameter(1).GetPlainString();
const gd::String &actualBehaviorType =
GetObjectsContainersList().GetTypeOfBehavior(behaviorName);
std::vector<gd::String> realObjects = std::vector<gd::String> realObjects =
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject()); GetObjectsContainersList().ExpandObjectName(
objectName, context.GetCurrentObject());
const BehaviorMetadata &autoInfo =
MetadataProvider::GetBehaviorMetadata(platform, actualBehaviorType);
AddIncludeFiles(autoInfo.includeFiles);
for (std::size_t i = 0; i < realObjects.size(); ++i) { for (std::size_t i = 0; i < realObjects.size(); ++i) {
// Setup context // Setup context
const BehaviorMetadata& autoInfo =
MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
AddIncludeFiles(autoInfo.includeFiles);
context.SetCurrentObject(realObjects[i]); context.SetCurrentObject(realObjects[i]);
context.ObjectsListNeeded(realObjects[i]); context.ObjectsListNeeded(realObjects[i]);
@@ -559,7 +629,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
action.GetParameters(), instrInfos.parameters, context); action.GetParameters(), instrInfos.parameters, context);
actionCode += actionCode +=
GenerateBehaviorAction(realObjects[i], GenerateBehaviorAction(realObjects[i],
action.GetParameter(1).GetPlainString(), behaviorName,
autoInfo, autoInfo,
functionCallName, functionCallName,
arguments, arguments,
@@ -583,6 +653,29 @@ gd::String EventsCodeGenerator::GenerateActionCode(
return actionCode; return actionCode;
} }
gd::String EventsCodeGenerator::GenerateLocalVariablesStackAccessor() {
return (HasProjectAndLayout() ? GetCodeNamespace()
: "eventsFunctionContext") +
".localVariables";
}
gd::String EventsCodeGenerator::GenerateAnyOrSceneVariableGetter(
const gd::Expression &variableExpression,
EventsCodeGenerationContext &context) {
const auto variableName = gd::ExpressionVariableNameFinder::GetVariableName(
*variableExpression.GetRootNode());
gd::String variableParameterType =
GetProjectScopedContainers().GetVariablesContainersList().Has(
variableName)
? "variable"
: "scenevar";
return gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, variableParameterType,
variableExpression.GetPlainString(), "", "AllowUndeclaredVariable");
}
const EventsCodeGenerator::CallbackDescriptor const EventsCodeGenerator::CallbackDescriptor
EventsCodeGenerator::GenerateCallback( EventsCodeGenerator::GenerateCallback(
const gd::String& callbackID, const gd::String& callbackID,
@@ -607,14 +700,20 @@ EventsCodeGenerator::GenerateCallback(
actionsCode += "} //End of subevents\n"; actionsCode += "} //End of subevents\n";
} }
gd::String restoreLocalVariablesCode;
restoreLocalVariablesCode +=
"asyncObjectsList.restoreLocalVariablesContainers(" +
GenerateLocalVariablesStackAccessor() + ");\n";
// Compose the callback function and add outside main // Compose the callback function and add outside main
const gd::String actionsDeclarationsCode = const gd::String actionsDeclarationsCode =
GenerateObjectsDeclarationCode(callbackContext); GenerateObjectsDeclarationCode(callbackContext);
const gd::String callbackCode = callbackFunctionName + " = function (" + const gd::String callbackCode =
GenerateEventsParameters(callbackContext) + callbackFunctionName + " = function (" +
") {\n" + actionsDeclarationsCode + GenerateEventsParameters(callbackContext) + ") {\n" +
actionsCode + "}\n"; restoreLocalVariablesCode +
actionsDeclarationsCode + actionsCode + "}\n";
AddCustomCodeOutsideMain(callbackCode); AddCustomCodeOutsideMain(callbackCode);
@@ -677,13 +776,13 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
if (ParameterMetadata::IsExpression("number", metadata.GetType())) { if (ParameterMetadata::IsExpression("number", metadata.GetType())) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode( argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "number", parameter, lastObjectName); *this, context, "number", parameter, lastObjectName, metadata.GetExtraInfo());
} else if (ParameterMetadata::IsExpression("string", metadata.GetType())) { } else if (ParameterMetadata::IsExpression("string", metadata.GetType())) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode( argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "string", parameter, lastObjectName); *this, context, "string", parameter, lastObjectName, metadata.GetExtraInfo());
} else if (ParameterMetadata::IsExpression("variable", metadata.GetType())) { } else if (ParameterMetadata::IsExpression("variable", metadata.GetType())) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode( argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, metadata.GetType(), parameter, lastObjectName); *this, context, metadata.GetType(), parameter, lastObjectName, metadata.GetExtraInfo());
} else if (ParameterMetadata::IsObject(metadata.GetType())) { } else if (ParameterMetadata::IsObject(metadata.GetType())) {
// It would be possible to run a gd::ExpressionCodeGenerator if later // It would be possible to run a gd::ExpressionCodeGenerator if later
// objects can have nested objects, or function returning objects. // objects can have nested objects, or function returning objects.
@@ -695,7 +794,8 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
} else if (metadata.GetType() == "operator") { } else if (metadata.GetType() == "operator") {
argOutput += parameter.GetPlainString(); argOutput += parameter.GetPlainString();
if (argOutput != "=" && argOutput != "+" && argOutput != "-" && if (argOutput != "=" && argOutput != "+" && argOutput != "-" &&
argOutput != "/" && argOutput != "*") { argOutput != "/" && argOutput != "*" && argOutput != "True" &&
argOutput != "False" && argOutput != "Toggle") {
cout << "Warning: Bad operator: Set to = by default." << endl; cout << "Warning: Bad operator: Set to = by default." << endl;
argOutput = "="; argOutput = "=";
} }
@@ -865,6 +965,11 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
gd::EventsList& events, EventsCodeGenerationContext& parentContext) { gd::EventsList& events, EventsCodeGenerationContext& parentContext) {
gd::String output; gd::String output;
for (std::size_t eId = 0; eId < events.size(); ++eId) { for (std::size_t eId = 0; eId < events.size(); ++eId) {
auto& event = events[eId];
if (event.HasVariables()) {
GetProjectScopedContainers().GetVariablesContainersList().Push(event.GetVariables());
}
// Each event has its own context : Objects picked in an event are totally // Each event has its own context : Objects picked in an event are totally
// different than the one picked in another. // different than the one picked in another.
gd::EventsCodeGenerationContext newContext; gd::EventsCodeGenerationContext newContext;
@@ -885,13 +990,17 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
auto& context = reuseParentContext ? reusedContext : newContext; auto& context = reuseParentContext ? reusedContext : newContext;
gd::String eventCoreCode = events[eId].GenerateEventCode(*this, context); gd::String eventCoreCode = event.GenerateEventCode(*this, context);
gd::String scopeBegin = GenerateScopeBegin(context); gd::String scopeBegin = GenerateScopeBegin(context);
gd::String scopeEnd = GenerateScopeEnd(context); gd::String scopeEnd = GenerateScopeEnd(context);
gd::String declarationsCode = GenerateObjectsDeclarationCode(context); gd::String declarationsCode = GenerateObjectsDeclarationCode(context);
output += "\n" + scopeBegin + "\n" + declarationsCode + "\n" + output += "\n" + scopeBegin + "\n" + declarationsCode + "\n" +
eventCoreCode + "\n" + scopeEnd + "\n"; eventCoreCode + "\n" + scopeEnd + "\n";
if (event.HasVariables()) {
GetProjectScopedContainers().GetVariablesContainersList().Pop();
}
} }
return output; return output;
@@ -1067,7 +1176,8 @@ gd::String EventsCodeGenerator::GenerateFreeAction(
// Generate call // Generate call
gd::String call; gd::String call;
if (instrInfos.codeExtraInformation.type == "number" || if (instrInfos.codeExtraInformation.type == "number" ||
instrInfos.codeExtraInformation.type == "string") { instrInfos.codeExtraInformation.type == "string" ||
instrInfos.codeExtraInformation.type == "boolean") {
if (instrInfos.codeExtraInformation.accessType == if (instrInfos.codeExtraInformation.accessType ==
gd::InstructionMetadata::ExtraInformation::MutatorAndOrAccessor) gd::InstructionMetadata::ExtraInformation::MutatorAndOrAccessor)
call = GenerateOperatorCall( call = GenerateOperatorCall(
@@ -1251,7 +1361,8 @@ EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
compilationForRuntime(false), compilationForRuntime(false),
maxCustomConditionsDepth(0), maxCustomConditionsDepth(0),
maxConditionsListsSize(0), maxConditionsListsSize(0),
eventsListNextUniqueId(0){}; eventsListNextUniqueId(0),
diagnosticReport(nullptr){};
EventsCodeGenerator::EventsCodeGenerator( EventsCodeGenerator::EventsCodeGenerator(
const gd::Platform& platform_, const gd::Platform& platform_,
@@ -1265,6 +1376,7 @@ EventsCodeGenerator::EventsCodeGenerator(
compilationForRuntime(false), compilationForRuntime(false),
maxCustomConditionsDepth(0), maxCustomConditionsDepth(0),
maxConditionsListsSize(0), maxConditionsListsSize(0),
eventsListNextUniqueId(0){}; eventsListNextUniqueId(0),
diagnosticReport(nullptr){};
} // namespace gd } // namespace gd

View File

@@ -3,8 +3,7 @@
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_EVENTSCODEGENERATOR_H #pragma once
#define GDCORE_EVENTSCODEGENERATOR_H
#include <set> #include <set>
#include <utility> #include <utility>
@@ -12,8 +11,10 @@
#include "GDCore/Events/Event.h" #include "GDCore/Events/Event.h"
#include "GDCore/Events/Instruction.h" #include "GDCore/Events/Instruction.h"
#include "GDCore/Events/CodeGeneration/DiagnosticReport.h"
#include "GDCore/Project/ProjectScopedContainers.h" #include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/String.h" #include "GDCore/String.h"
namespace gd { namespace gd {
class EventsList; class EventsList;
class Expression; class Expression;
@@ -336,6 +337,16 @@ class GD_CORE_API EventsCodeGenerator {
return projectScopedContainers; return projectScopedContainers;
} }
/**
* @brief Give access to the project scoped containers as code generation might
* push and pop variable containers (for local variables).
* This could be passed as a parameter recursively in code generation, but this requires
* heavy refactoring. Instead, we use this single instance.
*/
gd::ProjectScopedContainers& GetProjectScopedContainers() {
return projectScopedContainers;
}
/** /**
* \brief Return true if the code generation is done for a given project and * \brief Return true if the code generation is done for a given project and
* layout. If not, this means that the code is generated for a function. * layout. If not, this means that the code is generated for a function.
@@ -372,6 +383,14 @@ class GD_CORE_API EventsCodeGenerator {
*/ */
size_t GetMaxConditionsListsSize() const { return maxConditionsListsSize; } size_t GetMaxConditionsListsSize() const { return maxConditionsListsSize; }
void SetDiagnosticReport(gd::DiagnosticReport* diagnosticReport_) {
diagnosticReport = diagnosticReport_;
}
gd::DiagnosticReport* GetDiagnosticReport() {
return diagnosticReport;
}
/** /**
* \brief Generate the full name for accessing to a boolean variable used for * \brief Generate the full name for accessing to a boolean variable used for
* conditions. * conditions.
@@ -448,7 +467,7 @@ class GD_CORE_API EventsCodeGenerator {
*/ */
virtual gd::String GetCodeNamespace() { return ""; }; virtual gd::String GetCodeNamespace() { return ""; };
enum VariableScope { LAYOUT_VARIABLE = 0, PROJECT_VARIABLE, OBJECT_VARIABLE }; enum VariableScope { LAYOUT_VARIABLE = 0, PROJECT_VARIABLE, OBJECT_VARIABLE, ANY_VARIABLE };
/** /**
* Generate a single unique number for the specified instruction. * Generate a single unique number for the specified instruction.
@@ -478,7 +497,20 @@ class GD_CORE_API EventsCodeGenerator {
const gd::String& lhs, const gd::String& lhs,
const gd::String& rhs); const gd::String& rhs);
protected: /**
* \brief Generate the code to access the local variables stack.
*/
virtual gd::String GenerateLocalVariablesStackAccessor();
/**
* \brief Generate an any variable getter that fallbacks on scene variable for
* compatibility reason.
*/
gd::String
GenerateAnyOrSceneVariableGetter(const gd::Expression &variableExpression,
EventsCodeGenerationContext &context);
protected:
virtual const gd::String GenerateRelationalOperatorCodes( virtual const gd::String GenerateRelationalOperatorCodes(
const gd::String& operatorString); const gd::String& operatorString);
@@ -534,11 +566,15 @@ class GD_CORE_API EventsCodeGenerator {
const VariableScope& scope, const VariableScope& scope,
gd::EventsCodeGenerationContext& context, gd::EventsCodeGenerationContext& context,
const gd::String& objectName) { const gd::String& objectName) {
// This code is only used as a mock.
// See the real implementation in GDJS.
if (scope == LAYOUT_VARIABLE) { if (scope == LAYOUT_VARIABLE) {
return "getLayoutVariable(" + variableName + ")"; return "getLayoutVariable(" + variableName + ")";
} else if (scope == PROJECT_VARIABLE) { } else if (scope == PROJECT_VARIABLE) {
return "getProjectVariable(" + variableName + ")"; return "getProjectVariable(" + variableName + ")";
} else if (scope == ANY_VARIABLE) {
return "getAnyVariable(" + variableName + ")";
} }
return "getVariableForObject(" + objectName + ", " + variableName + ")"; return "getVariableForObject(" + objectName + ", " + variableName + ")";
@@ -779,6 +815,10 @@ class GD_CORE_API EventsCodeGenerator {
virtual gd::String GenerateGetBehaviorNameCode( virtual gd::String GenerateGetBehaviorNameCode(
const gd::String& behaviorName); const gd::String& behaviorName);
void CheckBehaviorParameters(
const gd::Instruction &instruction,
const gd::InstructionMetadata &instrInfos);
const gd::Platform& platform; ///< The platform being used. const gd::Platform& platform; ///< The platform being used.
gd::ProjectScopedContainers projectScopedContainers; gd::ProjectScopedContainers projectScopedContainers;
@@ -809,8 +849,9 @@ class GD_CORE_API EventsCodeGenerator {
instructionUniqueIds; ///< The unique ids generated for instructions. instructionUniqueIds; ///< The unique ids generated for instructions.
size_t eventsListNextUniqueId; ///< The next identifier to use for an events size_t eventsListNextUniqueId; ///< The next identifier to use for an events
///< list function name. ///< list function name.
gd::DiagnosticReport* diagnosticReport;
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_EVENTSCODEGENERATOR_H

View File

@@ -31,6 +31,7 @@
#include "GDCore/Project/ProjectScopedContainers.h" #include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h" #include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h" #include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/Events/CodeGeneration/DiagnosticReport.h"
namespace gd { namespace gd {
@@ -39,7 +40,8 @@ gd::String ExpressionCodeGenerator::GenerateExpressionCode(
EventsCodeGenerationContext& context, EventsCodeGenerationContext& context,
const gd::String& rootType, const gd::String& rootType,
const gd::Expression& expression, const gd::Expression& expression,
const gd::String& rootObjectName) { const gd::String& rootObjectName,
const gd::String& extraInfo) {
ExpressionCodeGenerator generator(rootType, rootObjectName, codeGenerator, context); ExpressionCodeGenerator generator(rootType, rootObjectName, codeGenerator, context);
auto node = expression.GetRootNode(); auto node = expression.GetRootNode();
@@ -52,13 +54,34 @@ gd::String ExpressionCodeGenerator::GenerateExpressionCode(
gd::ExpressionValidator validator(codeGenerator.GetPlatform(), gd::ExpressionValidator validator(codeGenerator.GetPlatform(),
codeGenerator.GetProjectScopedContainers(), codeGenerator.GetProjectScopedContainers(),
rootType); rootType,
extraInfo);
node->Visit(validator); node->Visit(validator);
if (!validator.GetFatalErrors().empty()) { if (!validator.GetFatalErrors().empty()) {
std::cout << "Error: \"" << validator.GetFatalErrors()[0]->GetMessage() std::cout << "Error: \"" << validator.GetFatalErrors()[0]->GetMessage()
<< "\" in: \"" << expression.GetPlainString() << "\" (" << "\" in: \"" << expression.GetPlainString() << "\" ("
<< rootType << ")" << std::endl; << rootType << ")" << std::endl;
auto *diagnosticReport = codeGenerator.GetDiagnosticReport();
if (diagnosticReport) {
for (auto *error : validator.GetFatalErrors()) {
if (error->GetType() ==
gd::ExpressionParserError::ErrorType::UndeclaredVariable ||
error->GetType() ==
gd::ExpressionParserError::ErrorType::UnknownIdentifier) {
const auto& variableName = error->GetActualValue();
if (!variableName.empty()) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::UndeclaredVariable,
error->GetMessage(), error->GetActualValue(),
"", error->GetObjectName());
diagnosticReport->Add(projectDiagnostic);
}
}
}
}
return generator.GenerateDefaultValue(rootType); return generator.GenerateDefaultValue(rootType);
} }
@@ -110,11 +133,13 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
if (gd::ParameterMetadata::IsExpression("variable", type)) { if (gd::ParameterMetadata::IsExpression("variable", type)) {
// The node is a variable inside an expression waiting for a *variable* to be returned, not its value. // The node is a variable inside an expression waiting for a *variable* to be returned, not its value.
EventsCodeGenerator::VariableScope scope = EventsCodeGenerator::VariableScope scope =
type == "globalvar" type == "variable"
? gd::EventsCodeGenerator::ANY_VARIABLE
: type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE ? gd::EventsCodeGenerator::PROJECT_VARIABLE
: ((type == "scenevar") : type == "scenevar"
? gd::EventsCodeGenerator::LAYOUT_VARIABLE ? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE); : gd::EventsCodeGenerator::OBJECT_VARIABLE;
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(), auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetObjectsContainersList(), codeGenerator.GetObjectsContainersList(),
@@ -137,19 +162,8 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
output += codeGenerator.GenerateVariableValueAs(type); output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() { }, [&]() {
if (!codeGenerator.HasProjectAndLayout()) { output += codeGenerator.GenerateGetVariable(
gd::LogWarning("Tried to generate access to a variable without a project/scene - the code generator only works for global and scene variables for now."); node.name, gd::EventsCodeGenerator::ANY_VARIABLE, context, "");
output += GenerateDefaultValue(type);
return;
}
// This could be adapted in the future if more scopes are supported.
EventsCodeGenerator::VariableScope scope = gd::EventsCodeGenerator::PROJECT_VARIABLE;
if (codeGenerator.GetProjectScopedContainers().GetVariablesContainersList().GetBottomMostVariablesContainer()->Has(node.name)) {
scope = gd::EventsCodeGenerator::LAYOUT_VARIABLE;
}
output += codeGenerator.GenerateGetVariable(node.name, scope, context, "");
if (node.child) node.child->Visit(*this); if (node.child) node.child->Visit(*this);
output += codeGenerator.GenerateVariableValueAs(type); output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() { }, [&]() {
@@ -209,11 +223,13 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
codeGenerator.GenerateObject(node.identifierName, type, context); codeGenerator.GenerateObject(node.identifierName, type, context);
} else if (gd::ParameterMetadata::IsExpression("variable", type)) { } else if (gd::ParameterMetadata::IsExpression("variable", type)) {
EventsCodeGenerator::VariableScope scope = EventsCodeGenerator::VariableScope scope =
type == "globalvar" type == "variable"
? gd::EventsCodeGenerator::ANY_VARIABLE
: type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE ? gd::EventsCodeGenerator::PROJECT_VARIABLE
: ((type == "scenevar") : type == "scenevar"
? gd::EventsCodeGenerator::LAYOUT_VARIABLE ? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE); : gd::EventsCodeGenerator::OBJECT_VARIABLE;
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(), auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetObjectsContainersList(), codeGenerator.GetObjectsContainersList(),
@@ -236,19 +252,9 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
node.childIdentifierName, gd::EventsCodeGenerator::OBJECT_VARIABLE, context, node.identifierName); node.childIdentifierName, gd::EventsCodeGenerator::OBJECT_VARIABLE, context, node.identifierName);
output += codeGenerator.GenerateVariableValueAs(type); output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() { }, [&]() {
if (!codeGenerator.HasProjectAndLayout()) { output += codeGenerator.GenerateGetVariable(
gd::LogWarning("Tried to generate access to a variable without a project/scene - the code generator only works for global and scene variables for now."); node.identifierName, gd::EventsCodeGenerator::ANY_VARIABLE, context,
output += GenerateDefaultValue(type); "");
return;
}
// This could be adapted in the future if more scopes are supported at runtime.
EventsCodeGenerator::VariableScope scope = gd::EventsCodeGenerator::PROJECT_VARIABLE;
if (variablesContainersList.GetBottomMostVariablesContainer()->Has(node.identifierName)) {
scope = gd::EventsCodeGenerator::LAYOUT_VARIABLE;
}
output += codeGenerator.GenerateGetVariable(node.identifierName, scope, context, "");
if (!node.childIdentifierName.empty()) { if (!node.childIdentifierName.empty()) {
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName); output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
} }

View File

@@ -59,7 +59,8 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
EventsCodeGenerationContext& context, EventsCodeGenerationContext& context,
const gd::String& type, const gd::String& type,
const gd::Expression& expression, const gd::Expression& expression,
const gd::String& objectName = ""); const gd::String& objectName = "",
const gd::String& extraInfo = "");
const gd::String& GetOutput() { return output; }; const gd::String& GetOutput() { return output; };

View File

@@ -17,6 +17,7 @@
namespace gd { namespace gd {
EventsList BaseEvent::badSubEvents; EventsList BaseEvent::badSubEvents;
VariablesContainer BaseEvent::badLocalVariables;
std::vector<gd::String> BaseEvent::emptyDependencies; std::vector<gd::String> BaseEvent::emptyDependencies;
gd::String BaseEvent::emptySourceFile; gd::String BaseEvent::emptySourceFile;
@@ -28,6 +29,8 @@ BaseEvent::BaseEvent()
bool BaseEvent::HasSubEvents() const { return !GetSubEvents().IsEmpty(); } bool BaseEvent::HasSubEvents() const { return !GetSubEvents().IsEmpty(); }
bool BaseEvent::HasVariables() const { return GetVariables().Count() > 0; }
gd::String BaseEvent::GenerateEventCode( gd::String BaseEvent::GenerateEventCode(
gd::EventsCodeGenerator& codeGenerator, gd::EventsCodeGenerator& codeGenerator,
gd::EventsCodeGenerationContext& context) { gd::EventsCodeGenerationContext& context) {

View File

@@ -3,9 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#if defined(GD_IDE_ONLY) #pragma once
#ifndef GDCORE_EVENT_H
#define GDCORE_EVENT_H
#include <iostream> #include <iostream>
#include <memory> #include <memory>
@@ -26,6 +24,7 @@ class SerializerElement;
class Instruction; class Instruction;
class EventVisitor; class EventVisitor;
class ReadOnlyEventVisitor; class ReadOnlyEventVisitor;
class VariablesContainer;
} // namespace gd } // namespace gd
namespace gd { namespace gd {
@@ -92,6 +91,32 @@ class GD_CORE_API BaseEvent {
*/ */
bool HasSubEvents() const; bool HasSubEvents() const;
/**
* Derived class have to redefine this function, so as to return true, if they
* can have local variables.
*/
virtual bool CanHaveVariables() const { return false; }
/**
* Return the local variables, if applicable.
*/
virtual const gd::VariablesContainer& GetVariables() const {
return badLocalVariables;
};
/**
* Return the local variables, if applicable.
*/
virtual gd::VariablesContainer& GetVariables() {
return badLocalVariables;
};
/**
* \brief Return true if the events has local variables.
* \warning This is only applicable when CanHaveVariables() return true.
*/
bool HasVariables() const;
/** /**
* \brief Return a list of all conditions of the event. * \brief Return a list of all conditions of the event.
* \note Used to preprocess or search in the conditions. * \note Used to preprocess or search in the conditions.
@@ -301,6 +326,7 @@ class GD_CORE_API BaseEvent {
///< Used for saving the event for instance. ///< Used for saving the event for instance.
static gd::EventsList badSubEvents; static gd::EventsList badSubEvents;
static gd::VariablesContainer badLocalVariables;
static std::vector<gd::String> emptyDependencies; static std::vector<gd::String> emptyDependencies;
static gd::String emptySourceFile; static gd::String emptySourceFile;
}; };
@@ -325,6 +351,3 @@ class EmptyEvent : public BaseEvent {
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_EVENT_H
#endif

View File

@@ -38,7 +38,7 @@ using namespace gd::GrammarTerminals;
* parser by refactoring out the dependency on gd::MetadataProvider (injecting * parser by refactoring out the dependency on gd::MetadataProvider (injecting
* instead functions to be called to query supported functions). * instead functions to be called to query supported functions).
* *
* \see gd::ExpressionParserDiagnostic * \see gd::ExpressionParserError
* \see gd::ExpressionNode * \see gd::ExpressionNode
*/ */
class GD_CORE_API ExpressionParser2 { class GD_CORE_API ExpressionParser2 {
@@ -547,28 +547,28 @@ class GD_CORE_API ExpressionParser2 {
} }
///@} ///@}
std::unique_ptr<ExpressionParserDiagnostic> ValidateOperator( std::unique_ptr<ExpressionParserError> ValidateOperator(
gd::String::value_type operatorChar) { gd::String::value_type operatorChar) {
if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' || if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
operatorChar == '*') { operatorChar == '*') {
return gd::make_unique<ExpressionParserDiagnostic>(); return std::unique_ptr<ExpressionParserError>(nullptr);
} }
return gd::make_unique<ExpressionParserError>( return gd::make_unique<ExpressionParserError>(
"invalid_operator", gd::ExpressionParserError::ErrorType::InvalidOperator,
_("You've used an operator that is not supported. Operator should be " _("You've used an operator that is not supported. Operator should be "
"either +, -, / or *."), "either +, -, / or *."),
GetCurrentPosition()); GetCurrentPosition());
} }
std::unique_ptr<ExpressionParserDiagnostic> ValidateUnaryOperator( std::unique_ptr<ExpressionParserError> ValidateUnaryOperator(
gd::String::value_type operatorChar, gd::String::value_type operatorChar,
size_t position) { size_t position) {
if (operatorChar == '+' || operatorChar == '-') { if (operatorChar == '+' || operatorChar == '-') {
return gd::make_unique<ExpressionParserDiagnostic>(); return std::unique_ptr<ExpressionParserError>(nullptr);
} }
return gd::make_unique<ExpressionParserError>( return gd::make_unique<ExpressionParserError>(
"invalid_operator", gd::ExpressionParserError::ErrorType::InvalidOperator,
_("You've used an \"unary\" operator that is not supported. Operator " _("You've used an \"unary\" operator that is not supported. Operator "
"should be " "should be "
"either + or -."), "either + or -."),
@@ -719,13 +719,15 @@ class GD_CORE_API ExpressionParser2 {
std::unique_ptr<ExpressionParserError> RaiseSyntaxError( std::unique_ptr<ExpressionParserError> RaiseSyntaxError(
const gd::String &message) { const gd::String &message) {
return std::move(gd::make_unique<ExpressionParserError>( return std::move(gd::make_unique<ExpressionParserError>(
"syntax_error", message, GetCurrentPosition())); gd::ExpressionParserError::ErrorType::SyntaxError, message,
GetCurrentPosition()));
} }
std::unique_ptr<ExpressionParserError> RaiseTypeError( std::unique_ptr<ExpressionParserError> RaiseTypeError(
const gd::String &message, size_t beginningPosition) { const gd::String &message, size_t beginningPosition) {
return std::move(gd::make_unique<ExpressionParserError>( return std::move(gd::make_unique<ExpressionParserError>(
"type_error", message, beginningPosition, GetCurrentPosition())); gd::ExpressionParserError::ErrorType::MismatchedType, message,
beginningPosition, GetCurrentPosition()));
} }
///@} ///@}

View File

@@ -6,5 +6,5 @@
#include "ExpressionParser2Node.h" #include "ExpressionParser2Node.h"
namespace gd { namespace gd {
gd::String ExpressionParserDiagnostic::noMessage = "";
} }

View File

@@ -3,14 +3,14 @@
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_EXPRESSIONPARSER2NODES_H #pragma once
#define GDCORE_EXPRESSIONPARSER2NODES_H
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "ExpressionParser2NodeWorker.h" #include "ExpressionParser2NodeWorker.h"
#include "GDCore/String.h" #include "GDCore/String.h"
namespace gd { namespace gd {
class Expression; class Expression;
class ObjectsContainer; class ObjectsContainer;
@@ -40,50 +40,56 @@ struct GD_CORE_API ExpressionParserLocation {
size_t endPosition; size_t endPosition;
}; };
/**
* \brief A diagnostic that can be attached to a gd::ExpressionNode.
*/
struct GD_CORE_API ExpressionParserDiagnostic {
virtual ~ExpressionParserDiagnostic() = default;
virtual bool IsError() { return false; }
virtual const gd::String &GetMessage() { return noMessage; }
virtual size_t GetStartPosition() { return 0; }
virtual size_t GetEndPosition() { return 0; }
private:
static gd::String noMessage;
};
/** /**
* \brief An error that can be attached to a gd::ExpressionNode. * \brief An error that can be attached to a gd::ExpressionNode.
*/ */
struct GD_CORE_API ExpressionParserError : public ExpressionParserDiagnostic { struct GD_CORE_API ExpressionParserError {
ExpressionParserError(const gd::String &type_, enum ErrorType {
SyntaxError,
InvalidOperator,
MismatchedType,
UndeclaredVariable,
UnknownIdentifier,
BracketsNotAllowedForObjects,
TooFewParameters,
TooManyParameters,
InvalidFunctionName,
MalformedVariableParameter,
MalformedObjectParameter,
UnknownParameterType,
MissingBehavior,
};
ExpressionParserError(gd::ExpressionParserError::ErrorType type_,
const gd::String &message_, const gd::String &message_,
const ExpressionParserLocation &location_) const ExpressionParserLocation &location_,
: type(type_), message(message_), location(location_){}; const gd::String &actualValue_ = "",
ExpressionParserError(const gd::String &type_, const gd::String &objectName_ = "")
const gd::String &message_, : type(type_), message(message_), location(location_),
size_t position_) actualValue(actualValue_), objectName(objectName_){};
ExpressionParserError(gd::ExpressionParserError::ErrorType type_,
const gd::String &message_, size_t position_)
: type(type_), message(message_), location(position_){}; : type(type_), message(message_), location(position_){};
ExpressionParserError(const gd::String &type_, ExpressionParserError(gd::ExpressionParserError::ErrorType type_,
const gd::String &message_, const gd::String &message_, size_t startPosition_,
size_t startPosition_,
size_t endPosition_) size_t endPosition_)
: type(type_), : type(type_), message(message_),
message(message_),
location(startPosition_, endPosition_){}; location(startPosition_, endPosition_){};
virtual ~ExpressionParserError(){}; virtual ~ExpressionParserError(){};
bool IsError() override { return true; } gd::ExpressionParserError::ErrorType GetType() { return type; }
const gd::String &GetMessage() override { return message; } const gd::String &GetMessage() { return message; }
size_t GetStartPosition() override { return location.GetStartPosition(); } const gd::String &GetObjectName() { return objectName; }
size_t GetEndPosition() override { return location.GetEndPosition(); } const gd::String &GetActualValue() { return actualValue; }
size_t GetStartPosition() { return location.GetStartPosition(); }
size_t GetEndPosition() { return location.GetEndPosition(); }
private: private:
gd::String type; gd::ExpressionParserError::ErrorType type;
gd::String message; gd::String message;
ExpressionParserLocation location; ExpressionParserLocation location;
gd::String objectName;
gd::String actualValue;
}; };
/** /**
@@ -95,7 +101,7 @@ struct GD_CORE_API ExpressionNode {
virtual ~ExpressionNode(){}; virtual ~ExpressionNode(){};
virtual void Visit(ExpressionParser2NodeWorker &worker){}; virtual void Visit(ExpressionParser2NodeWorker &worker){};
std::unique_ptr<ExpressionParserDiagnostic> diagnostic; std::unique_ptr<ExpressionParserError> diagnostic;
ExpressionParserLocation location; ///< The location of the entire node. Some ExpressionParserLocation location; ///< The location of the entire node. Some
/// nodes might have other locations /// nodes might have other locations
/// stored inside them. For example, a /// stored inside them. For example, a
@@ -425,5 +431,3 @@ struct GD_CORE_API EmptyNode : public FunctionCallOrObjectFunctionNameOrEmptyNod
}; };
} // namespace gd } // namespace gd
#endif

View File

@@ -66,6 +66,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.SetRelevantForFunctionEventsOnly() .SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
// Deprecated
extension extension
.AddAction("CopyArgumentToVariable", .AddAction("CopyArgumentToVariable",
_("Copy function parameter to variable"), _("Copy function parameter to variable"),
@@ -78,9 +79,25 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.SetHelpPath("/events/functions/return") .SetHelpPath("/events/functions/return")
.AddParameter("functionParameterName", _("Parameter name"), "variable") .AddParameter("functionParameterName", _("Parameter name"), "variable")
.AddParameter("scenevar", _("Scene variable")) .AddParameter("scenevar", _("Scene variable"))
.SetHidden()
.MarkAsAdvanced();
extension
.AddAction("CopyArgumentToVariable2",
_("Copy function parameter to variable"),
_("Copy a function parameter (also called \"argument\") to a variable. "
"The parameter type must be a variable."),
_("Copy the parameter _PARAM0_ into the variable _PARAM1_"),
"",
"res/function32.png",
"res/function32.png")
.SetHelpPath("/events/functions/return")
.AddParameter("functionParameterName", _("Parameter name"), "variable")
.AddParameter("variable", _("Variable"))
.SetRelevantForFunctionEventsOnly() .SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
// Deprecated
extension extension
.AddAction("CopyVariableToArgument", .AddAction("CopyVariableToArgument",
_("Copy variable to function parameter"), _("Copy variable to function parameter"),
@@ -93,6 +110,21 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
.SetHelpPath("/events/functions/return") .SetHelpPath("/events/functions/return")
.AddParameter("functionParameterName", _("Parameter name"), "variable") .AddParameter("functionParameterName", _("Parameter name"), "variable")
.AddParameter("scenevar", _("Scene variable")) .AddParameter("scenevar", _("Scene variable"))
.SetHidden()
.MarkAsAdvanced();
extension
.AddAction("CopyVariableToArgument2",
_("Copy variable to function parameter"),
_("Copy a variable to function parameter (also called \"argument\"). "
"The parameter type must be a variable."),
_("Copy the variable _PARAM1_ into the parameter _PARAM0_"),
"",
"res/function32.png",
"res/function32.png")
.SetHelpPath("/events/functions/return")
.AddParameter("functionParameterName", _("Parameter name"), "variable")
.AddParameter("variable", _("Variable"))
.SetRelevantForFunctionEventsOnly() .SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();

View File

@@ -417,6 +417,93 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.SetDefaultValue("\"\"") .SetDefaultValue("\"\"")
.MarkAsAdvanced(); .MarkAsAdvanced();
obj.AddAction("SetNumberObjectVariable",
_("Change variable value"),
_("Modify the number value of an object variable."),
_("the variable _PARAM1_"),
_("Variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions())
.SetRelevantForLayoutEventsOnly();
obj.AddAction("SetStringObjectVariable",
_("Change text variable"),
_("Modify the text of an object variable."),
_("the variable _PARAM1_"),
_("Variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions())
.SetRelevantForLayoutEventsOnly();
obj.AddAction("SetBooleanObjectVariable",
_("Change boolean variable"),
_("Modify the boolean value of an object variable."),
_("Change the variable _PARAM1_ of _PARAM0_: _PARAM2_"),
_("Variables"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("operator", _("Value"), "boolean")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("yesorno", _("Value"))
.SetRelevantForLayoutEventsOnly();
obj.AddCondition("NumberObjectVariable",
_("Variable value"),
_("Compare the number value of an object variable."),
_("the variable _PARAM1_"),
_("Variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.SetRelevantForLayoutEventsOnly();
obj.AddCondition("StringObjectVariable",
_("Text variable"),
_("Compare the text of an object variable."),
_("the variable _PARAM1_"),
_("Variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions())
.SetRelevantForLayoutEventsOnly();
obj.AddCondition("BooleanObjectVariable",
_("Boolean variable"),
_("Compare the boolean value of an object variable."),
_("The variable _PARAM1_ of _PARAM0_ is _PARAM2_"),
_("Variables"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("yesorno", _("Value"))
.SetRelevantForLayoutEventsOnly();
obj.AddAction("ModVarObjet", obj.AddAction("ModVarObjet",
_("Change number variable"), _("Change number variable"),
_("Modify the number value of an object variable."), _("Modify the number value of an object variable."),
@@ -428,7 +515,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable")) .AddParameter("objectvar", _("Variable"))
.UseStandardOperatorParameters("number", .UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions()); ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
obj.AddAction("ModVarObjetTxt", obj.AddAction("ModVarObjetTxt",
_("Change text variable"), _("Change text variable"),
@@ -441,7 +529,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable")) .AddParameter("objectvar", _("Variable"))
.UseStandardOperatorParameters("string", .UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions()); ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
obj.AddAction("SetObjectVariableAsBoolean", obj.AddAction("SetObjectVariableAsBoolean",
_("Change boolean variable"), _("Change boolean variable"),
@@ -454,7 +543,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable")) .AddParameter("objectvar", _("Variable"))
.AddParameter("trueorfalse", _("New Value:")); .AddParameter("trueorfalse", _("New Value:"))
.SetRelevantForFunctionEventsOnly();
obj.AddAction( obj.AddAction(
"ToggleObjectVariableAsBoolean", "ToggleObjectVariableAsBoolean",
@@ -469,7 +559,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/var.png") "res/actions/var.png")
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable")); .AddParameter("objectvar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
obj.AddCondition("ObjectVariableChildExists", obj.AddCondition("ObjectVariableChildExists",
_("Child existence"), _("Child existence"),
@@ -657,7 +748,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable")) .AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters( .UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions()); "number", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
obj.AddCondition("VarObjetTxt", obj.AddCondition("VarObjetTxt",
_("Text variable"), _("Text variable"),
@@ -670,7 +762,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable")) .AddParameter("objectvar", _("Variable"))
.UseStandardRelationalOperatorParameters( .UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions()); "string", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
obj.AddCondition("ObjectVariableAsBoolean", obj.AddCondition("ObjectVariableAsBoolean",
_("Boolean variable"), _("Boolean variable"),
@@ -683,7 +776,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable")) .AddParameter("objectvar", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is")) .AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true"); .SetDefaultValue("true")
.SetRelevantForFunctionEventsOnly();
obj.AddCondition("VarObjetDef", obj.AddCondition("VarObjetDef",
"Variable defined", "Variable defined",
@@ -697,6 +791,48 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("string", _("Variable")) .AddParameter("string", _("Variable"))
.SetHidden(); // Deprecated. .SetHidden(); // Deprecated.
obj.AddAction(
"PushStringToObjectVariable",
_("Add text variable"),
_("Adds a text (string) to the end of an object array variable."),
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("string", _("Text to add"))
.MarkAsAdvanced()
.SetRelevantForLayoutEventsOnly();
obj.AddAction("PushNumberToObjectVariable",
_("Add variable array value"),
_("Adds a number to the end of an object array variable."),
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("expression", _("Number to add"))
.MarkAsAdvanced()
.SetRelevantForLayoutEventsOnly();
obj.AddAction(
"PushBooleanToObjectVariable",
_("Add boolean variable"),
_("Adds a boolean to the end of an object array variable."),
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add"))
.MarkAsAdvanced()
.SetRelevantForLayoutEventsOnly();
// Deprecated
obj.AddAction( obj.AddAction(
"ObjectVariablePush", "ObjectVariablePush",
_("Add existing variable"), _("Add existing variable"),
@@ -708,6 +844,23 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable")) .AddParameter("objectvar", _("Array variable"))
.AddParameter("scenevar", _("Scene variable with the content to add")) .AddParameter("scenevar", _("Scene variable with the content to add"))
.SetParameterLongDescription(_("The content of the object variable will "
"*be copied* and added at the "
"end of the array."))
.MarkAsAdvanced()
.SetHidden();
obj.AddAction(
"ObjectVariablePush2",
_("Add existing variable"),
_("Adds an existing variable to the end of an object array variable."),
_("Add variable _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
_("Variables/Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable"))
.AddParameter("variable", _("Scene variable with the content to add"))
.SetParameterLongDescription(_("The content of the object variable will " .SetParameterLongDescription(_("The content of the object variable will "
"*be copied* and added at the " "*be copied* and added at the "
"end of the array.")) "end of the array."))
@@ -724,7 +877,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable")) .AddParameter("objectvar", _("Array variable"))
.AddParameter("string", _("Text to add")) .AddParameter("string", _("Text to add"))
.MarkAsAdvanced(); .MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
obj.AddAction("ObjectVariablePushNumber", obj.AddAction("ObjectVariablePushNumber",
_("Add number variable"), _("Add number variable"),
@@ -736,7 +890,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable")) .AddParameter("objectvar", _("Array variable"))
.AddParameter("expression", _("Number to add")) .AddParameter("expression", _("Number to add"))
.MarkAsAdvanced(); .MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
obj.AddAction( obj.AddAction(
"ObjectVariablePushBool", "ObjectVariablePushBool",
@@ -749,7 +904,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Array variable")) .AddParameter("objectvar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add")) .AddParameter("trueorfalse", _("Boolean to add"))
.MarkAsAdvanced(); .MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
obj.AddAction( obj.AddAction(
"ObjectVariableRemoveAt", "ObjectVariableRemoveAt",
@@ -1203,7 +1359,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Variables"), _("Variables"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("object", _("Object")) .AddParameter("object", _("Object"))
.AddParameter("objectvar", _("Variable")); .AddParameter("objectvar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
obj.AddExpression( obj.AddExpression(
"VariableChildCount", "VariableChildCount",

View File

@@ -68,19 +68,22 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
extension extension
.AddStrExpression("ToJSON", .AddStrExpression("ToJSON",
_("Convert scene variable to JSON"), _("Convert variable to JSON"),
_("Convert a scene variable to JSON"), _("Convert a variable to JSON"),
_("JSON"), _("JSON"),
"res/conditions/toujours24_black.png") "res/conditions/toujours24_black.png")
.AddParameter("scenevar", _("Scene variable to be stringified")); .AddParameter("variable", _("The variable to be stringified"),
"AllowUndeclaredVariable");
// Deprecated
extension extension
.AddStrExpression("GlobalVarToJSON", .AddStrExpression("GlobalVarToJSON",
_("Convert global variable to JSON"), _("Convert global variable to JSON"),
_("Convert a global variable to JSON"), _("Convert a global variable to JSON"),
_("JSON"), _("JSON"),
"res/conditions/toujours24_black.png") "res/conditions/toujours24_black.png")
.AddParameter("globalvar", _("The global variable to be stringified")); .AddParameter("globalvar", _("The global variable to be stringified"))
.SetHidden();
extension extension
.AddStrExpression("ObjectVarToJSON", .AddStrExpression("ObjectVarToJSON",
@@ -91,6 +94,7 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
.AddParameter("objectPtr", _("The object with the variable")) .AddParameter("objectPtr", _("The object with the variable"))
.AddParameter("objectvar", _("The object variable to be stringified")); .AddParameter("objectvar", _("The object variable to be stringified"));
// Deprecated
extension extension
.AddAction( .AddAction(
"JSONToVariableStructure", "JSONToVariableStructure",
@@ -102,8 +106,10 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
"res/actions/net.png") "res/actions/net.png")
.AddParameter("string", _("JSON string")) .AddParameter("string", _("JSON string"))
.AddParameter("scenevar", _("Variable where store the JSON object")) .AddParameter("scenevar", _("Variable where store the JSON object"))
.MarkAsAdvanced(); .MarkAsAdvanced()
.SetHidden();
// Deprecated
extension extension
.AddAction("JSONToGlobalVariableStructure", .AddAction("JSONToGlobalVariableStructure",
_("Convert JSON to global variable"), _("Convert JSON to global variable"),
@@ -116,6 +122,20 @@ BuiltinExtensionsImplementer::ImplementsCommonConversionsExtension(
.AddParameter("string", _("JSON string")) .AddParameter("string", _("JSON string"))
.AddParameter("globalvar", .AddParameter("globalvar",
_("Global variable where store the JSON object")) _("Global variable where store the JSON object"))
.MarkAsAdvanced()
.SetHidden();
extension
.AddAction(
"JSONToVariableStructure2",
_("Convert JSON to a variable"),
_("Parse a JSON object and store it into a variable"),
_("Convert JSON string _PARAM0_ and store it into variable _PARAM1_"),
"",
"res/actions/net24.png",
"res/actions/net.png")
.AddParameter("string", _("JSON string"))
.AddParameter("variable", _("Variable where to store the JSON object"))
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension

View File

@@ -98,6 +98,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
.AddParameter("string", _("Group")) .AddParameter("string", _("Group"))
.AddParameter("string", _("Text")); .AddParameter("string", _("Text"));
// Deprecated
extension extension
.AddAction( .AddAction(
"LireFichierExp", "LireFichierExp",
@@ -114,8 +115,27 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
.AddParameter("string", _("Storage name")) .AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group")) .AddParameter("string", _("Group"))
.AddCodeOnlyParameter("currentScene", "") .AddCodeOnlyParameter("currentScene", "")
.AddParameter("scenevar", _("Scene variables")); .AddParameter("scenevar", _("Scene variable"))
.SetHidden();
extension
.AddAction(
"ReadNumberFromStorage",
_("Load a value"),
_("Load the value saved in the specified element and store it in a "
"variable.\nSpecify the structure leading to the element using / "
"(example : Root/Level/Current)\nSpaces are forbidden in element "
"names."),
_("Load _PARAM1_ from storage _PARAM0_ and store value in _PARAM3_"),
"",
"res/actions/fichier24.png",
"res/actions/fichier.png")
.AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group"))
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("variable", _("Variable"));
// Deprecated
extension extension
.AddAction( .AddAction(
"LireFichierTxt", "LireFichierTxt",
@@ -133,7 +153,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
.AddParameter("string", _("Storage name")) .AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group")) .AddParameter("string", _("Group"))
.AddCodeOnlyParameter("currentScene", "") .AddCodeOnlyParameter("currentScene", "")
.AddParameter("scenevar", _("Scene variables")); .AddParameter("scenevar", _("Scene variable"))
.SetHidden();
extension
.AddAction(
"ReadStringFromStorage",
_("Load a text"),
_("Load the text saved in the specified element and store it in a "
"variable.\nSpecify the structure leading to the element using / "
"(example : Root/Level/Current)\nSpaces are forbidden in element "
"names."),
_("Load _PARAM1_ from storage _PARAM0_ and store as text in "
"_PARAM3_"),
"",
"res/actions/fichier24.png",
"res/actions/fichier.png")
.AddParameter("string", _("Storage name"))
.AddParameter("string", _("Group"))
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("variable", _("Variable"));
extension extension
.AddAction("DeleteGroupFichier", .AddAction("DeleteGroupFichier",

View File

@@ -25,29 +25,275 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
extension.AddInstructionOrExpressionGroupMetadata(_("Variables")) extension.AddInstructionOrExpressionGroupMetadata(_("Variables"))
.SetIcon("res/conditions/var24.png"); .SetIcon("res/conditions/var24.png");
extension
.AddCondition("NumberVariable",
_("Variable value"),
_("Compare the number value of a variable."),
_("The variable _PARAM0_"),
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions());
extension
.AddCondition("StringVariable",
_("Variable value"),
_("Compare the text (string) of a variable."),
_("The variable _PARAM0_"),
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions());
extension
.AddCondition(
"BooleanVariable",
_("Variable value"),
_("Compare the boolean value of a variable."),
_("The variable _PARAM0_ is _PARAM1_"),
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("trueorfalse", "");
extension
.AddAction("SetNumberVariable",
_("Change variable value"),
_("Modify the number value of a variable."),
_("the variable _PARAM0_"),
"",
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Variable"))
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions());
extension
.AddAction("SetStringVariable",
_("Change text variable"),
_("Modify the text (string) of a variable."),
_("the variable _PARAM0_"),
"",
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Variable"))
.UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions());
extension
.AddAction(
"SetBooleanVariable",
_("Change boolean variable"),
_("Modify the boolean value of a variable."),
_("Change the variable _PARAM0_: _PARAM1_"),
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("operator", _("Value"), "boolean")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("trueorfalse", "");
extension
.AddCondition(
"VariableChildCount",
_("Number of children"),
_("Compare the number of children in an array variable."),
_("The number of children in the array variable _PARAM0_"),
_("Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Array variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions())
.MarkAsAdvanced();
extension
.AddCondition("VariableChildExists2",
_("Child existence"),
_("Check if the specified child of the structure "
"variable exists."),
_("Child _PARAM1_ of variable _PARAM0_ exists"),
_("Arrays and structures"),
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("string", _("Name of the child"))
.MarkAsAdvanced();
extension
.AddAction(
"RemoveVariableChild",
_("Remove a child"),
_("Remove a child from a structure variable."),
_("Remove child _PARAM1_ from structure variable _PARAM0_"),
_("Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Structure variable"))
.AddParameter("string", _("Child's name"))
.MarkAsAdvanced();
extension
.AddAction("ClearVariableChildren",
_("Clear children"),
_("Remove all the children from the structure or array "
"variable."),
_("Clear children from variable _PARAM0_"),
_("Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Structure or array variable"))
.MarkAsAdvanced();
extension
.AddAction("PushVariable",
_("Add existing variable"),
_("Adds an existing variable at the end of an array "
"variable."),
_("Add variable _PARAM1_ to array variable _PARAM0_"),
_("Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.AddParameter("variable", _("Variable with the content to add"))
.SetParameterLongDescription(
_("The content of the variable will *be copied* and added at the "
"end of the array."))
.MarkAsAdvanced();
extension
.AddAction(
"PushString",
_("Add text variable"),
_("Adds a text (string) at the end of a array variable."),
_("Add the value _PARAM1_ to array variable _PARAM0_"),
_("Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.AddParameter("string", _("Text to add"))
.MarkAsAdvanced();
extension
.AddAction("PushNumber",
_("Add variable array value"),
_("Adds a number at the end of an array variable."),
_("Add the value _PARAM1_ to array variable _PARAM0_"),
_("Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.AddParameter("expression", _("Number to add"))
.MarkAsAdvanced();
extension
.AddAction("PushBoolean",
_("Add boolean variable"),
_("Adds a boolean at the end of an array variable."),
_("Add the value _PARAM1_ to array variable _PARAM0_"),
_("Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add"))
.MarkAsAdvanced();
extension
.AddAction("RemoveVariableAt",
_("Remove variable by index"),
_("Removes a variable at the specified index of an array "
"variable."),
_("Remove variable at index _PARAM1_ from array "
"variable _PARAM0_"),
_("Arrays and structures"),
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.AddParameter("expression", _("Index to remove"))
.MarkAsAdvanced();
extension
.AddStrExpression(
"VariableFirstString",
_("First text child"),
_("Get the value of the first element of an array variable, if "
"it is a text (string)."),
_("Arrays and structures"),
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.SetRelevantForLayoutEventsOnly();
extension
.AddExpression(
"VariableFirstNumber",
_("First number child"),
_("Get the value of the first element of an array variable, if "
"it is a number."),
_("Arrays and structures"),
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.SetRelevantForLayoutEventsOnly();
extension
.AddStrExpression(
"VariableLastString",
_("Last text child"),
_("Get the value of the last element of an array variable, if "
"it is a text (string)."),
_("Arrays and structures"),
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.SetRelevantForLayoutEventsOnly();
extension
.AddExpression(
"VariableLastNumber",
_("Last number child"),
_("Get the value of the last element of an array variable, if "
"it is a number."),
_("Arrays and structures"),
"res/actions/var.png")
.AddParameter("variable", _("Array variable"))
.SetRelevantForLayoutEventsOnly();
// Legacy instructions
extension extension
.AddCondition("VarScene", .AddCondition("VarScene",
_("Number variable"), _("Number variable"),
_("Compare the number value of a scene variable."), _("Compare the number value of a scene variable."),
_("The number of scene variable _PARAM0_"), _("The number of scene variable _PARAM0_"),
_("Scene variables"), _("External variables/Scene variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("scenevar", _("Variable")) .AddParameter("scenevar", _("Variable"))
.UseStandardRelationalOperatorParameters( .UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions()); "number", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
extension extension
.AddCondition("VarSceneTxt", .AddCondition("VarSceneTxt",
_("Text variable"), _("Text variable"),
_("Compare the text (string) of a scene variable."), _("Compare the text (string) of a scene variable."),
_("The text of scene variable _PARAM0_"), _("The text of scene variable _PARAM0_"),
_("Scene variables"), _("External variables/Scene variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("scenevar", _("Variable")) .AddParameter("scenevar", _("Variable"))
.UseStandardRelationalOperatorParameters( .UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions()); "string", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
extension extension
.AddCondition( .AddCondition(
@@ -55,12 +301,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Boolean variable"), _("Boolean variable"),
_("Compare the boolean value of a scene variable."), _("Compare the boolean value of a scene variable."),
_("The boolean value of scene variable _PARAM0_ is _PARAM1_"), _("The boolean value of scene variable _PARAM0_ is _PARAM1_"),
_("Scene variables"), _("External variables/Scene variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("scenevar", _("Variable")) .AddParameter("scenevar", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is")) .AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true"); .SetDefaultValue("true")
.SetRelevantForFunctionEventsOnly();
extension extension
.AddCondition("VariableChildExists", .AddCondition("VariableChildExists",
@@ -68,11 +315,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Check if the specified child of the scene structure " _("Check if the specified child of the scene structure "
"variable exists."), "variable exists."),
_("Child _PARAM1_ of scene variable _PARAM0_ exists"), _("Child _PARAM1_ of scene variable _PARAM0_ exists"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("scenevar", _("Variable")) .AddParameter("scenevar", _("Variable"))
.AddParameter("string", _("Name of the child")) .AddParameter("string", _("Name of the child"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -81,11 +329,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Check if the specified child of the global structure " _("Check if the specified child of the global structure "
"variable exists."), "variable exists."),
_("Child _PARAM1_ of global variable _PARAM0_ exists"), _("Child _PARAM1_ of global variable _PARAM0_ exists"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("globalvar", _("Variable")) .AddParameter("globalvar", _("Variable"))
.AddParameter("string", _("Name of the child")) .AddParameter("string", _("Name of the child"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -93,7 +342,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"Variable defined", "Variable defined",
"Test if the scene variable exists.", "Test if the scene variable exists.",
"Scene variable _PARAM0_ is defined", "Scene variable _PARAM0_ is defined",
_("Scene variables"), _("External variables/Scene variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddCodeOnlyParameter("currentScene", "") .AddCodeOnlyParameter("currentScene", "")
@@ -105,12 +354,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Number variable"), _("Number variable"),
_("Compare the number value of a global variable."), _("Compare the number value of a global variable."),
_("the global variable _PARAM0_"), _("the global variable _PARAM0_"),
_("Global variables"), _("External variables/Global variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("globalvar", _("Variable")) .AddParameter("globalvar", _("Variable"))
.UseStandardRelationalOperatorParameters( .UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions()) "number", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -118,12 +368,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Text variable"), _("Text variable"),
_("Compare the text (string) of a global variable."), _("Compare the text (string) of a global variable."),
_("the text of the global variable _PARAM0_"), _("the text of the global variable _PARAM0_"),
_("Global variables"), _("External variables/Global variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("globalvar", _("Variable")) .AddParameter("globalvar", _("Variable"))
.UseStandardRelationalOperatorParameters( .UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions()) "string", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -132,19 +383,20 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Boolean variable"), _("Boolean variable"),
_("Compare the boolean value of a global variable."), _("Compare the boolean value of a global variable."),
_("The boolean value of global variable _PARAM0_ is _PARAM1_"), _("The boolean value of global variable _PARAM0_ is _PARAM1_"),
_("Global variables"), _("External variables/Global variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("globalvar", _("Variable")) .AddParameter("globalvar", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is")) .AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true"); .SetDefaultValue("true")
.SetRelevantForFunctionEventsOnly();
extension extension
.AddCondition("VarGlobalDef", .AddCondition("VarGlobalDef",
"Variable defined", "Variable defined",
"Test if a global variable exists.", "Test if a global variable exists.",
"Global variable _PARAM0_ is defined", "Global variable _PARAM0_ is defined",
_("Global variables"), _("External variables/Global variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddCodeOnlyParameter("currentScene", "") .AddCodeOnlyParameter("currentScene", "")
@@ -157,24 +409,26 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Change number variable"), _("Change number variable"),
_("Modify the number value of a scene variable."), _("Modify the number value of a scene variable."),
_("the scene variable _PARAM0_"), _("the scene variable _PARAM0_"),
_("Scene variables"), _("External variables/Scene variables"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Variable")) .AddParameter("scenevar", _("Variable"))
.UseStandardOperatorParameters("number", .UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions()); ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
extension extension
.AddAction("ModVarSceneTxt", .AddAction("ModVarSceneTxt",
_("Change text variable"), _("Change text variable"),
_("Modify the text (string) of a scene variable."), _("Modify the text (string) of a scene variable."),
_("the text of scene variable _PARAM0_"), _("the text of scene variable _PARAM0_"),
_("Scene variables"), _("External variables/Scene variables"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Variable")) .AddParameter("scenevar", _("Variable"))
.UseStandardOperatorParameters("string", .UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions()); ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
extension extension
.AddAction( .AddAction(
@@ -182,11 +436,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Change boolean variable"), _("Change boolean variable"),
_("Modify the boolean value of a scene variable."), _("Modify the boolean value of a scene variable."),
_("Set the boolean value of scene variable _PARAM0_ to _PARAM1_"), _("Set the boolean value of scene variable _PARAM0_ to _PARAM1_"),
_("Scene variables"), _("External variables/Scene variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("scenevar", _("Variable")) .AddParameter("scenevar", _("Variable"))
.AddParameter("trueorfalse", _("New Value:")); .AddParameter("trueorfalse", _("New Value:"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddAction("ToggleSceneVariableAsBoolean", .AddAction("ToggleSceneVariableAsBoolean",
@@ -195,22 +450,24 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("If it was true, it will become false, and if it was " _("If it was true, it will become false, and if it was "
"false it will become true."), "false it will become true."),
_("Toggle the boolean value of scene variable _PARAM0_"), _("Toggle the boolean value of scene variable _PARAM0_"),
_("Scene variables"), _("External variables/Scene variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("scenevar", _("Variable")); .AddParameter("scenevar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddAction("ModVarGlobal", .AddAction("ModVarGlobal",
_("Change number variable"), _("Change number variable"),
_("Modify the number value of a global variable."), _("Modify the number value of a global variable."),
_("the global variable _PARAM0_"), _("the global variable _PARAM0_"),
_("Global variables"), _("External variables/Global variables"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Variable")) .AddParameter("globalvar", _("Variable"))
.UseStandardOperatorParameters("number", .UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions()) ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -218,12 +475,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Change text variable"), _("Change text variable"),
_("Modify the text (string) of a global variable."), _("Modify the text (string) of a global variable."),
_("the text of global variable _PARAM0_"), _("the text of global variable _PARAM0_"),
_("Global variables"), _("External variables/Global variables"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Variable")) .AddParameter("globalvar", _("Variable"))
.UseStandardOperatorParameters("string", .UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions()) ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -232,11 +490,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Change boolean variable"), _("Change boolean variable"),
_("Modify the boolean value of a global variable."), _("Modify the boolean value of a global variable."),
_("Set the boolean value of global variable _PARAM0_ to _PARAM1_"), _("Set the boolean value of global variable _PARAM0_ to _PARAM1_"),
_("Global variables"), _("External variables/Global variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("globalvar", _("Variable")) .AddParameter("globalvar", _("Variable"))
.AddParameter("trueorfalse", _("New Value:")); .AddParameter("trueorfalse", _("New Value:"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddAction("ToggleGlobalVariableAsBoolean", .AddAction("ToggleGlobalVariableAsBoolean",
@@ -245,10 +504,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("If it was true, it will become false, and if it was " _("If it was true, it will become false, and if it was "
"false it will become true."), "false it will become true."),
_("Toggle the boolean value of global variable _PARAM0_"), _("Toggle the boolean value of global variable _PARAM0_"),
_("Global variables"), _("External variables/Global variables"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("globalvar", _("Variable")); .AddParameter("globalvar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddAction( .AddAction(
@@ -256,12 +516,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Remove a child"), _("Remove a child"),
_("Remove a child from a scene structure variable."), _("Remove a child from a scene structure variable."),
_("Remove child _PARAM1_ from scene structure variable _PARAM0_"), _("Remove child _PARAM1_ from scene structure variable _PARAM0_"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Structure variable")) .AddParameter("scenevar", _("Structure variable"))
.AddParameter("string", _("Child's name")) .AddParameter("string", _("Child's name"))
.MarkAsAdvanced(); .MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
extension extension
.AddAction( .AddAction(
@@ -269,12 +530,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Remove a child"), _("Remove a child"),
_("Remove a child from a global structure variable."), _("Remove a child from a global structure variable."),
_("Remove child _PARAM1_ from global structure variable _PARAM0_"), _("Remove child _PARAM1_ from global structure variable _PARAM0_"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Structure variable")) .AddParameter("globalvar", _("Structure variable"))
.AddParameter("string", _("Child's name")) .AddParameter("string", _("Child's name"))
.MarkAsAdvanced(); .MarkAsAdvanced()
.SetRelevantForFunctionEventsOnly();
extension extension
.AddAction("VariableClearChildren", .AddAction("VariableClearChildren",
@@ -282,10 +544,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Remove all the children from the scene structure or array " _("Remove all the children from the scene structure or array "
"variable."), "variable."),
_("Clear children from scene variable _PARAM0_"), _("Clear children from scene variable _PARAM0_"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Structure or array variable")) .AddParameter("scenevar", _("Structure or array variable"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -294,10 +557,11 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Remove all the children from the global structure or array " _("Remove all the children from the global structure or array "
"variable."), "variable."),
_("Clear children from global variable _PARAM0_"), _("Clear children from global variable _PARAM0_"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Structure or array variable")) .AddParameter("globalvar", _("Structure or array variable"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -306,7 +570,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Adds an existing variable at the end of a scene array " _("Adds an existing variable at the end of a scene array "
"variable."), "variable."),
_("Add variable _PARAM1_ to array variable _PARAM0_"), _("Add variable _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array variable")) .AddParameter("scenevar", _("Array variable"))
@@ -314,6 +578,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.SetParameterLongDescription( .SetParameterLongDescription(
_("The content of the variable will *be copied* and added at the " _("The content of the variable will *be copied* and added at the "
"end of the array.")) "end of the array."))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -322,11 +587,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add text variable"), _("Add text variable"),
_("Adds a text (string) at the end of a scene array variable."), _("Adds a text (string) at the end of a scene array variable."),
_("Add text _PARAM1_ to array variable _PARAM0_"), _("Add text _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array variable")) .AddParameter("scenevar", _("Array variable"))
.AddParameter("string", _("Text to add")) .AddParameter("string", _("Text to add"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -334,11 +600,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add number variable"), _("Add number variable"),
_("Adds a number at the end of a scene array variable."), _("Adds a number at the end of a scene array variable."),
_("Add number _PARAM1_ to array variable _PARAM0_"), _("Add number _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array variable")) .AddParameter("scenevar", _("Array variable"))
.AddParameter("expression", _("Number to add")) .AddParameter("expression", _("Number to add"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -346,11 +613,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add boolean variable"), _("Add boolean variable"),
_("Adds a boolean at the end of a scene array variable."), _("Adds a boolean at the end of a scene array variable."),
_("Add boolean _PARAM1_ to array variable _PARAM0_"), _("Add boolean _PARAM1_ to array variable _PARAM0_"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array variable")) .AddParameter("scenevar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add")) .AddParameter("trueorfalse", _("Boolean to add"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -360,11 +628,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"variable."), "variable."),
_("Remove variable at index _PARAM1_ from scene array " _("Remove variable at index _PARAM1_ from scene array "
"variable _PARAM0_"), "variable _PARAM0_"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array variable")) .AddParameter("scenevar", _("Array variable"))
.AddParameter("expression", _("Index to remove")) .AddParameter("expression", _("Index to remove"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -373,12 +642,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Number of children"), _("Number of children"),
_("Compare the number of children in a scene array variable."), _("Compare the number of children in a scene array variable."),
_("The number of children in the array variable _PARAM0_"), _("The number of children in the array variable _PARAM0_"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("scenevar", _("Array variable")) .AddParameter("scenevar", _("Array variable"))
.UseStandardRelationalOperatorParameters( .UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions()) "number", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -387,9 +657,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("First text child"), _("First text child"),
_("Get the value of the first element of a scene array variable, if " _("Get the value of the first element of a scene array variable, if "
"it is a text (string)."), "it is a text (string)."),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array variable")); .AddParameter("scenevar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddExpression( .AddExpression(
@@ -397,9 +668,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("First number child"), _("First number child"),
_("Get the value of the first element of a scene array variable, if " _("Get the value of the first element of a scene array variable, if "
"it is a number."), "it is a number."),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array variable")); .AddParameter("scenevar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddStrExpression( .AddStrExpression(
@@ -407,9 +679,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Last text child"), _("Last text child"),
_("Get the value of the last element of a scene array variable, if " _("Get the value of the last element of a scene array variable, if "
"it is a text (string)."), "it is a text (string)."),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array variable")); .AddParameter("scenevar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddExpression( .AddExpression(
@@ -417,9 +690,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Last number child"), _("Last number child"),
_("Get the value of the last element of a scene array variable, if " _("Get the value of the last element of a scene array variable, if "
"it is a number."), "it is a number."),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array variable")); .AddParameter("scenevar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddAction( .AddAction(
@@ -427,7 +701,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add existing variable"), _("Add existing variable"),
_("Adds an existing variable at the end of a global array variable."), _("Adds an existing variable at the end of a global array variable."),
_("Add variable _PARAM1_ to array variable _PARAM0_"), _("Add variable _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array variable")) .AddParameter("globalvar", _("Array variable"))
@@ -435,6 +709,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
.SetParameterLongDescription( .SetParameterLongDescription(
_("The content of the variable will *be copied* and added at the " _("The content of the variable will *be copied* and added at the "
"end of the array.")) "end of the array."))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -444,11 +719,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"array variable."), "array variable."),
_("Remove variable at index _PARAM1_ from global array " _("Remove variable at index _PARAM1_ from global array "
"variable _PARAM0_"), "variable _PARAM0_"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array variable")) .AddParameter("globalvar", _("Array variable"))
.AddParameter("expression", _("Index to remove")) .AddParameter("expression", _("Index to remove"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -457,11 +733,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add text variable"), _("Add text variable"),
_("Adds a text (string) at the end of a global array variable."), _("Adds a text (string) at the end of a global array variable."),
_("Add text _PARAM1_ to array variable _PARAM0_"), _("Add text _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array variable")) .AddParameter("globalvar", _("Array variable"))
.AddParameter("string", _("Text to add")) .AddParameter("string", _("Text to add"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -469,11 +746,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add number variable"), _("Add number variable"),
_("Adds a number at the end of a global array variable."), _("Adds a number at the end of a global array variable."),
_("Add number _PARAM1_ to array variable _PARAM0_"), _("Add number _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array variable")) .AddParameter("globalvar", _("Array variable"))
.AddParameter("expression", _("Number to add")) .AddParameter("expression", _("Number to add"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -481,11 +759,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Add boolean variable"), _("Add boolean variable"),
_("Adds a boolean at the end of a global array variable."), _("Adds a boolean at the end of a global array variable."),
_("Add boolean _PARAM1_ to array variable _PARAM0_"), _("Add boolean _PARAM1_ to array variable _PARAM0_"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var24.png", "res/actions/var24.png",
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array variable")) .AddParameter("globalvar", _("Array variable"))
.AddParameter("trueorfalse", _("Boolean to add")) .AddParameter("trueorfalse", _("Boolean to add"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -494,12 +773,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Number of children"), _("Number of children"),
_("Compare the number of children in a global array variable."), _("Compare the number of children in a global array variable."),
_("The number of children of the array variable _PARAM0_"), _("The number of children of the array variable _PARAM0_"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/conditions/var24.png", "res/conditions/var24.png",
"res/conditions/var.png") "res/conditions/var.png")
.AddParameter("globalvar", _("Array variable")) .AddParameter("globalvar", _("Array variable"))
.UseStandardRelationalOperatorParameters( .UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions()) "number", ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced(); .MarkAsAdvanced();
extension extension
@@ -507,18 +787,20 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("First text child"), _("First text child"),
_("Value of the first element of a global array " _("Value of the first element of a global array "
"variable, if it is a text (string) variable."), "variable, if it is a text (string) variable."),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array variable")); .AddParameter("globalvar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddExpression("GlobalVariableFirstNumber", .AddExpression("GlobalVariableFirstNumber",
_("First number child"), _("First number child"),
_("Value of the first element of a global array " _("Value of the first element of a global array "
"variable, if it is a number variable"), "variable, if it is a number variable"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array variable")); .AddParameter("globalvar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddStrExpression( .AddStrExpression(
@@ -526,9 +808,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Last text child"), _("Last text child"),
_("Value of the last element of a global array variable, if " _("Value of the last element of a global array variable, if "
"it is a text (string) variable."), "it is a text (string) variable."),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array variable")); .AddParameter("globalvar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddExpression( .AddExpression(
@@ -536,59 +819,65 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
_("Last number child"), _("Last number child"),
_("Value of the last element of a global array variable, if " _("Value of the last element of a global array variable, if "
"it is a number variable"), "it is a number variable"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array variable")); .AddParameter("globalvar", _("Array variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddExpression("GlobalVariableChildCount", .AddExpression("GlobalVariableChildCount",
_("Number of children"), _("Number of children"),
_("Number of children in a global array or " _("Number of children in a global array or "
"structure variable"), "structure variable"),
_("Global variables/Arrays and structures"), _("External variables/Global variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Array or structure variable")); .AddParameter("globalvar", _("Array or structure variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddExpression("VariableChildCount", .AddExpression("VariableChildCount",
_("Number of children"), _("Number of children"),
_("Number of children in a scene array or " _("Number of children in a scene array or "
"structure variable"), "structure variable"),
_("Scene variables/Arrays and structures"), _("External variables/Scene variables/Arrays and structures"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Array or structure variable")); .AddParameter("variable", _("Array or structure variable"), "AllowUndeclaredVariable");
extension extension
.AddExpression("Variable", .AddExpression("Variable",
_("Number variable"), _("Number variable"),
_("Number value of a scene variable"), _("Number value of a scene variable"),
_("Scene variables"), _("External variables/Scene variables"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Variable")); .AddParameter("scenevar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddStrExpression("VariableString", .AddStrExpression("VariableString",
_("Text variable"), _("Text variable"),
_("Text of a scene variable"), _("Text of a scene variable"),
_("Scene variables"), _("External variables/Scene variables"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("scenevar", _("Variable")); .AddParameter("scenevar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddExpression("GlobalVariable", .AddExpression("GlobalVariable",
_("Number variable"), _("Number variable"),
_("Number value of a global variable"), _("Number value of a global variable"),
_("Global variables"), _("External variables/Global variables"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Name of the global variable")); .AddParameter("globalvar", _("Name of the global variable"))
.SetRelevantForFunctionEventsOnly();
extension extension
.AddStrExpression("GlobalVariableString", .AddStrExpression("GlobalVariableString",
_("Text variable"), _("Text variable"),
_("Text of a global variable"), _("Text of a global variable"),
_("Global variables"), _("External variables/Global variables"),
"res/actions/var.png") "res/actions/var.png")
.AddParameter("globalvar", _("Variable")); .AddParameter("globalvar", _("Variable"))
.SetRelevantForFunctionEventsOnly();
} }
} // namespace gd } // namespace gd

View File

@@ -456,6 +456,15 @@ class GD_CORE_API InstructionMetadata : public gd::AbstractFunctionMetadata {
return *this; return *this;
} }
/**
* \brief Return the type manipulated in a standard way by the instruction.
*
* \param type "number" or "string"
*/
const gd::String &GetManipulatedType() const {
return codeExtraInformation.type;
}
/** /**
* If InstructionMetadata::ExtraInformation::SetManipulatedType was called * If InstructionMetadata::ExtraInformation::SetManipulatedType was called
* with "number" or "string", this function will tell the code generator the * with "number" or "string", this function will tell the code generator the

View File

@@ -26,7 +26,7 @@ void EventBasedBehaviorBrowser::ExposeEvents(
void EventBasedBehaviorBrowser::ExposeEvents( void EventBasedBehaviorBrowser::ExposeEvents(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) const { gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) const {
gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents( gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
project, eventsBasedBehavior, worker); project, eventsFunctionsExtension, eventsBasedBehavior, worker);
} }
void EventBasedBehaviorBrowser::ExposeObjects( void EventBasedBehaviorBrowser::ExposeObjects(

View File

@@ -29,8 +29,11 @@ namespace gd {
*/ */
class GD_CORE_API EventBasedBehaviorBrowser : public ProjectBrowser { class GD_CORE_API EventBasedBehaviorBrowser : public ProjectBrowser {
public: public:
EventBasedBehaviorBrowser(gd::EventsBasedBehavior &eventsBasedBehavior_) EventBasedBehaviorBrowser(
: eventsBasedBehavior(eventsBasedBehavior_) {} const gd::EventsFunctionsExtension &eventsFunctionsExtension_,
gd::EventsBasedBehavior &eventsBasedBehavior_)
: eventsFunctionsExtension(eventsFunctionsExtension_),
eventsBasedBehavior(eventsBasedBehavior_) {}
/** /**
* \brief Call the specified worker on all events of the event-based * \brief Call the specified worker on all events of the event-based
@@ -48,7 +51,7 @@ public:
* This should be the preferred way to traverse all the events of an event-based behavior. * This should be the preferred way to traverse all the events of an event-based behavior.
*/ */
void void
ExposeEvents(gd::Project &project, ExposeEvents(gd::Project &project,
gd::ArbitraryEventsWorkerWithContext &worker) const override; gd::ArbitraryEventsWorkerWithContext &worker) const override;
/** /**
@@ -80,6 +83,7 @@ public:
gd::ArbitraryBehaviorSharedDataWorker &worker) const override; gd::ArbitraryBehaviorSharedDataWorker &worker) const override;
private: private:
const gd::EventsFunctionsExtension &eventsFunctionsExtension;
gd::EventsBasedBehavior &eventsBasedBehavior; gd::EventsBasedBehavior &eventsBasedBehavior;
}; };

View File

@@ -14,29 +14,27 @@
#include "GDCore/Events/Expression.h" #include "GDCore/Events/Expression.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h" #include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/String.h" #include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
using namespace std; using namespace std;
namespace gd { namespace gd {
ArbitraryEventsWorker::~ArbitraryEventsWorker() {} AbstractArbitraryEventsWorker::~AbstractArbitraryEventsWorker() {}
void ArbitraryEventsWorker::VisitEventList(gd::EventsList& events) { void AbstractArbitraryEventsWorker::VisitEventList(gd::EventsList& events) {
DoVisitEventList(events); DoVisitEventList(events);
for (std::size_t i = 0; i < events.size();) { for (std::size_t i = 0; i < events.size();) {
if (events[i].AcceptVisitor(*this)) if (events[i].AcceptVisitor(*this))
events.RemoveEvent(i); events.RemoveEvent(i);
else { else {
if (events[i].CanHaveSubEvents())
VisitEventList(events[i].GetSubEvents());
++i; ++i;
} }
} }
} }
bool ArbitraryEventsWorker::VisitEvent(gd::BaseEvent& event) { bool AbstractArbitraryEventsWorker::VisitEvent(gd::BaseEvent& event) {
bool shouldDelete = DoVisitEvent(event); bool shouldDelete = DoVisitEvent(event);
if (shouldDelete) return true; if (shouldDelete) return true;
@@ -55,15 +53,17 @@ bool ArbitraryEventsWorker::VisitEvent(gd::BaseEvent& event) {
*expressionAndMetadata.first, expressionAndMetadata.second); *expressionAndMetadata.first, expressionAndMetadata.second);
} }
if (!shouldDelete && event.CanHaveSubEvents()) {
VisitEventList(event.GetSubEvents());
}
return shouldDelete; return shouldDelete;
} }
bool ArbitraryEventsWorker::VisitLinkEvent(gd::LinkEvent& linkEvent) { bool AbstractArbitraryEventsWorker::VisitLinkEvent(gd::LinkEvent& linkEvent) {
return DoVisitLinkEvent(linkEvent); return DoVisitLinkEvent(linkEvent);
} }
void ArbitraryEventsWorker::VisitInstructionList( void AbstractArbitraryEventsWorker::VisitInstructionList(
gd::InstructionsList& instructions, bool areConditions) { gd::InstructionsList& instructions, bool areConditions) {
DoVisitInstructionList(instructions, areConditions); DoVisitInstructionList(instructions, areConditions);
@@ -79,22 +79,19 @@ void ArbitraryEventsWorker::VisitInstructionList(
} }
} }
bool ArbitraryEventsWorker::VisitInstruction(gd::Instruction& instruction, bool AbstractArbitraryEventsWorker::VisitInstruction(gd::Instruction& instruction,
bool isCondition) { bool isCondition) {
return DoVisitInstruction(instruction, isCondition); return DoVisitInstruction(instruction, isCondition);
} }
bool ArbitraryEventsWorker::VisitEventExpression(gd::Expression& expression, bool AbstractArbitraryEventsWorker::VisitEventExpression(gd::Expression& expression,
const gd::ParameterMetadata& metadata) { const gd::ParameterMetadata& metadata) {
return DoVisitEventExpression(expression, metadata); return DoVisitEventExpression(expression, metadata);
} }
ArbitraryEventsWorkerWithContext::~ArbitraryEventsWorkerWithContext() {} AbstractReadOnlyArbitraryEventsWorker::~AbstractReadOnlyArbitraryEventsWorker() {}
void AbstractReadOnlyArbitraryEventsWorker::VisitEventList(const gd::EventsList& events) {
ReadOnlyArbitraryEventsWorker::~ReadOnlyArbitraryEventsWorker() {}
void ReadOnlyArbitraryEventsWorker::VisitEventList(const gd::EventsList& events) {
DoVisitEventList(events); DoVisitEventList(events);
for (std::size_t i = 0; i < events.size(); ++i) { for (std::size_t i = 0; i < events.size(); ++i) {
@@ -109,7 +106,7 @@ void ReadOnlyArbitraryEventsWorker::VisitEventList(const gd::EventsList& events)
} }
} }
void ReadOnlyArbitraryEventsWorker::VisitEvent(const gd::BaseEvent& event) { void AbstractReadOnlyArbitraryEventsWorker::VisitEvent(const gd::BaseEvent& event) {
DoVisitEvent(event); DoVisitEvent(event);
const vector<const gd::InstructionsList*> conditionsVectors = const vector<const gd::InstructionsList*> conditionsVectors =
@@ -130,11 +127,11 @@ void ReadOnlyArbitraryEventsWorker::VisitEvent(const gd::BaseEvent& event) {
} }
} }
void ReadOnlyArbitraryEventsWorker::VisitLinkEvent(const gd::LinkEvent& linkEvent) { void AbstractReadOnlyArbitraryEventsWorker::VisitLinkEvent(const gd::LinkEvent& linkEvent) {
DoVisitLinkEvent(linkEvent); DoVisitLinkEvent(linkEvent);
} }
void ReadOnlyArbitraryEventsWorker::VisitInstructionList( void AbstractReadOnlyArbitraryEventsWorker::VisitInstructionList(
const gd::InstructionsList& instructions, bool areConditions) { const gd::InstructionsList& instructions, bool areConditions) {
DoVisitInstructionList(instructions, areConditions); DoVisitInstructionList(instructions, areConditions);
@@ -150,21 +147,73 @@ void ReadOnlyArbitraryEventsWorker::VisitInstructionList(
} }
} }
void ReadOnlyArbitraryEventsWorker::VisitInstruction(const gd::Instruction& instruction, void AbstractReadOnlyArbitraryEventsWorker::VisitInstruction(const gd::Instruction& instruction,
bool isCondition) { bool isCondition) {
DoVisitInstruction(instruction, isCondition); DoVisitInstruction(instruction, isCondition);
} }
void ReadOnlyArbitraryEventsWorker::VisitEventExpression(const gd::Expression& expression, void AbstractReadOnlyArbitraryEventsWorker::VisitEventExpression(const gd::Expression& expression,
const gd::ParameterMetadata& metadata) { const gd::ParameterMetadata& metadata) {
DoVisitEventExpression(expression, metadata); DoVisitEventExpression(expression, metadata);
} }
void ReadOnlyArbitraryEventsWorker::StopAnyEventIteration() { void AbstractReadOnlyArbitraryEventsWorker::StopAnyEventIteration() {
shouldStopIteration = true; shouldStopIteration = true;
} }
ArbitraryEventsWorker::~ArbitraryEventsWorker() {}
bool ArbitraryEventsWorker::VisitEvent(gd::BaseEvent &event) {
return AbstractArbitraryEventsWorker::VisitEvent(event);
}
ArbitraryEventsWorkerWithContext::~ArbitraryEventsWorkerWithContext() {}
bool ArbitraryEventsWorkerWithContext::VisitEvent(gd::BaseEvent &event) {
if (!event.HasVariables()) {
return AbstractArbitraryEventsWorker::VisitEvent(event);
}
// Push local variables
auto newProjectScopedContainers =
ProjectScopedContainers::MakeNewProjectScopedContainersWithLocalVariables(
*currentProjectScopedContainers, event);
auto *parentProjectScopedContainers = currentProjectScopedContainers;
currentProjectScopedContainers = &newProjectScopedContainers;
bool shouldDelete = AbstractArbitraryEventsWorker::VisitEvent(event);
// Pop local variables
currentProjectScopedContainers = parentProjectScopedContainers;
return shouldDelete;
}
ReadOnlyArbitraryEventsWorker::~ReadOnlyArbitraryEventsWorker() {}
void ReadOnlyArbitraryEventsWorker::VisitEvent(
const gd::BaseEvent &event) {
AbstractReadOnlyArbitraryEventsWorker::VisitEvent(event);
}
ReadOnlyArbitraryEventsWorkerWithContext::~ReadOnlyArbitraryEventsWorkerWithContext() {} ReadOnlyArbitraryEventsWorkerWithContext::~ReadOnlyArbitraryEventsWorkerWithContext() {}
void ReadOnlyArbitraryEventsWorkerWithContext::VisitEvent(
const gd::BaseEvent &event) {
if (!event.HasVariables()) {
AbstractReadOnlyArbitraryEventsWorker::VisitEvent(event);
return;
}
// Push local variables
auto newProjectScopedContainers =
ProjectScopedContainers::MakeNewProjectScopedContainersWithLocalVariables(
*currentProjectScopedContainers, event);
auto *parentProjectScopedContainers = currentProjectScopedContainers;
currentProjectScopedContainers = &newProjectScopedContainers;
AbstractReadOnlyArbitraryEventsWorker::VisitEvent(event);
// Pop local variables
currentProjectScopedContainers = parentProjectScopedContainers;
}
} // namespace gd } // namespace gd

View File

@@ -3,8 +3,8 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_ARBITRARYEVENTSWORKER_H #pragma once
#define GDCORE_ARBITRARYEVENTSWORKER_H
#include <map> #include <map>
#include <memory> #include <memory>
#include <vector> #include <vector>
@@ -12,6 +12,7 @@
#include "GDCore/Events/EventVisitor.h" #include "GDCore/Events/EventVisitor.h"
#include "GDCore/Project/ProjectScopedContainers.h" #include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/String.h" #include "GDCore/String.h"
namespace gd { namespace gd {
class Instruction; class Instruction;
class BaseEvent; class BaseEvent;
@@ -25,27 +26,24 @@ class ParameterMetadata;
namespace gd { namespace gd {
/** /**
* \brief ArbitraryEventsWorker is an abstract class used to browse events (and * \brief AbstractArbitraryEventsWorker is a base abstract class used to browse events (and
* instructions) and do some work on them. Can be used to implement refactoring * instructions) and do some work on them. It must not be inherited directly.
* for example.
* *
* \see gd::ArbitraryEventsWorker
* \see gd::ArbitraryEventsWorkerWithContext * \see gd::ArbitraryEventsWorkerWithContext
* *
* \ingroup IDE * \ingroup IDE
*/ */
class GD_CORE_API ArbitraryEventsWorker : private EventVisitor { class GD_CORE_API AbstractArbitraryEventsWorker : private EventVisitor {
public: public:
ArbitraryEventsWorker(){}; AbstractArbitraryEventsWorker(){};
virtual ~ArbitraryEventsWorker(); virtual ~AbstractArbitraryEventsWorker();
/** protected:
* \brief Launch the worker on the specified events list. virtual bool VisitEvent(gd::BaseEvent& event) override;
*/ void VisitEventList(gd::EventsList& events);
void Launch(gd::EventsList& events) { VisitEventList(events); };
private: private:
void VisitEventList(gd::EventsList& events);
bool VisitEvent(gd::BaseEvent& event) override;
bool VisitLinkEvent(gd::LinkEvent& linkEvent) override; bool VisitLinkEvent(gd::LinkEvent& linkEvent) override;
void VisitInstructionList(gd::InstructionsList& instructions, void VisitInstructionList(gd::InstructionsList& instructions,
bool areConditions); bool areConditions);
@@ -101,6 +99,31 @@ class GD_CORE_API ArbitraryEventsWorker : private EventVisitor {
} }
}; };
/**
* \brief ArbitraryEventsWorker is an abstract class used to browse events (and
* instructions) and do some work on them. Can be used to implement refactoring
* for example.
*
* \see gd::ArbitraryEventsWorkerWithContext
*
* \ingroup IDE
*/
class GD_CORE_API ArbitraryEventsWorker : public AbstractArbitraryEventsWorker {
public:
ArbitraryEventsWorker(){};
virtual ~ArbitraryEventsWorker();
/**
* \brief Launch the worker on the specified events list.
*/
void Launch(gd::EventsList &events) {
AbstractArbitraryEventsWorker::VisitEventList(events);
};
private:
bool VisitEvent(gd::BaseEvent &event) override;
};
/** /**
* \brief An events worker that will know about the context (the objects * \brief An events worker that will know about the context (the objects
* container). Useful for workers working on expressions notably. * container). Useful for workers working on expressions notably.
@@ -110,10 +133,10 @@ class GD_CORE_API ArbitraryEventsWorker : private EventVisitor {
* \ingroup IDE * \ingroup IDE
*/ */
class GD_CORE_API ArbitraryEventsWorkerWithContext class GD_CORE_API ArbitraryEventsWorkerWithContext
: public ArbitraryEventsWorker { : public AbstractArbitraryEventsWorker {
public: public:
ArbitraryEventsWorkerWithContext() ArbitraryEventsWorkerWithContext()
: projectScopedContainers(nullptr){}; : currentProjectScopedContainers(nullptr){};
virtual ~ArbitraryEventsWorkerWithContext(); virtual ~ArbitraryEventsWorkerWithContext();
/** /**
@@ -121,53 +144,50 @@ class GD_CORE_API ArbitraryEventsWorkerWithContext
* giving the objects container on which the events are applying to. * giving the objects container on which the events are applying to.
*/ */
void Launch(gd::EventsList& events, void Launch(gd::EventsList& events,
const gd::ProjectScopedContainers& projectScopedContainers_) { const gd::ProjectScopedContainers& projectScopedContainers) {
projectScopedContainers = &projectScopedContainers_; currentProjectScopedContainers = &projectScopedContainers;
ArbitraryEventsWorker::Launch(events); AbstractArbitraryEventsWorker::VisitEventList(events);
}; };
void Launch(gd::EventsList& events) = delete; protected:
protected:
const gd::ProjectScopedContainers& GetProjectScopedContainers() { const gd::ProjectScopedContainers& GetProjectScopedContainers() {
// Pointers are guaranteed to be not nullptr after // Pointers are guaranteed to be not nullptr after
// Launch was called. // Launch was called.
return *projectScopedContainers; return *currentProjectScopedContainers;
}; };
const gd::ObjectsContainersList& GetObjectsContainersList() { const gd::ObjectsContainersList& GetObjectsContainersList() {
// Pointers are guaranteed to be not nullptr after // Pointers are guaranteed to be not nullptr after
// Launch was called. // Launch was called.
return projectScopedContainers->GetObjectsContainersList(); return currentProjectScopedContainers->GetObjectsContainersList();
}; };
private: private:
const gd::ProjectScopedContainers* projectScopedContainers; bool VisitEvent(gd::BaseEvent& event) override;
const gd::ProjectScopedContainers* currentProjectScopedContainers;
}; };
/** /**
* \brief ReadOnlyArbitraryEventsWorker is an abstract class used to browse events (and * \brief ReadOnlyArbitraryEventsWorker is an abstract class used to browse events (and
* instructions). It can be used to implement autocompletion for example. * instructions). It must not be inherited directly.
* *
* \see gd::ReadOnlyArbitraryEventsWorker
* \see gd::ReadOnlyArbitraryEventsWorkerWithContext * \see gd::ReadOnlyArbitraryEventsWorkerWithContext
* *
* \ingroup IDE * \ingroup IDE
*/ */
class GD_CORE_API ReadOnlyArbitraryEventsWorker : private ReadOnlyEventVisitor { class GD_CORE_API AbstractReadOnlyArbitraryEventsWorker : private ReadOnlyEventVisitor {
public: public:
ReadOnlyArbitraryEventsWorker() : shouldStopIteration(false) {}; AbstractReadOnlyArbitraryEventsWorker() : shouldStopIteration(false) {};
virtual ~ReadOnlyArbitraryEventsWorker(); virtual ~AbstractReadOnlyArbitraryEventsWorker();
/**
* \brief Launch the worker on the specified events list.
*/
void Launch(const gd::EventsList& events) { VisitEventList(events); };
protected: protected:
void StopAnyEventIteration() override; void StopAnyEventIteration() override;
virtual void VisitEvent(const gd::BaseEvent& event) override;
void VisitEventList(const gd::EventsList& events);
private: private:
void VisitEventList(const gd::EventsList& events);
void VisitEvent(const gd::BaseEvent& event) override;
void VisitLinkEvent(const gd::LinkEvent& linkEvent) override; void VisitLinkEvent(const gd::LinkEvent& linkEvent) override;
void VisitInstructionList(const gd::InstructionsList& instructions, void VisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions); bool areConditions);
@@ -213,6 +233,31 @@ protected:
bool shouldStopIteration; bool shouldStopIteration;
}; };
/**
* \brief ReadOnlyArbitraryEventsWorker is an abstract class used to browse events (and
* instructions). It can be used to implement autocompletion for example.
*
* \see gd::ReadOnlyArbitraryEventsWorkerWithContext
*
* \ingroup IDE
*/
class GD_CORE_API ReadOnlyArbitraryEventsWorker
: public AbstractReadOnlyArbitraryEventsWorker {
public:
ReadOnlyArbitraryEventsWorker(){};
virtual ~ReadOnlyArbitraryEventsWorker();
/**
* \brief Launch the worker on the specified events list.
*/
void Launch(const gd::EventsList &events) {
AbstractReadOnlyArbitraryEventsWorker::VisitEventList(events);
};
private:
void VisitEvent(const gd::BaseEvent &event) override;
};
/** /**
* \brief An events worker that will know about the context (the objects * \brief An events worker that will know about the context (the objects
* container). Useful for workers working on expressions notably. * container). Useful for workers working on expressions notably.
@@ -222,10 +267,10 @@ protected:
* \ingroup IDE * \ingroup IDE
*/ */
class GD_CORE_API ReadOnlyArbitraryEventsWorkerWithContext class GD_CORE_API ReadOnlyArbitraryEventsWorkerWithContext
: public ReadOnlyArbitraryEventsWorker { : public AbstractReadOnlyArbitraryEventsWorker {
public: public:
ReadOnlyArbitraryEventsWorkerWithContext() ReadOnlyArbitraryEventsWorkerWithContext()
: projectScopedContainers(nullptr){}; : currentProjectScopedContainers(nullptr){};
virtual ~ReadOnlyArbitraryEventsWorkerWithContext(); virtual ~ReadOnlyArbitraryEventsWorkerWithContext();
/** /**
@@ -233,24 +278,22 @@ class GD_CORE_API ReadOnlyArbitraryEventsWorkerWithContext
* giving the objects container on which the events are applying to. * giving the objects container on which the events are applying to.
*/ */
void Launch(const gd::EventsList& events, void Launch(const gd::EventsList& events,
const gd::ProjectScopedContainers& projectScopedContainers_) { const gd::ProjectScopedContainers& projectScopedContainers) {
projectScopedContainers = &projectScopedContainers_; currentProjectScopedContainers = &projectScopedContainers;
ReadOnlyArbitraryEventsWorker::Launch(events); AbstractReadOnlyArbitraryEventsWorker::VisitEventList(events);
}; };
void Launch(gd::EventsList& events) = delete; protected:
protected:
const gd::ProjectScopedContainers& GetProjectScopedContainers() { const gd::ProjectScopedContainers& GetProjectScopedContainers() {
// Pointers are guaranteed to be not nullptr after // Pointers are guaranteed to be not nullptr after
// Launch was called. // Launch was called.
return *projectScopedContainers; return *currentProjectScopedContainers;
}; };
private: private:
const gd::ProjectScopedContainers* projectScopedContainers; void VisitEvent(const gd::BaseEvent& event) override;
const gd::ProjectScopedContainers* currentProjectScopedContainers;
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_ARBITRARYEVENTSWORKER_H

View File

@@ -0,0 +1,65 @@
/*
* GDevelop Core
* Copyright 2008-2024 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "BehaviorParametersFiller.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Instruction.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace gd {
bool BehaviorParametersFiller::DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) {
const auto &metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
instruction.GetParameters(), metadata.GetParameters(),
[&](const gd::ParameterMetadata &parameterMetadata,
const gd::Expression &parameterValue, size_t parameterIndex,
const gd::String &lastObjectName) {
if (parameterMetadata.GetValueTypeMetadata().IsBehavior() &&
parameterValue.GetPlainString().length() == 0) {
auto &expectedBehaviorTypeName =
parameterMetadata.GetValueTypeMetadata().GetExtraInfo();
auto &objectsContainersList =
projectScopedContainers.GetObjectsContainersList();
auto behaviorNames =
objectsContainersList.GetBehaviorsOfObject(lastObjectName, true);
gd::String foundBehaviorName = "";
for (auto &behaviorName : behaviorNames) {
auto behaviorTypeName =
objectsContainersList.GetTypeOfBehavior(behaviorName, false);
if (behaviorTypeName == expectedBehaviorTypeName) {
foundBehaviorName = behaviorName;
break;
}
}
if (!foundBehaviorName.empty()) {
instruction.SetParameter(parameterIndex,
gd::Expression(foundBehaviorName));
}
}
});
return false;
}
BehaviorParametersFiller::~BehaviorParametersFiller() {}
} // namespace gd

View File

@@ -0,0 +1,45 @@
/*
* GDevelop Core
* Copyright 2008-2024 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
#include <map>
#include <memory>
#include <vector>
namespace gd {
class Instruction;
class Platform;
class ProjectScopedContainers;
} // namespace gd
namespace gd {
/**
* \brief Fill empty behavior parameters with any behavior that matches the
* required behavior type.
*
* \ingroup IDE
*/
class GD_CORE_API BehaviorParametersFiller : public ArbitraryEventsWorker {
public:
BehaviorParametersFiller(
const gd::Platform &platform_,
const gd::ProjectScopedContainers &projectScopedContainers_)
: platform(platform_),
projectScopedContainers(projectScopedContainers_){};
virtual ~BehaviorParametersFiller();
private:
bool DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) override;
const gd::Platform &platform;
const gd::ProjectScopedContainers &projectScopedContainers;
};
} // namespace gd

View File

@@ -588,31 +588,6 @@ bool EventsRefactorer::RemoveObjectInConditions(
return somethingModified; return somethingModified;
} }
void EventsRefactorer::RemoveObjectInEvents(const gd::Platform& platform,
gd::ProjectScopedContainers& projectScopedContainers,
gd::EventsList& events,
gd::String name) {
for (std::size_t i = 0; i < events.size(); ++i) {
vector<gd::InstructionsList*> conditionsVectors =
events[i].GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
bool conditionsModified = RemoveObjectInConditions(
platform, projectScopedContainers, *conditionsVectors[j], name);
}
vector<gd::InstructionsList*> actionsVectors =
events[i].GetAllActionsVectors();
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
bool actionsModified = RemoveObjectInActions(
platform, projectScopedContainers, *actionsVectors[j], name);
}
if (events[i].CanHaveSubEvents())
RemoveObjectInEvents(
platform, projectScopedContainers, events[i].GetSubEvents(), name);
}
}
gd::String ReplaceAllOccurrencesCaseInsensitive(gd::String context, gd::String ReplaceAllOccurrencesCaseInsensitive(gd::String context,
const gd::String& from, const gd::String& from,
const gd::String& to) { const gd::String& to) {

View File

@@ -86,14 +86,6 @@ class GD_CORE_API EventsRefactorer {
gd::String oldName, gd::String oldName,
gd::String newName); gd::String newName);
/**
* Remove all actions or conditions using an object
*/
static void RemoveObjectInEvents(const gd::Platform& platform,
gd::ProjectScopedContainers& projectScopedContainers,
gd::EventsList& events,
gd::String name);
/** /**
* Search for a gd::String in events * Search for a gd::String in events
* *

View File

@@ -0,0 +1,100 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.h"
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableNameFinder.h"
#include "GDCore/IDE/VariableInstructionSwitcher.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace gd {
bool EventsVariableInstructionTypeSwitcher::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const auto& metadata = isCondition
? gd::MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: gd::MetadataProvider::GetActionMetadata(
platform, instruction.GetType());
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
instruction.GetParameters(),
metadata.GetParameters(),
[&](const gd::ParameterMetadata& parameterMetadata,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
const gd::String& type = parameterMetadata.GetType();
if (!gd::ParameterMetadata::IsExpression("variable", type) ||
!gd::VariableInstructionSwitcher::IsSwitchableVariableInstruction(
instruction.GetType())) {
return;
}
const auto variableName =
gd::ExpressionVariableNameFinder::GetVariableName(
*parameterValue.GetRootNode());
const gd::VariablesContainer *variablesContainer = nullptr;
if (type == "objectvar") {
const auto &objectsContainersList =
GetProjectScopedContainers().GetObjectsContainersList();
if (objectsContainersList.HasObjectOrGroupWithVariableNamed(
lastObjectName, variableName) !=
gd::ObjectsContainersList::VariableExistence::DoesNotExist) {
variablesContainer =
GetProjectScopedContainers()
.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(lastObjectName);
}
} else {
if (GetProjectScopedContainers().GetVariablesContainersList().Has(
variableName)) {
variablesContainer =
&GetProjectScopedContainers()
.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(variableName);
}
}
// Every occurrence of the variable or its children are checked.
// Ensuring that a child is actually the one with a type change would
// take more time.
if (variablesContainer == &targetVariablesContainer) {
if (typeChangedVariableNames.find(variableName) !=
typeChangedVariableNames.end()) {
gd::VariableInstructionSwitcher::
SwitchBetweenUnifiedInstructionIfNeeded(
platform, GetProjectScopedContainers(), instruction);
}
}
});
return false;
}
EventsVariableInstructionTypeSwitcher::~EventsVariableInstructionTypeSwitcher() {}
} // namespace gd

View File

@@ -0,0 +1,53 @@
/*
* GDevelop Core
* Copyright 2008-2024 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class VariablesContainer;
class Platform;
} // namespace gd
namespace gd {
/**
* \brief Switch the types of variable instructions for a given set of
* variables.
*
* \see gd::VariableInstructionSwitcher
*
* \ingroup IDE
*/
class GD_CORE_API EventsVariableInstructionTypeSwitcher
: public ArbitraryEventsWorkerWithContext {
public:
EventsVariableInstructionTypeSwitcher(
const gd::Platform &platform_,
const gd::VariablesContainer &targetVariablesContainer_,
const std::unordered_set<gd::String> &typeChangedVariableNames_)
: platform(platform_),
targetVariablesContainer(targetVariablesContainer_),
typeChangedVariableNames(typeChangedVariableNames_){};
virtual ~EventsVariableInstructionTypeSwitcher();
private:
bool DoVisitInstruction(gd::Instruction &instruction,
bool isCondition) override;
const gd::Platform &platform;
const gd::VariablesContainer &targetVariablesContainer;
gd::String objectName;
const std::unordered_set<gd::String> &typeChangedVariableNames;
};
} // namespace gd

View File

@@ -20,6 +20,9 @@
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h" #include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h" #include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h" #include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableNameFinder.h"
#include "GDCore/IDE/VariableInstructionSwitcher.h"
#include "GDCore/IDE/WholeProjectRefactorer.h"
#include "GDCore/Project/Layout.h" #include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h" #include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h" #include "GDCore/Project/ProjectScopedContainers.h"
@@ -42,7 +45,7 @@ class GD_CORE_API ExpressionVariableReplacer
const gd::Platform& platform_, const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_, const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::VariablesContainer& targetVariablesContainer_, const gd::VariablesContainer& targetVariablesContainer_,
const std::unordered_map<gd::String, gd::String>& oldToNewVariableNames_, const VariablesRenamingChangesetNode& variablesRenamingChangesetRoot_,
const std::unordered_set<gd::String>& removedVariableNames_) const std::unordered_set<gd::String>& removedVariableNames_)
: hasDoneRenaming(false), : hasDoneRenaming(false),
removedVariableUsed(false), removedVariableUsed(false),
@@ -50,7 +53,7 @@ class GD_CORE_API ExpressionVariableReplacer
projectScopedContainers(projectScopedContainers_), projectScopedContainers(projectScopedContainers_),
forcedInitialVariablesContainer(nullptr), forcedInitialVariablesContainer(nullptr),
targetVariablesContainer(targetVariablesContainer_), targetVariablesContainer(targetVariablesContainer_),
oldToNewVariableNames(oldToNewVariableNames_), variablesRenamingChangesetRoot(variablesRenamingChangesetRoot_),
removedVariableNames(removedVariableNames_){}; removedVariableNames(removedVariableNames_){};
virtual ~ExpressionVariableReplacer(){}; virtual ~ExpressionVariableReplacer(){};
@@ -80,20 +83,28 @@ class GD_CORE_API ExpressionVariableReplacer
// will be accessed. // will be accessed.
if (forcedInitialVariablesContainer) { if (forcedInitialVariablesContainer) {
const gd::String oldVariableName = node.name;
PushVariablesRenamingChangesetRoot();
// A scope was forced. Honor it: it means this node represents a variable // A scope was forced. Honor it: it means this node represents a variable
// of the forced variables container. // of the forced variables container.
if (forcedInitialVariablesContainer == &targetVariablesContainer) { if (forcedInitialVariablesContainer == &targetVariablesContainer) {
RenameOrRemoveVariableOfTargetVariableContainer(node.name); RenameOrRemoveVariableOfTargetVariableContainer(node.name);
} }
if (node.child) node.child->Visit(*this); if (node.child) {
bool hasBeenPushed =
PushVariablesRenamingChangesetNodeForVariable(oldVariableName);
node.child->Visit(*this);
PopVariablesRenamingChangesetNode(hasBeenPushed);
}
PopVariablesRenamingChangesetNode(true);
return; return;
} }
// Match the potential *new* name of the variable, because refactorings are // Match the potential *new* name of the variable, because refactorings are
// done after changes in the variables container. // done after changes in the variables container.
projectScopedContainers.MatchIdentifierWithName<void>( projectScopedContainers.MatchIdentifierWithName<void>(
GetPotentialNewName(node.name), node.name,
[&]() { [&]() {
// This represents an object. // This represents an object.
// Remember the object name. // Remember the object name.
@@ -103,14 +114,21 @@ class GD_CORE_API ExpressionVariableReplacer
}, },
[&]() { [&]() {
// This is a variable. // This is a variable.
if (projectScopedContainers.GetVariablesContainersList() if (&projectScopedContainers.GetVariablesContainersList()
.HasVariablesContainer(targetVariablesContainer)) { .GetVariablesContainerFromVariableName(node.name) ==
&targetVariablesContainer) {
// The node represents a variable, that can come from the target // The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it: // (because the target is in the scope), replace or remove it:
RenameOrRemoveVariableOfTargetVariableContainer(node.name); PushVariablesRenamingChangesetRoot();
RenameVariableAndVisitChild(node.name, node.child.get());
PopVariablesRenamingChangesetNode(true);
} else {
if (node.child) {
PushVariablesRenamingChangesetNodeForIgnoredVariables();
node.child->Visit(*this);
PopVariablesRenamingChangesetNode(true);
}
} }
if (node.child) node.child->Visit(*this);
}, },
[&]() { [&]() {
// This is a property. // This is a property.
@@ -121,14 +139,7 @@ class GD_CORE_API ExpressionVariableReplacer
if (node.child) node.child->Visit(*this); if (node.child) node.child->Visit(*this);
}, },
[&]() { [&]() {
// This is something else - potentially a deleted variable. // This is something else.
if (projectScopedContainers.GetVariablesContainersList()
.HasVariablesContainer(targetVariablesContainer)) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemoveVariableOfTargetVariableContainer(node.name);
}
if (node.child) node.child->Visit(*this); if (node.child) node.child->Visit(*this);
}); });
} }
@@ -136,25 +147,49 @@ class GD_CORE_API ExpressionVariableReplacer
auto& objectsContainersList = auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList(); projectScopedContainers.GetObjectsContainersList();
if (!objectNameToUseForVariableAccessor.empty()) { if (!objectNameToUseForVariableAccessor.empty()) {
// This is always true because MatchIdentifierWithName is used to get
// objectNameToUseForVariableAccessor.
if (objectsContainersList.HasObjectOrGroupVariablesContainer( if (objectsContainersList.HasObjectOrGroupVariablesContainer(
objectNameToUseForVariableAccessor, targetVariablesContainer)) { objectNameToUseForVariableAccessor, targetVariablesContainer)) {
objectNameToUseForVariableAccessor = "";
// The node represents an object variable, and this object variables are // The node represents an object variable, and this object variables are
// the target. Do the replacement or removals: // the target. Do the replacement or removals:
RenameOrRemoveVariableOfTargetVariableContainer(node.name); PushVariablesRenamingChangesetRoot();
RenameVariableAndVisitChild(node.name, node.child.get());
PopVariablesRenamingChangesetNode(true);
} }
} else {
RenameVariableAndVisitChild(node.name, node.child.get());
} }
objectNameToUseForVariableAccessor = "";
if (node.child) node.child->Visit(*this);
} }
void OnVisitVariableBracketAccessorNode( void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override { VariableBracketAccessorNode& node) override {
objectNameToUseForVariableAccessor = ""; objectNameToUseForVariableAccessor = "";
// TODO Literal expressions could be checked to handle renaming in
// `expression` and in `child`.
node.expression->Visit(*this); node.expression->Visit(*this);
if (node.child) node.child->Visit(*this); if (node.child) {
PushVariablesRenamingChangesetNodeForIgnoredVariables();
node.child->Visit(*this);
PopVariablesRenamingChangesetNode(true);
}
} }
void OnVisitIdentifierNode(IdentifierNode& node) override { void OnVisitIdentifierNode(IdentifierNode &node) override {
auto renameVariableAndChild = [this, &node]() {
PushVariablesRenamingChangesetRoot();
const gd::String oldVariableName = node.identifierName;
RenameOrRemoveVariableOfTargetVariableContainer(node.identifierName);
if (!node.childIdentifierName.empty()) {
bool hasBeenPushed =
PushVariablesRenamingChangesetNodeForVariable(oldVariableName);
RenameOrRemoveVariableOfTargetVariableContainer(
node.childIdentifierName);
PopVariablesRenamingChangesetNode(hasBeenPushed);
}
PopVariablesRenamingChangesetNode(true);
};
auto& objectsContainersList = auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList(); projectScopedContainers.GetObjectsContainersList();
@@ -166,7 +201,7 @@ class GD_CORE_API ExpressionVariableReplacer
// A scope was forced. Honor it: it means this node represents a variable // A scope was forced. Honor it: it means this node represents a variable
// of the forced variables container. // of the forced variables container.
if (forcedInitialVariablesContainer == &targetVariablesContainer) { if (forcedInitialVariablesContainer == &targetVariablesContainer) {
RenameOrRemoveVariableOfTargetVariableContainer(node.identifierName); renameVariableAndChild();
} }
return; return;
} }
@@ -174,25 +209,27 @@ class GD_CORE_API ExpressionVariableReplacer
// Match the potential *new* name of the variable, because refactorings are // Match the potential *new* name of the variable, because refactorings are
// done after changes in the variables container. // done after changes in the variables container.
projectScopedContainers.MatchIdentifierWithName<void>( projectScopedContainers.MatchIdentifierWithName<void>(
GetPotentialNewName(node.identifierName), node.identifierName,
[&]() { [&]() {
// This represents an object. // This represents an object.
if (objectsContainersList.HasObjectOrGroupVariablesContainer( if (objectsContainersList.HasObjectOrGroupVariablesContainer(
node.identifierName, targetVariablesContainer)) { node.identifierName, targetVariablesContainer)) {
// The node represents an object variable, and this object variables // The node represents an object variable, and this object variables
// are the target. Do the replacement or removals: // are the target. Do the replacement or removals:
PushVariablesRenamingChangesetRoot();
RenameOrRemoveVariableOfTargetVariableContainer( RenameOrRemoveVariableOfTargetVariableContainer(
node.childIdentifierName); node.childIdentifierName);
PopVariablesRenamingChangesetNode(true);
} }
}, },
[&]() { [&]() {
// This is a variable. // This is a variable.
if (projectScopedContainers.GetVariablesContainersList() if (&projectScopedContainers.GetVariablesContainersList()
.HasVariablesContainer(targetVariablesContainer)) { .GetVariablesContainerFromVariableName(
node.identifierName) == &targetVariablesContainer) {
// The node represents a variable, that can come from the target // The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it: // (because the target is in the scope), replace or remove it:
RenameOrRemoveVariableOfTargetVariableContainer( renameVariableAndChild();
node.identifierName);
} }
}, },
[&]() { [&]() {
@@ -202,14 +239,7 @@ class GD_CORE_API ExpressionVariableReplacer
// This is a parameter. // This is a parameter.
}, },
[&]() { [&]() {
// This is something else - potentially a deleted variable. // This is something else.
if (projectScopedContainers.GetVariablesContainersList()
.HasVariablesContainer(targetVariablesContainer)) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:
RenameOrRemoveVariableOfTargetVariableContainer(
node.identifierName);
}
}); });
} }
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {} void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
@@ -268,19 +298,24 @@ class GD_CORE_API ExpressionVariableReplacer
bool hasDoneRenaming; bool hasDoneRenaming;
bool removedVariableUsed; bool removedVariableUsed;
const gd::String& GetPotentialNewName(const gd::String& oldName) {
return oldToNewVariableNames.count(oldName) >= 1
? oldToNewVariableNames.find(oldName)->second
: oldName;
}
bool RenameOrRemoveVariableOfTargetVariableContainer( bool RenameOrRemoveVariableOfTargetVariableContainer(
gd::String& variableName) { gd::String& variableName) {
const auto *currentVariablesRenamingChangesetNode =
GetCurrentVariablesRenamingChangesetNode();
if (!currentVariablesRenamingChangesetNode) {
return false;
}
const auto &oldToNewVariableNames =
currentVariablesRenamingChangesetNode->oldToNewVariableNames;
if (oldToNewVariableNames.count(variableName) >= 1) { if (oldToNewVariableNames.count(variableName) >= 1) {
variableName = oldToNewVariableNames.find(variableName)->second; variableName = oldToNewVariableNames.find(variableName)->second;
hasDoneRenaming = true; hasDoneRenaming = true;
return true; return true;
} else if (removedVariableNames.count(variableName) >= 1) { } else if (
// Only the root variable is checked for removing.
currentVariablesRenamingChangesetNode ==
&variablesRenamingChangesetRoot &&
removedVariableNames.count(variableName) >= 1) {
removedVariableUsed = true; removedVariableUsed = true;
return true; return true;
} }
@@ -288,6 +323,62 @@ class GD_CORE_API ExpressionVariableReplacer
return false; // Nothing was changed or done. return false; // Nothing was changed or done.
} }
void RenameVariableAndVisitChild(gd::String &variableName,
ExpressionNode *childNode) {
// `variableName` is modified by
// `RenameOrRemoveVariableOfTargetVariableContainer`.
const gd::String oldVariableName = variableName;
RenameOrRemoveVariableOfTargetVariableContainer(variableName);
if (childNode) {
bool hasBeenPushed =
PushVariablesRenamingChangesetNodeForVariable(oldVariableName);
childNode->Visit(*this);
PopVariablesRenamingChangesetNode(hasBeenPushed);
}
}
void PushVariablesRenamingChangesetRoot() {
variablesRenamingChangesetNodeStack.push_back(&variablesRenamingChangesetRoot);
}
void PushVariablesRenamingChangesetNodeForIgnoredVariables() {
variablesRenamingChangesetNodeStack.push_back(nullptr);
}
const gd::VariablesRenamingChangesetNode *GetCurrentVariablesRenamingChangesetNode() {
return variablesRenamingChangesetNodeStack.size() == 0 ?
&variablesRenamingChangesetRoot :
variablesRenamingChangesetNodeStack
[variablesRenamingChangesetNodeStack.size() - 1];
}
bool PushVariablesRenamingChangesetNodeForVariable(const gd::String& variableName) {
const auto *currentVariablesRenamingChangesetNode = GetCurrentVariablesRenamingChangesetNode();
if (!currentVariablesRenamingChangesetNode) {
// There were already no more change on a parent.
return false;
}
const auto &childVariablesRenamingChangesetNodeItr =
currentVariablesRenamingChangesetNode->modifiedVariables.find(
variableName);
if (childVariablesRenamingChangesetNodeItr ==
currentVariablesRenamingChangesetNode->modifiedVariables.end()) {
// There is no more change on the current variable child.
variablesRenamingChangesetNodeStack.push_back(nullptr);
}
else {
variablesRenamingChangesetNodeStack.push_back(
childVariablesRenamingChangesetNodeItr->second.get());
}
return true;
}
void PopVariablesRenamingChangesetNode(bool hasBeenPushed) {
if (hasBeenPushed) {
variablesRenamingChangesetNodeStack.pop_back();
}
}
// Scope: // Scope:
const gd::Platform& platform; const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers; const gd::ProjectScopedContainers& projectScopedContainers;
@@ -295,10 +386,11 @@ class GD_CORE_API ExpressionVariableReplacer
// Renaming or removing to do: // Renaming or removing to do:
const gd::VariablesContainer& targetVariablesContainer; const gd::VariablesContainer& targetVariablesContainer;
const std::unordered_map<gd::String, gd::String>& oldToNewVariableNames; const VariablesRenamingChangesetNode &variablesRenamingChangesetRoot;
const std::unordered_set<gd::String>& removedVariableNames; const std::unordered_set<gd::String>& removedVariableNames;
gd::String objectNameToUseForVariableAccessor; gd::String objectNameToUseForVariableAccessor;
std::vector<const VariablesRenamingChangesetNode*> variablesRenamingChangesetNodeStack;
}; };
const gd::VariablesContainer* const gd::VariablesContainer*
@@ -351,7 +443,7 @@ bool EventsVariableReplacer::DoVisitInstruction(gd::Instruction& instruction,
ExpressionVariableReplacer renamer(platform, ExpressionVariableReplacer renamer(platform,
GetProjectScopedContainers(), GetProjectScopedContainers(),
targetVariablesContainer, targetVariablesContainer,
oldToNewVariableNames, variablesRenamingChangesetRoot,
removedVariableNames); removedVariableNames);
renamer.SetForcedInitialVariablesContainer( renamer.SetForcedInitialVariablesContainer(
FindForcedVariablesContainerIfAny(type, lastObjectName)); FindForcedVariablesContainerIfAny(type, lastObjectName));
@@ -383,7 +475,7 @@ bool EventsVariableReplacer::DoVisitEventExpression(
ExpressionVariableReplacer renamer(platform, ExpressionVariableReplacer renamer(platform,
GetProjectScopedContainers(), GetProjectScopedContainers(),
targetVariablesContainer, targetVariablesContainer,
oldToNewVariableNames, variablesRenamingChangesetRoot,
removedVariableNames); removedVariableNames);
renamer.SetForcedInitialVariablesContainer( renamer.SetForcedInitialVariablesContainer(
FindForcedVariablesContainerIfAny(type, "")); FindForcedVariablesContainerIfAny(type, ""));

View File

@@ -4,6 +4,7 @@
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#pragma once #pragma once
#include <map> #include <map>
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
@@ -12,11 +13,13 @@
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h" #include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h" #include "GDCore/String.h"
namespace gd { namespace gd {
class BaseEvent; class BaseEvent;
class VariablesContainer; class VariablesContainer;
class EventsList; class EventsList;
class Platform; class Platform;
struct VariablesRenamingChangesetNode;
} // namespace gd } // namespace gd
namespace gd { namespace gd {
@@ -33,12 +36,12 @@ class GD_CORE_API EventsVariableReplacer
EventsVariableReplacer( EventsVariableReplacer(
const gd::Platform &platform_, const gd::Platform &platform_,
const gd::VariablesContainer &targetVariablesContainer_, const gd::VariablesContainer &targetVariablesContainer_,
const std::unordered_map<gd::String, gd::String> &oldToNewVariableNames_, const VariablesRenamingChangesetNode &variablesRenamingChangesetRoot_,
const std::unordered_set<gd::String> &removedVariableNames_) const std::unordered_set<gd::String> &removedVariableNames_)
: platform(platform_), : platform(platform_),
targetVariablesContainer(targetVariablesContainer_), targetVariablesContainer(targetVariablesContainer_),
oldToNewVariableNames(oldToNewVariableNames_), variablesRenamingChangesetRoot(variablesRenamingChangesetRoot_),
removedVariableNames(removedVariableNames_){}; removedVariableNames(removedVariableNames_) {};
virtual ~EventsVariableReplacer(); virtual ~EventsVariableReplacer();
private: private:
@@ -53,7 +56,7 @@ class GD_CORE_API EventsVariableReplacer
const gd::Platform &platform; const gd::Platform &platform;
const gd::VariablesContainer &targetVariablesContainer; const gd::VariablesContainer &targetVariablesContainer;
gd::String objectName; gd::String objectName;
const std::unordered_map<gd::String, gd::String> &oldToNewVariableNames; const VariablesRenamingChangesetNode &variablesRenamingChangesetRoot;
const std::unordered_set<gd::String> &removedVariableNames; const std::unordered_set<gd::String> &removedVariableNames;
}; };

View File

@@ -229,7 +229,7 @@ std::set<gd::String> EventsVariablesFinder::FindAllObjectVariables(
const gd::Platform& platform, const gd::Platform& platform,
const gd::Project& project, const gd::Project& project,
const gd::Layout& layout, const gd::Layout& layout,
const gd::Object& object) { const gd::String& objectName) {
std::set<gd::String> results; std::set<gd::String> results;
FindArgumentsInEventsAndDependencies( FindArgumentsInEventsAndDependencies(
@@ -238,7 +238,7 @@ std::set<gd::String> EventsVariablesFinder::FindAllObjectVariables(
project, project,
layout, layout,
"objectvar", "objectvar",
object.GetName()); objectName);
return results; return results;
} }

View File

@@ -63,14 +63,14 @@ class EventsVariablesFinder {
* *
* \param project The project * \param project The project
* \param layout The layout to use. * \param layout The layout to use.
* \param object The object to be scanned * \param objectName The name of the object to be scanned
* \return A std::set containing the names of all object variables used. * \return A std::set containing the names of all object variables used.
*/ */
static std::set<gd::String> FindAllObjectVariables( static std::set<gd::String> FindAllObjectVariables(
const gd::Platform& platform, const gd::Platform& platform,
const gd::Project& project, const gd::Project& project,
const gd::Layout& layout, const gd::Layout& layout,
const gd::Object& object); const gd::String& objectName);
private: private:

View File

@@ -3,8 +3,7 @@
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_EXPRESSIONAUTOCOMPLETIONPROVIDER_H #pragma once
#define GDCORE_EXPRESSIONAUTOCOMPLETIONPROVIDER_H
#include <memory> #include <memory>
#include <vector> #include <vector>
@@ -20,7 +19,7 @@
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h" #include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h" #include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h" #include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableParentFinder.h" #include "GDCore/IDE/Events/ExpressionVariablePathFinder.h"
#include "GDCore/Project/ProjectScopedContainers.h" #include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/Variable.h" #include "GDCore/Project/Variable.h"
@@ -147,6 +146,19 @@ struct GD_CORE_API ExpressionCompletionDescription {
return *this; return *this;
} }
/**
* \brief Return the scope of the variable, for a variable completion.
*/
gd::VariablesContainer::SourceType GetVariableScope() const {
return variableScope;
}
ExpressionCompletionDescription &
SetVariableScope(gd::VariablesContainer::SourceType variableScope_) {
variableScope = variableScope_;
return *this;
}
/** /**
* \brief Return the prefix that must be completed. * \brief Return the prefix that must be completed.
*/ */
@@ -315,6 +327,7 @@ struct GD_CORE_API ExpressionCompletionDescription {
size_t replacementEndPosition_) size_t replacementEndPosition_)
: completionKind(completionKind_), : completionKind(completionKind_),
variableType(gd::Variable::Number), variableType(gd::Variable::Number),
variableScope(gd::VariablesContainer::Unknown),
replacementStartPosition(replacementStartPosition_), replacementStartPosition(replacementStartPosition_),
replacementEndPosition(replacementEndPosition_), replacementEndPosition(replacementEndPosition_),
isExact(false), isExact(false),
@@ -325,6 +338,7 @@ struct GD_CORE_API ExpressionCompletionDescription {
private: private:
CompletionKind completionKind; CompletionKind completionKind;
gd::Variable::Type variableType; gd::Variable::Type variableType;
gd::VariablesContainer::SourceType variableScope;
gd::String type; gd::String type;
gd::String prefix; gd::String prefix;
gd::String completion; gd::String completion;
@@ -525,7 +539,7 @@ class GD_CORE_API ExpressionCompletionFinder
} }
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override { void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
VariableAndItsParent variableAndItsParent = VariableAndItsParent variableAndItsParent =
gd::ExpressionVariableParentFinder::GetLastParentOfNode( gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, node); platform, projectScopedContainers, node);
// If no child, we're at the end of a variable (like `GrandChild` in // If no child, we're at the end of a variable (like `GrandChild` in
@@ -666,7 +680,7 @@ class GD_CORE_API ExpressionCompletionFinder
[&]() { [&]() {
// This is a variable. // This is a variable.
VariableAndItsParent variableAndItsParent = VariableAndItsParent variableAndItsParent =
gd::ExpressionVariableParentFinder::GetLastParentOfNode( gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, node); platform, projectScopedContainers, node);
AddCompletionsForChildrenVariablesOf( AddCompletionsForChildrenVariablesOf(
@@ -933,6 +947,7 @@ class GD_CORE_API ExpressionCompletionFinder
location.GetEndPosition()); location.GetEndPosition());
description.SetCompletion(variableName); description.SetCompletion(variableName);
description.SetVariableType(variable.GetType()); description.SetVariableType(variable.GetType());
description.SetVariableScope(variablesContainer.GetSourceType());
completions.push_back(description); completions.push_back(description);
if (eagerlyCompleteIfExactMatch && variableName == search) { if (eagerlyCompleteIfExactMatch && variableName == search) {
@@ -958,6 +973,7 @@ class GD_CORE_API ExpressionCompletionFinder
location.GetEndPosition()); location.GetEndPosition());
description.SetCompletion(variableName); description.SetCompletion(variableName);
description.SetVariableType(variable.GetType()); description.SetVariableType(variable.GetType());
description.SetVariableScope(gd::VariablesContainer::Object);
completions.push_back(description); completions.push_back(description);
if (eagerlyCompleteIfExactMatch && variableName == search) { if (eagerlyCompleteIfExactMatch && variableName == search) {
@@ -1012,6 +1028,10 @@ class GD_CORE_API ExpressionCompletionFinder
location.GetEndPosition()); location.GetEndPosition());
description.SetCompletion(variableName); description.SetCompletion(variableName);
description.SetVariableType(variable.GetType()); description.SetVariableType(variable.GetType());
description.SetVariableScope(
projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(variableName)
.GetSourceType());
completions.push_back(description); completions.push_back(description);
if (eagerlyCompleteIfExactMatch && variableName == search) { if (eagerlyCompleteIfExactMatch && variableName == search) {
@@ -1060,6 +1080,10 @@ class GD_CORE_API ExpressionCompletionFinder
location.GetEndPosition()); location.GetEndPosition());
description.SetCompletion(variableName); description.SetCompletion(variableName);
description.SetVariableType(variable.GetType()); description.SetVariableType(variable.GetType());
description.SetVariableScope(
projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(variableName)
.GetSourceType());
completions.push_back(description); completions.push_back(description);
if (eagerlyCompleteIfExactMatch && variableName == search) { if (eagerlyCompleteIfExactMatch && variableName == search) {
@@ -1113,5 +1137,3 @@ class GD_CORE_API ExpressionCompletionFinder
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_EXPRESSIONAUTOCOMPLETIONPROVIDER_H

View File

@@ -106,20 +106,20 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
auto variableExistence = objectsContainersList.HasObjectOrGroupWithVariableNamed(identifier.identifierName, identifier.childIdentifierName); auto variableExistence = objectsContainersList.HasObjectOrGroupWithVariableNamed(identifier.identifierName, identifier.childIdentifierName);
if (variableExistence == gd::ObjectsContainersList::DoesNotExist) { if (variableExistence == gd::ObjectsContainersList::DoesNotExist) {
RaiseTypeError(_("This variable does not exist on this object or group."), RaiseUndeclaredVariableError(_("This variable does not exist on this object or group."),
identifier.childIdentifierNameLocation); identifier.childIdentifierNameLocation, identifier.childIdentifierName, identifier.identifierName);
return true; // We should have found a variable. return true; // We should have found a variable.
} }
else if (variableExistence == gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) { else if (variableExistence == gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) {
RaiseTypeError(_("This variable only exists on some objects of the group. It must be declared for all objects."), RaiseUndeclaredVariableError(_("This variable only exists on some objects of the group. It must be declared for all objects."),
identifier.childIdentifierNameLocation); identifier.childIdentifierNameLocation, identifier.childIdentifierName, identifier.identifierName);
return true; // We should have found a variable. return true; // We should have found a variable.
} }
else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) { else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) {
RaiseTypeError(_("This group is empty. Add an object to this group first."), RaiseUndeclaredVariableError(_("This group is empty. Add an object to this group first."),
identifier.identifierNameLocation); identifier.identifierNameLocation, identifier.childIdentifierName, identifier.identifierName);
return true; // We should have found a variable. return true; // We should have found a variable.
} }
@@ -166,8 +166,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
return true; // We found a property, even if the child is not allowed. return true; // We found a property, even if the child is not allowed.
} }
const gd::NamedPropertyDescriptor& property = projectScopedContainers const gd::NamedPropertyDescriptor& property = propertiesContainersList.Get(identifier.identifierName).second;
.GetPropertiesContainersList().Get(identifier.identifierName).second;
if (property.GetType() == "Number") { if (property.GetType() == "Number") {
childType = Type::Number; childType = Type::Number;
@@ -248,9 +247,10 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
if (!function.behaviorName.empty() && if (!function.behaviorName.empty() &&
!objectsContainersList.HasBehaviorInObjectOrGroup(function.objectName, !objectsContainersList.HasBehaviorInObjectOrGroup(function.objectName,
function.behaviorName)) { function.behaviorName)) {
RaiseTypeError(_("This behavior is not attached to this object."), RaiseError(gd::ExpressionParserError::ErrorType::MissingBehavior,
function.behaviorNameLocation, _("This behavior is not attached to this object."),
/*isFatal=*/false); function.behaviorNameLocation,
/*isFatal=*/false, function.behaviorName, function.objectName);
return returnType; return returnType;
} }
@@ -264,11 +264,11 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) { if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
if (function.functionName.empty()) { if (function.functionName.empty()) {
RaiseError("invalid_function_name", RaiseError(gd::ExpressionParserError::ErrorType::InvalidFunctionName,
_("Enter the name of the function to call."), _("Enter the name of the function to call."),
function.location); function.location);
} else { } else {
RaiseError("invalid_function_name", RaiseError(gd::ExpressionParserError::ErrorType::InvalidFunctionName,
_("Cannot find an expression with this name: ") + _("Cannot find an expression with this name: ") +
function.functionName + "\n" + function.functionName + "\n" +
_("Double check that you've not made any typo in the name."), _("Double check that you've not made any typo in the name."),
@@ -341,13 +341,13 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
if (function.parameters.size() < minParametersCount) { if (function.parameters.size() < minParametersCount) {
RaiseError( RaiseError(
"too_few_parameters", gd::ExpressionParserError::ErrorType::TooFewParameters,
_("You have not entered enough parameters for the expression.") + _("You have not entered enough parameters for the expression.") +
" " + expectedCountMessage, " " + expectedCountMessage,
function.location); function.location);
} else { } else {
RaiseError( RaiseError(
"extra_parameter", gd::ExpressionParserError::ErrorType::TooManyParameters,
_("This parameter was not expected by this expression. Remove it " _("This parameter was not expected by this expression. Remove it "
"or verify that you've entered the proper expression name.") + "or verify that you've entered the proper expression name.") +
" " + expectedCountMessage, " " + expectedCountMessage,
@@ -376,7 +376,10 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
dynamic_cast<EmptyNode*>(parameter.get()) == nullptr) { dynamic_cast<EmptyNode*>(parameter.get()) == nullptr) {
auto currentParentType = parentType; auto currentParentType = parentType;
parentType = StringToType(parameterMetadata.GetType()); parentType = StringToType(parameterMetadata.GetType());
auto parentParameterExtraInfo = currentParameterExtraInfo;
currentParameterExtraInfo = &parameterMetadata.GetExtraInfo();
parameter->Visit(*this); parameter->Visit(*this);
currentParameterExtraInfo = parentParameterExtraInfo;
parentType = currentParentType; parentType = currentParentType;
const gd::String& expectedParameterType = parameterMetadata.GetType(); const gd::String& expectedParameterType = parameterMetadata.GetType();
@@ -384,7 +387,7 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
ExpressionValidator::variableTypeString, expectedParameterType)) { ExpressionValidator::variableTypeString, expectedParameterType)) {
if (dynamic_cast<IdentifierNode*>(parameter.get()) == nullptr && if (dynamic_cast<IdentifierNode*>(parameter.get()) == nullptr &&
dynamic_cast<VariableNode*>(parameter.get()) == nullptr) { dynamic_cast<VariableNode*>(parameter.get()) == nullptr) {
RaiseError("malformed_variable_parameter", RaiseError(gd::ExpressionParserError::ErrorType::MalformedVariableParameter,
_("A variable name was expected but something else was " _("A variable name was expected but something else was "
"written. Enter just the name of the variable for this " "written. Enter just the name of the variable for this "
"parameter."), "parameter."),
@@ -392,7 +395,7 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
} }
} else if (gd::ParameterMetadata::IsObject(expectedParameterType)) { } else if (gd::ParameterMetadata::IsObject(expectedParameterType)) {
if (dynamic_cast<IdentifierNode*>(parameter.get()) == nullptr) { if (dynamic_cast<IdentifierNode*>(parameter.get()) == nullptr) {
RaiseError("malformed_object_parameter", RaiseError(gd::ExpressionParserError::ErrorType::MalformedObjectParameter,
_("An object name was expected but something else was " _("An object name was expected but something else was "
"written. Enter just the name of the object for this " "written. Enter just the name of the object for this "
"parameter."), "parameter."),
@@ -406,7 +409,7 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
!gd::ParameterMetadata::IsExpression( !gd::ParameterMetadata::IsExpression(
ExpressionValidator::stringTypeString, ExpressionValidator::stringTypeString,
expectedParameterType)) { expectedParameterType)) {
RaiseError("unknown_parameter_type", RaiseError(gd::ExpressionParserError::ErrorType::UnknownParameterType,
_("This function is improperly set up. Reach out to the " _("This function is improperly set up. Reach out to the "
"extension developer or a GDevelop maintainer to fix " "extension developer or a GDevelop maintainer to fix "
"this issue"), "this issue"),
@@ -440,6 +443,10 @@ const gd::String& ExpressionValidator::TypeToString(Type type) {
return numberOrStringTypeString; return numberOrStringTypeString;
case Type::Variable: case Type::Variable:
return variableTypeString; return variableTypeString;
case Type::LegacyVariable:
// This function is only used to display error.
// Users don't care if it's legacy or not.
return variableTypeString;
case Type::Object: case Type::Object:
return objectTypeString; return objectTypeString;
case Type::Empty: case Type::Empty:
@@ -466,7 +473,12 @@ ExpressionValidator::Type ExpressionValidator::StringToType(
if (type == ExpressionValidator::variableTypeString || if (type == ExpressionValidator::variableTypeString ||
gd::ParameterMetadata::IsExpression( gd::ParameterMetadata::IsExpression(
ExpressionValidator::variableTypeString, type)) { ExpressionValidator::variableTypeString, type)) {
return Type::Variable; if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
return Type::LegacyVariable;
}
else {
return Type::Variable;
}
} }
if (type == ExpressionValidator::objectTypeString || if (type == ExpressionValidator::objectTypeString ||
gd::ParameterMetadata::IsObject(type)) { gd::ParameterMetadata::IsObject(type)) {

View File

@@ -3,8 +3,7 @@
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_EXPRESSIONVALIDATOR_H #pragma once
#define GDCORE_EXPRESSIONVALIDATOR_H
#include <memory> #include <memory>
#include <vector> #include <vector>
@@ -39,12 +38,14 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
public: public:
ExpressionValidator(const gd::Platform &platform_, ExpressionValidator(const gd::Platform &platform_,
const gd::ProjectScopedContainers & projectScopedContainers_, const gd::ProjectScopedContainers & projectScopedContainers_,
const gd::String &rootType_) const gd::String &rootType_,
const gd::String &extraInfo_ = "")
: platform(platform_), : platform(platform_),
projectScopedContainers(projectScopedContainers_), projectScopedContainers(projectScopedContainers_),
parentType(StringToType(gd::ParameterMetadata::GetExpressionValueType(rootType_))), parentType(StringToType(gd::ParameterMetadata::GetExpressionValueType(rootType_))),
childType(Type::Unknown), childType(Type::Unknown),
forbidsUsageOfBracketsBecauseParentIsObject(false) {}; forbidsUsageOfBracketsBecauseParentIsObject(false),
currentParameterExtraInfo(&extraInfo_) {};
virtual ~ExpressionValidator(){}; virtual ~ExpressionValidator(){};
/** /**
@@ -65,7 +66,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
* *
* Expressions with fatal error can't be generated. * Expressions with fatal error can't be generated.
*/ */
const std::vector<ExpressionParserDiagnostic*>& GetFatalErrors() { const std::vector<ExpressionParserError*>& GetFatalErrors() {
return fatalErrors; return fatalErrors;
}; };
@@ -74,7 +75,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
* *
* No errors means that the expression is valid. * No errors means that the expression is valid.
*/ */
const std::vector<ExpressionParserDiagnostic*>& GetAllErrors() { const std::vector<ExpressionParserError*>& GetAllErrors() {
return allErrors; return allErrors;
}; };
@@ -93,14 +94,14 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
if (leftType == Type::Number) { if (leftType == Type::Number) {
if (node.op == ' ') { if (node.op == ' ') {
RaiseError("syntax_error", RaiseError(gd::ExpressionParserError::ErrorType::SyntaxError,
"No operator found. Did you forget to enter an operator (like +, -, " "No operator found. Did you forget to enter an operator (like +, -, "
"* or /) between numbers or expressions?", node.rightHandSide->location); "* or /) between numbers or expressions?", node.rightHandSide->location);
} }
} }
else if (leftType == Type::String) { else if (leftType == Type::String) {
if (node.op == ' ') { if (node.op == ' ') {
RaiseError("syntax_error", RaiseError(gd::ExpressionParserError::ErrorType::SyntaxError,
"You must add the operator + between texts or expressions. For " "You must add the operator + between texts or expressions. For "
"example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location); "example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
} }
@@ -115,7 +116,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
_("Operators (+, -, /, *) can't be used with an object name. Remove " _("Operators (+, -, /, *) can't be used with an object name. Remove "
"the operator."), "the operator."),
node.rightHandSide->location); node.rightHandSide->location);
} else if (leftType == Type::Variable) { } else if (leftType == Type::Variable || leftType == Type::LegacyVariable) {
RaiseOperatorError( RaiseOperatorError(
_("Operators (+, -, /, *) can't be used in variable names. Remove " _("Operators (+, -, /, *) can't be used in variable names. Remove "
"the operator from the variable name."), "the operator from the variable name."),
@@ -162,7 +163,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
_("Operators (+, -) can't be used with an object name. Remove the " _("Operators (+, -) can't be used with an object name. Remove the "
"operator."), "operator."),
node.location); node.location);
} else if (rightType == Type::Variable) { } else if (rightType == Type::Variable || rightType == Type::LegacyVariable) {
RaiseTypeError( RaiseTypeError(
_("Operators (+, -) can't be used in variable names. Remove " _("Operators (+, -) can't be used in variable names. Remove "
"the operator from the variable name."), "the operator from the variable name."),
@@ -200,7 +201,20 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
ReportAnyError(node); ReportAnyError(node);
if (parentType == Type::Variable) { if (parentType == Type::Variable) {
childType = Type::Variable; childType = parentType;
if (!currentParameterExtraInfo || *currentParameterExtraInfo != "AllowUndeclaredVariable") {
const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
if (!variablesContainersList.Has(node.name)) {
RaiseUndeclaredVariableError(_("No variable with this name found."), node.location, node.name);
}
}
if (node.child) {
node.child->Visit(*this);
}
} else if (parentType == Type::LegacyVariable) {
childType = parentType;
if (node.child) { if (node.child) {
node.child->Visit(*this); node.child->Visit(*this);
@@ -270,7 +284,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
ReportAnyError(node); ReportAnyError(node);
if (forbidsUsageOfBracketsBecauseParentIsObject) { if (forbidsUsageOfBracketsBecauseParentIsObject) {
RaiseError("brackets_not_allowed_for_objects", RaiseError(gd::ExpressionParserError::ErrorType::BracketsNotAllowedForObjects,
_("You can't use the brackets to access an object variable. " _("You can't use the brackets to access an object variable. "
"Use a dot followed by the variable name, like this: " "Use a dot followed by the variable name, like this: "
"`MyObject.MyVariable`."), "`MyObject.MyVariable`."),
@@ -279,9 +293,14 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
forbidsUsageOfBracketsBecauseParentIsObject = false; forbidsUsageOfBracketsBecauseParentIsObject = false;
Type currentParentType = parentType; Type currentParentType = parentType;
Type currentChildType = childType;
parentType = Type::NumberOrString; parentType = Type::NumberOrString;
auto parentParameterExtraInfo = currentParameterExtraInfo;
currentParameterExtraInfo = nullptr;
node.expression->Visit(*this); node.expression->Visit(*this);
currentParameterExtraInfo = parentParameterExtraInfo;
parentType = currentParentType; parentType = currentParentType;
childType = currentChildType;
if (node.child) { if (node.child) {
node.child->Visit(*this); node.child->Visit(*this);
@@ -293,7 +312,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
if (!ValidateObjectVariableOrVariableOrProperty(node)) { if (!ValidateObjectVariableOrVariableOrProperty(node)) {
// The identifier is not a variable, so either the variable is not properly declared // The identifier is not a variable, so either the variable is not properly declared
// or it's a text without quotes. // or it's a text without quotes.
RaiseTypeError(_("You must wrap your text inside double quotes " RaiseUnknownIdentifierError(_("You must wrap your text inside double quotes "
"(example: \"Hello world\")."), "(example: \"Hello world\")."),
node.location); node.location);
} }
@@ -301,20 +320,27 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
else if (parentType == Type::Number) { else if (parentType == Type::Number) {
if (!ValidateObjectVariableOrVariableOrProperty(node)) { if (!ValidateObjectVariableOrVariableOrProperty(node)) {
// The identifier is not a variable, so the variable is not properly declared. // The identifier is not a variable, so the variable is not properly declared.
RaiseTypeError( RaiseUnknownIdentifierError(_("You must enter a number."), node.location);
_("You must enter a number."), node.location);
} }
} }
else if (parentType == Type::NumberOrString) { else if (parentType == Type::NumberOrString) {
if (!ValidateObjectVariableOrVariableOrProperty(node)) { if (!ValidateObjectVariableOrVariableOrProperty(node)) {
// The identifier is not a variable, so either the variable is not properly declared // The identifier is not a variable, so either the variable is not properly declared
// or it's a text without quotes. // or it's a text without quotes.
RaiseTypeError( RaiseUnknownIdentifierError(
_("You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."), _("You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."),
node.location); node.location);
} }
} }
else if (parentType != Type::Object && parentType != Type::Variable) { else if (parentType == Type::Variable) {
if (!currentParameterExtraInfo || *currentParameterExtraInfo != "AllowUndeclaredVariable") {
const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
if (!variablesContainersList.Has(node.identifierName)) {
RaiseUndeclaredVariableError(_("No variable with this name found."), node.location, node.identifierName);
}
}
}
else if (parentType != Type::Object && parentType != Type::LegacyVariable) {
// It can't happen. // It can't happen.
RaiseTypeError( RaiseTypeError(
_("You've entered a name, but this type was expected:") + " " + TypeToString(parentType), _("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
@@ -338,7 +364,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
} else if (parentType == Type::String) { } else if (parentType == Type::String) {
message = _( message = _(
"You must enter a text (between quotes) or a valid expression call."); "You must enter a text (between quotes) or a valid expression call.");
} else if (parentType == Type::Variable) { } else if (parentType == Type::Variable || parentType == Type::LegacyVariable) {
message = _("You must enter a variable name."); message = _("You must enter a variable name.");
} else if (parentType == Type::Object) { } else if (parentType == Type::Object) {
message = _("You must enter a valid object name."); message = _("You must enter a valid object name.");
@@ -351,12 +377,12 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
} }
private: private:
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, Object, Empty}; enum Type {Unknown = 0, Number, String, NumberOrString, Variable, LegacyVariable, Object, Empty};
Type ValidateFunction(const gd::FunctionCallNode& function); Type ValidateFunction(const gd::FunctionCallNode& function);
bool ValidateObjectVariableOrVariableOrProperty(const gd::IdentifierNode& identifier); bool ValidateObjectVariableOrVariableOrProperty(const gd::IdentifierNode& identifier);
void ReportAnyError(const ExpressionNode& node, bool isFatal = true) { void ReportAnyError(const ExpressionNode& node, bool isFatal = true) {
if (node.diagnostic && node.diagnostic->IsError()) { if (node.diagnostic) {
// Syntax errors are holden by the AST nodes. // Syntax errors are holden by the AST nodes.
// It's fine to give pointers on them as the AST live longer than errors // It's fine to give pointers on them as the AST live longer than errors
// handling. // handling.
@@ -367,10 +393,13 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
} }
} }
void RaiseError(const gd::String &type, void RaiseError(gd::ExpressionParserError::ErrorType type,
const gd::String &message, const ExpressionParserLocation &location, bool isFatal = true) { const gd::String &message,
const ExpressionParserLocation &location, bool isFatal = true,
const gd::String &actualValue = "",
const gd::String &objectName = "") {
auto diagnostic = gd::make_unique<ExpressionParserError>( auto diagnostic = gd::make_unique<ExpressionParserError>(
type, message, location); type, message, location, actualValue, objectName);
allErrors.push_back(diagnostic.get()); allErrors.push_back(diagnostic.get());
if (isFatal) { if (isFatal) {
fatalErrors.push_back(diagnostic.get()); fatalErrors.push_back(diagnostic.get());
@@ -381,14 +410,31 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
supplementalErrors.push_back(std::move(diagnostic)); supplementalErrors.push_back(std::move(diagnostic));
} }
void RaiseTypeError( void RaiseUnknownIdentifierError(const gd::String &message,
const gd::String &message, const ExpressionParserLocation &location, bool isFatal = true) { const ExpressionParserLocation &location) {
RaiseError("type_error", message, location, isFatal); RaiseError(gd::ExpressionParserError::ErrorType::UnknownIdentifier, message,
location);
} }
void RaiseOperatorError( void RaiseUndeclaredVariableError(const gd::String &message,
const gd::String &message, const ExpressionParserLocation &location) { const ExpressionParserLocation &location,
RaiseError("invalid_operator", message, location); const gd::String &variableName,
const gd::String &objectName = "") {
RaiseError(gd::ExpressionParserError::ErrorType::UndeclaredVariable,
message, location, true, variableName, objectName);
}
void RaiseTypeError(const gd::String &message,
const ExpressionParserLocation &location,
bool isFatal = true) {
RaiseError(gd::ExpressionParserError::ErrorType::MismatchedType, message,
location, isFatal);
}
void RaiseOperatorError(const gd::String &message,
const ExpressionParserLocation &location) {
RaiseError(gd::ExpressionParserError::ErrorType::InvalidOperator, message,
location);
} }
void ReadChildTypeFromVariable(gd::Variable::Type variableType) { void ReadChildTypeFromVariable(gd::Variable::Type variableType) {
@@ -412,20 +458,21 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
static const gd::String stringTypeString; static const gd::String stringTypeString;
static const gd::String numberOrStringTypeString; static const gd::String numberOrStringTypeString;
static const gd::String variableTypeString; static const gd::String variableTypeString;
static const gd::String legacyVariableTypeString;
static const gd::String objectTypeString; static const gd::String objectTypeString;
static const gd::String identifierTypeString; static const gd::String identifierTypeString;
static const gd::String emptyTypeString; static const gd::String emptyTypeString;
std::vector<ExpressionParserDiagnostic*> fatalErrors; std::vector<ExpressionParserError*> fatalErrors;
std::vector<ExpressionParserDiagnostic*> allErrors; std::vector<ExpressionParserError*> allErrors;
std::vector<std::unique_ptr<ExpressionParserDiagnostic>> supplementalErrors; std::vector<std::unique_ptr<ExpressionParserError>> supplementalErrors;
Type childType; ///< The type "discovered" down the tree and passed up. Type childType; ///< The type "discovered" down the tree and passed up.
Type parentType; ///< The type "required" by the top of the tree. Type parentType; ///< The type "required" by the top of the tree.
bool forbidsUsageOfBracketsBecauseParentIsObject; bool forbidsUsageOfBracketsBecauseParentIsObject;
const gd::String *currentParameterExtraInfo;
const gd::Platform &platform; const gd::Platform &platform;
const gd::ProjectScopedContainers &projectScopedContainers; const gd::ProjectScopedContainers &projectScopedContainers;
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_EXPRESSIONVALIDATOR_H

View File

@@ -0,0 +1,55 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
namespace gd {
/**
* \brief Find the variable name from a variable expression.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionVariableNameFinder : public ExpressionParser2NodeWorker {
public:
static const gd::String GetVariableName(gd::ExpressionNode& node) {
gd::ExpressionVariableNameFinder typeFinder;
node.Visit(typeFinder);
return typeFinder.variableName;
}
virtual ~ExpressionVariableNameFinder(){};
protected:
ExpressionVariableNameFinder()
: variableName("") {};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
void OnVisitOperatorNode(OperatorNode& node) override {}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
variableName = node.name;
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {}
void OnVisitIdentifierNode(IdentifierNode& node) override {
variableName = node.identifierName;
}
void OnVisitEmptyNode(EmptyNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {}
private:
gd::String variableName;
};
} // namespace gd

View File

@@ -1,374 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
class Expression;
class ObjectsContainer;
class Platform;
class ParameterMetadata;
class ExpressionMetadata;
} // namespace gd
namespace gd {
/**
* \brief Contains a variables container or a variable. Useful
* to refer to the parent of a variable (which can be a VariablesContainer
* or another Variable).
*/
struct VariableAndItsParent {
const gd::VariablesContainer* parentVariablesContainer;
const gd::Variable* parentVariable;
};
/**
* \brief Find the last parent (i.e: the variables container) of a node
* representing a variable.
*
* Useful for completions, to know which variables can be entered in a node.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionVariableParentFinder
: public ExpressionParser2NodeWorker {
public:
static VariableAndItsParent GetLastParentOfNode(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::ExpressionNode& node) {
gd::ExpressionVariableParentFinder typeFinder(platform,
projectScopedContainers);
node.Visit(typeFinder);
return typeFinder.variableAndItsParent;
}
virtual ~ExpressionVariableParentFinder(){};
protected:
ExpressionVariableParentFinder(
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_)
: platform(platform_),
projectScopedContainers(projectScopedContainers_),
variableNode(nullptr),
thisIsALegacyPrescopedVariable(false),
bailOutBecauseEmptyVariableName(false),
legacyPrescopedVariablesContainer(nullptr),
variableAndItsParent{} {};
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
void OnVisitOperatorNode(OperatorNode& node) override {}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (variableNode != nullptr) {
// This is not possible
return;
}
variableNode = &node;
// Check if the parent is a function call, in which we might be dealing
// with a legacy pre-scoped variable parameter:
if (node.parent) node.parent->Visit(*this);
if (thisIsALegacyPrescopedVariable) {
// The node represents a variable name, and the variables container
// containing it was identified in the FunctionCallNode.
childVariableNames.insert(childVariableNames.begin(), node.name);
if (legacyPrescopedVariablesContainer)
variableAndItsParent = WalkUntilLastParent(
*legacyPrescopedVariablesContainer, childVariableNames);
} else {
// Otherwise, the identifier is to be interpreted as usual:
// it can be an object (on which a variable is accessed),
// or a variable.
projectScopedContainers.MatchIdentifierWithName<void>(
node.name,
[&]() {
// This is an object.
const auto* variablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(node.name);
if (variablesContainer)
variableAndItsParent =
WalkUntilLastParent(*variablesContainer, childVariableNames);
},
[&]() {
// This is a variable.
if (projectScopedContainers.GetVariablesContainersList().Has(
node.name)) {
variableAndItsParent = WalkUntilLastParent(
projectScopedContainers.GetVariablesContainersList().Get(
node.name),
childVariableNames);
}
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of parameters.
},
[&]() {
// Ignore unrecognised identifiers here.
});
}
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.name.empty() && node.child) {
// A variable accessor should always have a name if it has a child (i.e:
// another accessor). While the parser may have generated an empty name,
// flag this so we avoid finding a wrong parent (and so, run the risk of
// giving wrong autocompletions).
bailOutBecauseEmptyVariableName = true;
}
childVariableNames.insert(childVariableNames.begin(), node.name);
if (node.parent) node.parent->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (variableNode != nullptr) {
// This is not possible
return;
}
// This node is not necessarily a variable node.
// It will be checked when visiting the FunctionCallNode, just after.
variableNode = &node;
// Check if the parent is a function call, in which we might be dealing
// with a legacy pre-scoped variable parameter:
if (node.parent) node.parent->Visit(*this);
if (thisIsALegacyPrescopedVariable) {
// The identifier represents a variable name, and the variables container
// containing it was identified in the FunctionCallNode.
if (!node.childIdentifierName.empty())
childVariableNames.insert(childVariableNames.begin(),
node.childIdentifierName);
childVariableNames.insert(childVariableNames.begin(),
node.identifierName);
if (legacyPrescopedVariablesContainer)
variableAndItsParent = WalkUntilLastParent(
*legacyPrescopedVariablesContainer, childVariableNames);
} else {
// Otherwise, the identifier is to be interpreted as usual:
// it can be an object (on which a variable is accessed),
// or a variable.
projectScopedContainers.MatchIdentifierWithName<void>(
node.identifierName,
[&]() {
// This is an object.
if (!node.childIdentifierName.empty())
childVariableNames.insert(childVariableNames.begin(),
node.childIdentifierName);
const auto* variablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(node.identifierName);
if (variablesContainer)
variableAndItsParent =
WalkUntilLastParent(*variablesContainer, childVariableNames);
},
[&]() {
// This is a variable.
if (!node.childIdentifierName.empty())
childVariableNames.insert(childVariableNames.begin(),
node.childIdentifierName);
if (projectScopedContainers.GetVariablesContainersList().Has(
node.identifierName)) {
variableAndItsParent = WalkUntilLastParent(
projectScopedContainers.GetVariablesContainersList().Get(
node.identifierName),
childVariableNames);
}
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore unrecognised identifiers here.
});
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
// Add a child with an empty name, which will be interpreted as
// "take the first child/item of the structure/array".
childVariableNames.insert(childVariableNames.begin(), "");
if (node.parent) node.parent->Visit(*this);
}
void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {
if (variableNode == nullptr) {
return;
}
int parameterIndex = -1;
for (int i = 0; i < functionCall.parameters.size(); i++) {
if (functionCall.parameters.at(i).get() == variableNode) {
parameterIndex = i;
break;
}
}
if (parameterIndex < 0) {
return;
}
const auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList();
const gd::ParameterMetadata* parameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform, objectsContainersList, functionCall, parameterIndex);
if (parameterMetadata == nullptr) return; // Unexpected
// Support for legacy pre-scoped variables:
if (parameterMetadata->GetValueTypeMetadata().IsLegacyPreScopedVariable()) {
if (parameterMetadata->GetType() == "objectvar") {
// Legacy convention where a "objectvar"
// parameter represents a variable of the object represented by the
// previous "object" parameter. The object on which the function is
// called is returned if no previous parameters are objects.
gd::String objectName = functionCall.objectName;
for (int previousIndex = parameterIndex - 1; previousIndex >= 0;
previousIndex--) {
const gd::ParameterMetadata* previousParameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform, objectsContainersList, functionCall, previousIndex);
if (previousParameterMetadata != nullptr &&
gd::ParameterMetadata::IsObject(
previousParameterMetadata->GetType())) {
auto previousParameterNode =
functionCall.parameters[previousIndex].get();
IdentifierNode* objectNode =
dynamic_cast<IdentifierNode*>(previousParameterNode);
objectName = objectNode->identifierName;
break;
}
}
legacyPrescopedVariablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(objectName);
thisIsALegacyPrescopedVariable = true;
} else if (parameterMetadata->GetType() == "scenevar") {
legacyPrescopedVariablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
thisIsALegacyPrescopedVariable = true;
} else if (parameterMetadata->GetType() == "globalvar") {
legacyPrescopedVariablesContainer =
projectScopedContainers.GetVariablesContainersList()
.GetTopMostVariablesContainer();
thisIsALegacyPrescopedVariable = true;
}
} else {
thisIsALegacyPrescopedVariable = false;
legacyPrescopedVariablesContainer = nullptr;
}
}
private:
VariableAndItsParent WalkUntilLastParent(
const gd::Variable& variable,
const std::vector<gd::String>& childVariableNames,
size_t startIndex = 0) {
if (bailOutBecauseEmptyVariableName)
return {}; // Do not even attempt to find the parent if we had an issue
// when visiting nodes.
const gd::Variable* currentVariable = &variable;
// Walk until size - 1 as we want the last parent.
for (size_t index = startIndex; index + 1 < childVariableNames.size();
++index) {
const gd::String& childName = childVariableNames[index];
if (childName.empty()) {
if (currentVariable->GetChildrenCount() == 0) {
// The array or structure is empty, we can't walk through it - there
// is no "parent".
return {};
}
if (currentVariable->GetType() == gd::Variable::Array) {
currentVariable = &currentVariable->GetAtIndex(0);
} else {
currentVariable =
currentVariable->GetAllChildren().begin()->second.get();
}
} else {
if (!currentVariable->HasChild(childName)) {
// Non existing child - there is no "parent".
return {};
}
currentVariable = &currentVariable->GetChild(childName);
}
}
// Return the last parent of the chain of variables (so not the last
// variable but the one before it).
return {.parentVariable = currentVariable};
}
VariableAndItsParent WalkUntilLastParent(
const gd::VariablesContainer& variablesContainer,
const std::vector<gd::String>& childVariableNames) {
if (bailOutBecauseEmptyVariableName)
return {}; // Do not even attempt to find the parent if we had an issue
// when visiting nodes.
if (childVariableNames.empty())
return {}; // There is no "parent" to the variables container itself.
const gd::String& firstChildName = *childVariableNames.begin();
const gd::Variable* variable = variablesContainer.Has(firstChildName)
? &variablesContainer.Get(firstChildName)
: nullptr;
if (childVariableNames.size() == 1 || !variable)
return {// Only one child: the parent is the variables container itself.
.parentVariablesContainer = &variablesContainer};
return WalkUntilLastParent(*variable, childVariableNames, 1);
}
gd::ExpressionNode* variableNode;
std::vector<gd::String> childVariableNames;
bool thisIsALegacyPrescopedVariable;
bool bailOutBecauseEmptyVariableName;
const gd::VariablesContainer* legacyPrescopedVariablesContainer;
VariableAndItsParent variableAndItsParent;
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
};
} // namespace gd

View File

@@ -0,0 +1,195 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ExpressionVariablePathFinder.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
namespace gd {
/**
* \brief Find the pre-scoped container of legacy variables or the object name
* from the function call node.
*/
class GD_CORE_API ExpressionLiteralFinder : public ExpressionParser2NodeWorker {
public:
virtual ~ExpressionLiteralFinder(){};
gd::String literalValue;
ExpressionLiteralFinder() : literalValue(""){};
protected:
void OnVisitSubExpressionNode(SubExpressionNode &node) override {}
void OnVisitOperatorNode(OperatorNode &node) override {}
void OnVisitUnaryOperatorNode(UnaryOperatorNode &node) override {}
void OnVisitNumberNode(NumberNode &node) override {
literalValue = node.number;
}
void OnVisitTextNode(TextNode &node) override { literalValue = node.text; }
void OnVisitVariableNode(VariableNode &node) override {}
void OnVisitVariableAccessorNode(VariableAccessorNode &node) override {}
void OnVisitIdentifierNode(IdentifierNode &node) override {}
void OnVisitEmptyNode(EmptyNode &node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode &node) override {}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode &node) override {}
void OnVisitFunctionCallNode(FunctionCallNode &functionCall) override {}
};
void ExpressionVariablePathFinder::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode &node) {
// Try to find a literal accessor or add a child with an empty name, which
// will be interpreted as "take the first child/item of the structure/array".
gd::ExpressionLiteralFinder expressionLiteralFinder;
if (node.expression) {
node.expression->Visit(expressionLiteralFinder);
}
childVariableNames.push_back(expressionLiteralFinder.literalValue);
if (node.child && &node != lastNodeToCheck) {
node.child->Visit(*this);
}
}
/**
* \brief Find the pre-scoped container of legacy variables or the object name
* from the function call node.
*/
class GD_CORE_API ExpressionVariableContextFinder
: public ExpressionParser2NodeWorker {
public:
virtual ~ExpressionVariableContextFinder(){};
gd::String objectName;
gd::String parameterType;
gd::ExpressionNode* variableNode;
ExpressionVariableContextFinder(
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_)
: platform(platform_),
projectScopedContainers(projectScopedContainers_),
variableNode(nullptr),
objectName(""),
parameterType("") {};
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
void OnVisitOperatorNode(OperatorNode& node) override {}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (variableNode) {
// This is not possible
return;
}
variableNode = &node;
// Check if the parent is a function call, in which we might be dealing
// with a legacy pre-scoped variable parameter:
if (node.parent) node.parent->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.parent) node.parent->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
if (variableNode) {
// This is not possible
return;
}
// This node is not necessarily a variable node.
// It will be checked when visiting the FunctionCallNode, just after.
variableNode = &node;
// Check if the parent is a function call, in which we might be dealing
// with a legacy pre-scoped variable parameter:
if (node.parent) node.parent->Visit(*this);
}
void OnVisitEmptyNode(EmptyNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
if (node.parent) node.parent->Visit(*this);
}
void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {
int parameterIndex = -1;
for (int i = 0; i < functionCall.parameters.size(); i++) {
if (functionCall.parameters.at(i).get() == variableNode) {
parameterIndex = i;
break;
}
}
if (parameterIndex < 0) {
return;
}
const auto& objectsContainersList =
projectScopedContainers.GetObjectsContainersList();
const gd::ParameterMetadata* parameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform, objectsContainersList, functionCall, parameterIndex);
if (parameterMetadata == nullptr) return; // Unexpected
if (parameterMetadata->GetType() == "objectvar") {
// Legacy convention where a "objectvar"
// parameter represents a variable of the object represented by the
// previous "object" parameter. The object on which the function is
// called is returned if no previous parameters are objects.
objectName = functionCall.objectName;
for (int previousIndex = parameterIndex - 1; previousIndex >= 0;
previousIndex--) {
const gd::ParameterMetadata* previousParameterMetadata =
MetadataProvider::GetFunctionCallParameterMetadata(
platform, objectsContainersList, functionCall, previousIndex);
if (previousParameterMetadata != nullptr &&
gd::ParameterMetadata::IsObject(
previousParameterMetadata->GetType())) {
auto previousParameterNode =
functionCall.parameters[previousIndex].get();
IdentifierNode* objectNode =
dynamic_cast<IdentifierNode*>(previousParameterNode);
objectName = objectNode->identifierName;
break;
}
}
parameterType = parameterMetadata->GetType();
}
}
private:
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
};
VariableAndItsParent ExpressionVariablePathFinder::GetLastParentOfNode(
const gd::Platform &platform,
const gd::ProjectScopedContainers &projectScopedContainers,
gd::ExpressionNode &node) {
gd::ExpressionVariableContextFinder contextFinder(platform,
projectScopedContainers);
node.Visit(contextFinder);
if (!contextFinder.variableNode) {
return {};
}
gd::ExpressionVariablePathFinder typeFinder(platform, projectScopedContainers,
contextFinder.parameterType,
contextFinder.objectName, &node);
contextFinder.variableNode->Visit(typeFinder);
if (typeFinder.variableName.empty() || !typeFinder.variablesContainer) {
return {};
}
return typeFinder.WalkUntilLastParent(*typeFinder.variablesContainer,
typeFinder.childVariableNames);
}
} // namespace gd

View File

@@ -0,0 +1,357 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include <memory>
#include <vector>
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Project/VariablesContainer.h"
namespace gd {
class Platform;
} // namespace gd
namespace gd {
/**
* \brief Contains a variables container or a variable. Useful
* to refer to the parent of a variable (which can be a VariablesContainer
* or another Variable).
*/
struct VariableAndItsParent {
const gd::VariablesContainer* parentVariablesContainer;
const gd::Variable* parentVariable;
};
/**
* \brief Find a variable path from an expression node.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionVariablePathFinder
: public ExpressionParser2NodeWorker {
public:
static VariableAndItsParent GetLastParentOfNode(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::ExpressionNode& node);
static const gd::Variable::Type GetVariableType(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::ExpressionNode& node, const gd::String& objectName) {
// The context is not checked because this is called on variable parameters.
gd::String parameterType = objectName.empty() ? "variable" : "objectvar";
gd::String objName = objectName;
gd::ExpressionVariablePathFinder typeFinder(
platform, projectScopedContainers, parameterType, objName);
node.Visit(typeFinder);
if (typeFinder.variableName.empty() || !typeFinder.variablesContainer) {
return gd::Variable::Unknown;
}
auto *variable = typeFinder.WalkUntilLastChild(
typeFinder.variablesContainer->Get(typeFinder.variableName),
typeFinder.childVariableNames);
return variable ? variable->GetType() : gd::Variable::Unknown;
}
static const gd::Variable::Type GetArrayVariableType(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::ExpressionNode& node, const gd::String& objectName) {
// The context is not checked because this is called on variable parameters.
gd::String parameterType = objectName.empty() ? "variable" : "objectvar";
gd::String objName = objectName;
gd::ExpressionVariablePathFinder typeFinder(
platform, projectScopedContainers, parameterType, objName);
node.Visit(typeFinder);
if (typeFinder.variableName.empty() || !typeFinder.variablesContainer) {
return gd::Variable::Unknown;
}
auto *variable = typeFinder.WalkUntilLastChild(
typeFinder.variablesContainer->Get(typeFinder.variableName),
typeFinder.childVariableNames);
if (variable && variable->GetType() != gd::Variable::Array) {
return gd::Variable::Unknown;
}
return variable && variable->GetChildrenCount() > 0
? variable->GetAtIndex(0).GetType()
: gd::Variable::Unknown;
}
virtual ~ExpressionVariablePathFinder(){};
protected:
ExpressionVariablePathFinder(
const gd::Platform& platform_,
const gd::ProjectScopedContainers& projectScopedContainers_,
const gd::String& parameterType_,
gd::String& objectName_,
const gd::ExpressionNode* lastNodeToCheck_ = nullptr)
: platform(platform_),
projectScopedContainers(projectScopedContainers_),
parameterType(parameterType_),
objectName(objectName_),
lastNodeToCheck(lastNodeToCheck_),
variablesContainer(nullptr),
variableName(""),
bailOutBecauseEmptyVariableName(false) {};
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override;
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
void OnVisitOperatorNode(OperatorNode& node) override {}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
FindVariableFor(node.name);
if (node.child && &node != lastNodeToCheck) node.child->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.name.empty() && node.child) {
// A variable accessor should always have a name if it has a child (i.e:
// another accessor). While the parser may have generated an empty name,
// flag this so we avoid finding a wrong parent (and so, run the risk of
// giving wrong autocompletions).
bailOutBecauseEmptyVariableName = true;
}
if (variableName.empty()) {
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
if (objectsContainersList.HasObjectOrGroupWithVariableNamed(objectName,
node.name) !=
gd::ObjectsContainersList::VariableExistence::DoesNotExist) {
variableName = node.name;
variablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(objectName);
}
} else {
childVariableNames.push_back(node.name);
}
if (node.child && &node != lastNodeToCheck) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {
FindVariableFor(node.identifierName, node.identifierNameDotLocation.IsValid() ? &node.childIdentifierName : nullptr);
}
void OnVisitEmptyNode(EmptyNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {}
void FindVariableFor(const gd::String& identifier, gd::String* childIdentifier = nullptr) {
if (!objectName.empty()) {
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
if (objectsContainersList.HasObjectOrGroupWithVariableNamed(objectName,
identifier) !=
gd::ObjectsContainersList::VariableExistence::DoesNotExist) {
variableName = identifier;
variablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(objectName);
if (childIdentifier) {
childVariableNames.push_back(*childIdentifier);
}
}
}
else if (parameterType == "scenevar") {
// The node represents a variable name, and the variables container
// containing it was identified in the FunctionCallNode.
variablesContainer = projectScopedContainers.GetVariablesContainersList()
.GetBottomMostVariablesContainer();
variableName = identifier;
if (childIdentifier) {
childVariableNames.push_back(*childIdentifier);
}
}
else if (parameterType == "globalvar") {
// The node represents a variable name, and the variables container
// containing it was identified in the FunctionCallNode.
variablesContainer = projectScopedContainers.GetVariablesContainersList()
.GetTopMostVariablesContainer();
variableName = identifier;
if (childIdentifier) {
childVariableNames.push_back(*childIdentifier);
}
} else {
// Otherwise, the identifier is to be interpreted as usual:
// it can be an object (on which a variable is accessed),
// or a variable.
projectScopedContainers.MatchIdentifierWithName<void>(
identifier,
[&]() {
objectName = identifier;
if (childIdentifier) {
if (parameterType == "variable") {
// An object is overlapping the variable.
// Even in "variable" parameters, this is not allowed to be
// consistent with expressions.
} else {
// It's an object variable expression.
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
if (objectsContainersList.HasObjectOrGroupWithVariableNamed(objectName,
*childIdentifier) !=
gd::ObjectsContainersList::VariableExistence::DoesNotExist) {
variableName = *childIdentifier;
variablesContainer =
projectScopedContainers.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(objectName);
}
}
}
},
[&]() {
// This is a variable.
if (projectScopedContainers.GetVariablesContainersList().Has(identifier)) {
variablesContainer =
&(projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(identifier));
variableName = identifier;
if (childIdentifier) {
childVariableNames.push_back(*childIdentifier);
}
}
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of parameters.
},
[&]() {
// Ignore unrecognised identifiers here.
});
}
}
private:
const gd::Variable* WalkUntilLastChild(
const gd::Variable& variable,
const std::vector<gd::String>& childVariableNames,
size_t startIndex = 0) {
if (bailOutBecauseEmptyVariableName)
return nullptr; // Do not even attempt to find the parent if we had an issue
// when visiting nodes.
const gd::Variable* currentVariable = &variable;
for (size_t index = startIndex; index < childVariableNames.size();
++index) {
const gd::String& childName = childVariableNames[index];
if (childName.empty()) {
if (currentVariable->GetChildrenCount() == 0) {
// The array or structure is empty, we can't walk through it - there
// is no "parent".
return nullptr;
}
if (currentVariable->GetType() == gd::Variable::Array) {
currentVariable = &currentVariable->GetAtIndex(0);
} else {
currentVariable =
currentVariable->GetAllChildren().begin()->second.get();
}
} else {
if (!currentVariable->HasChild(childName)) {
// Non existing child - there is no "parent".
return nullptr;
}
currentVariable = &currentVariable->GetChild(childName);
}
}
// Return the last parent of the chain of variables (so not the last
// variable but the one before it).
return currentVariable;
}
VariableAndItsParent WalkUntilLastParent(
const gd::Variable& variable,
const std::vector<gd::String>& childVariableNames,
size_t startIndex = 0) {
if (bailOutBecauseEmptyVariableName)
return {}; // Do not even attempt to find the parent if we had an issue
// when visiting nodes.
const gd::Variable* currentVariable = &variable;
// Walk until size - 1 as we want the last parent.
for (size_t index = startIndex; index + 1 < childVariableNames.size();
++index) {
const gd::String& childName = childVariableNames[index];
if (childName.empty()) {
if (currentVariable->GetChildrenCount() == 0) {
// The array or structure is empty, we can't walk through it - there
// is no "parent".
return {};
}
if (currentVariable->GetType() == gd::Variable::Array) {
currentVariable = &currentVariable->GetAtIndex(0);
} else {
currentVariable =
currentVariable->GetAllChildren().begin()->second.get();
}
} else {
if (!currentVariable->HasChild(childName)) {
// Non existing child - there is no "parent".
return {};
}
currentVariable = &currentVariable->GetChild(childName);
}
}
// Return the last parent of the chain of variables (so not the last
// variable but the one before it).
return {.parentVariable = currentVariable};
}
VariableAndItsParent WalkUntilLastParent(
const gd::VariablesContainer& variablesContainer,
const std::vector<gd::String>& childVariableNames) {
if (bailOutBecauseEmptyVariableName)
return {}; // Do not even attempt to find the parent if we had an issue
// when visiting nodes.
if (variableName.empty())
return {}; // There is no "parent" to the variables container itself.
const gd::Variable* variable = variablesContainer.Has(variableName)
? &variablesContainer.Get(variableName)
: nullptr;
if (childVariableNames.empty() || !variable)
return {// No child: the parent is the variables container itself.
.parentVariablesContainer = &variablesContainer};
return WalkUntilLastParent(*variable, childVariableNames, 0);
}
const gd::Platform& platform;
const gd::ProjectScopedContainers& projectScopedContainers;
const gd::String& parameterType;
gd::String& objectName;
const gd::ExpressionNode* lastNodeToCheck;
const gd::VariablesContainer* variablesContainer;
gd::String variableName;
std::vector<gd::String> childVariableNames;
bool bailOutBecauseEmptyVariableName;
};
} // namespace gd

View File

@@ -22,12 +22,7 @@ void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
const gd::Project& project, const gd::Project& project,
const gd::EventsFunctionsContainer functionContainer, const gd::EventsFunctionsContainer functionContainer,
const gd::EventsFunction& eventsFunction, const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer) { gd::ObjectsContainer& outputObjectsContainer) {
// Functions don't have access to objects from the "outer" scope.
outputGlobalObjectsContainer.GetObjects().clear();
outputGlobalObjectsContainer.GetObjectGroups().Clear();
// Functions scope for objects is defined according // Functions scope for objects is defined according
// to parameters // to parameters
outputObjectsContainer.GetObjects().clear(); outputObjectsContainer.GetObjects().clear();
@@ -45,13 +40,11 @@ void EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
const gd::Project& project, const gd::Project& project,
const gd::EventsBasedBehavior& eventsBasedBehavior, const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction& eventsFunction, const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer) { gd::ObjectsContainer& outputObjectsContainer) {
// The context is build the same way as free function... // The context is build the same way as free function...
FreeEventsFunctionToObjectsContainer(project, FreeEventsFunctionToObjectsContainer(project,
eventsBasedBehavior.GetEventsFunctions(), eventsBasedBehavior.GetEventsFunctions(),
eventsFunction, eventsFunction,
outputGlobalObjectsContainer,
outputObjectsContainer); outputObjectsContainer);
// ...and has an "Object" by convention... // ...and has an "Object" by convention...
@@ -83,13 +76,11 @@ void EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
const gd::Project& project, const gd::Project& project,
const gd::EventsBasedObject& eventsBasedObject, const gd::EventsBasedObject& eventsBasedObject,
const gd::EventsFunction& eventsFunction, const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer) { gd::ObjectsContainer& outputObjectsContainer) {
// The context is build the same way as free function... // The context is build the same way as free function...
FreeEventsFunctionToObjectsContainer(project, FreeEventsFunctionToObjectsContainer(project,
eventsBasedObject.GetEventsFunctions(), eventsBasedObject.GetEventsFunctions(),
eventsFunction, eventsFunction,
outputGlobalObjectsContainer,
outputObjectsContainer); outputObjectsContainer);
// TODO EBO Use a constant instead a hard coded value "Object". // TODO EBO Use a constant instead a hard coded value "Object".

View File

@@ -3,11 +3,11 @@
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#if defined(GD_IDE_ONLY) #pragma once
#ifndef EventsFunctionTools_H
#define EventsFunctionTools_H
#include <vector> #include <vector>
#include "GDCore/String.h" #include "GDCore/String.h"
namespace gd { namespace gd {
class Project; class Project;
class EventsFunctionsContainer; class EventsFunctionsContainer;
@@ -37,8 +37,8 @@ class GD_CORE_API EventsFunctionTools {
const gd::Project& project, const gd::Project& project,
const gd::EventsFunctionsContainer functionContainer, const gd::EventsFunctionsContainer functionContainer,
const gd::EventsFunction& eventsFunction, const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer); gd::ObjectsContainer& outputObjectsContainer);
/** /**
* \brief Given a behavior events function, initialize the given objects container * \brief Given a behavior events function, initialize the given objects container
* with objects described in the events function parameters, in * with objects described in the events function parameters, in
@@ -52,8 +52,8 @@ class GD_CORE_API EventsFunctionTools {
const gd::Project& project, const gd::Project& project,
const gd::EventsBasedBehavior& eventsBasedBehavior, const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction& eventsFunction, const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer); gd::ObjectsContainer& outputObjectsContainer);
/** /**
* \brief Given a parent-object events function, initialize the given objects container * \brief Given a parent-object events function, initialize the given objects container
* with objects described in the events function parameters, in * with objects described in the events function parameters, in
@@ -67,10 +67,6 @@ class GD_CORE_API EventsFunctionTools {
const gd::Project& project, const gd::Project& project,
const gd::EventsBasedObject& eventsBasedObject, const gd::EventsBasedObject& eventsBasedObject,
const gd::EventsFunction& eventsFunction, const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer); gd::ObjectsContainer& outputObjectsContainer);
}; };
} // namespace gd } // namespace gd
#endif // EventsFunctionTools_H
#endif

View File

@@ -148,12 +148,6 @@ void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::ArbitraryEventsWorker &worker) { gd::ArbitraryEventsWorker &worker) {
// Add (free) events functions // Add (free) events functions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) { for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
project, eventsFunctionsExtension, *eventsFunction,
globalObjectsAndGroups, objectsAndGroups);
worker.Launch(eventsFunction->GetEvents()); worker.Launch(eventsFunction->GetEvents());
} }
@@ -176,14 +170,11 @@ void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::ArbitraryEventsWorkerWithContext &worker) { gd::ArbitraryEventsWorkerWithContext &worker) {
// Add (free) events functions // Add (free) events functions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) { for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups; gd::ObjectsContainer parameterObjectsContainer;
gd::ObjectsContainer objectsAndGroups; auto projectScopedContainers = gd::ProjectScopedContainers::
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer( MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsFunctionsExtension, *eventsFunction, project, eventsFunctionsExtension, *eventsFunction,
globalObjectsAndGroups, objectsAndGroups); parameterObjectsContainer);
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
projectScopedContainers.AddParameters(eventsFunction->GetParametersForEvents(eventsFunctionsExtension));
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers); worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
} }
@@ -192,13 +183,13 @@ void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
for (auto &&eventsBasedBehavior : for (auto &&eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors() eventsFunctionsExtension.GetEventsBasedBehaviors()
.GetInternalVector()) { .GetInternalVector()) {
ExposeEventsBasedBehaviorEvents(project, *eventsBasedBehavior, worker); ExposeEventsBasedBehaviorEvents(project, eventsFunctionsExtension, *eventsBasedBehavior, worker);
} }
// Add (object) events functions // Add (object) events functions
for (auto &&eventsBasedObject : for (auto &&eventsBasedObject :
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) { eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
ExposeEventsBasedObjectEvents(project, *eventsBasedObject, worker); ExposeEventsBasedObjectEvents(project, eventsFunctionsExtension, *eventsBasedObject, worker);
} }
} }
@@ -212,20 +203,17 @@ void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
} }
void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents( void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
gd::Project &project, const gd::EventsBasedBehavior &eventsBasedBehavior, gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::ArbitraryEventsWorkerWithContext &worker) { gd::ArbitraryEventsWorkerWithContext &worker) {
auto &behaviorEventsFunctions = eventsBasedBehavior.GetEventsFunctions(); auto &behaviorEventsFunctions = eventsBasedBehavior.GetEventsFunctions();
for (auto &&eventsFunction : behaviorEventsFunctions.GetInternalVector()) { for (auto &&eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups; gd::ObjectsContainer parameterObjectsContainers;
gd::EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer( auto projectScopedContainers = gd::ProjectScopedContainers::
project, eventsBasedBehavior, *eventsFunction, globalObjectsAndGroups, MakeNewProjectScopedContainersForBehaviorEventsFunction(
objectsAndGroups); project, eventsFunctionsExtension, eventsBasedBehavior,
auto projectScopedContainers = *eventsFunction, parameterObjectsContainers);
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
projectScopedContainers.AddPropertiesContainer(eventsBasedBehavior.GetSharedPropertyDescriptors());
projectScopedContainers.AddPropertiesContainer(eventsBasedBehavior.GetPropertyDescriptors());
projectScopedContainers.AddParameters(eventsFunction->GetParametersForEvents(eventsBasedBehavior.GetEventsFunctions()));
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers); worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
} }
@@ -236,30 +224,23 @@ void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
gd::ArbitraryEventsWorker &worker) { gd::ArbitraryEventsWorker &worker) {
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions(); auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) { for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
gd::EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
project, eventsBasedObject, *eventsFunction, globalObjectsAndGroups,
objectsAndGroups);
worker.Launch(eventsFunction->GetEvents()); worker.Launch(eventsFunction->GetEvents());
} }
} }
void ProjectBrowserHelper::ExposeEventsBasedObjectEvents( void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
gd::Project &project, const gd::EventsBasedObject &eventsBasedObject, gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
gd::ArbitraryEventsWorkerWithContext &worker) { gd::ArbitraryEventsWorkerWithContext &worker) {
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions(); auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) { for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups; gd::ObjectsContainer parameterObjectsContainers;
gd::EventsFunctionTools::ObjectEventsFunctionToObjectsContainer( auto projectScopedContainers = gd::ProjectScopedContainers::
project, eventsBasedObject, *eventsFunction, globalObjectsAndGroups, MakeNewProjectScopedContainersForObjectEventsFunction(
objectsAndGroups); project, eventsFunctionsExtension, eventsBasedObject,
auto projectScopedContainers = *eventsFunction, parameterObjectsContainers);
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
projectScopedContainers.AddPropertiesContainer(eventsBasedObject.GetPropertyDescriptors());
projectScopedContainers.AddParameters(eventsFunction->GetParametersForEvents(eventsBasedObject.GetEventsFunctions()));
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers); worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
} }

View File

@@ -122,7 +122,9 @@ public:
* event-based behavior. * event-based behavior.
*/ */
static void ExposeEventsBasedBehaviorEvents( static void ExposeEventsBasedBehaviorEvents(
gd::Project &project, const gd::EventsBasedBehavior &eventsBasedBehavior, gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::ArbitraryEventsWorkerWithContext &worker); gd::ArbitraryEventsWorkerWithContext &worker);
/** /**
@@ -144,10 +146,11 @@ public:
* This should be the preferred way to traverse all the events of an * This should be the preferred way to traverse all the events of an
* event-based object. * event-based object.
*/ */
static void static void ExposeEventsBasedObjectEvents(
ExposeEventsBasedObjectEvents(gd::Project &project, gd::Project &project,
const gd::EventsBasedObject &eventsBasedObject, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorkerWithContext &worker); const gd::EventsBasedObject &eventsBasedObject,
gd::ArbitraryEventsWorkerWithContext &worker);
/** /**
* \brief Call the specified worker on all ObjectContainers of the project * \brief Call the specified worker on all ObjectContainers of the project

View File

@@ -30,14 +30,26 @@ void GD_CORE_API ProjectStripper::StripProjectForExport(gd::Project &project) {
project.GetLayout(i).GetEvents().Clear(); project.GetLayout(i).GetEvents().Clear();
} }
// Keep the EventsBasedObject object list because it's useful for the Runtime // Keep:
// - the EventsBasedObject object list because it's useful for the Runtime
// to create the child-object. // to create the child-object.
// - the globalVariables and sceneVariables
for (unsigned int extensionIndex = 0; for (unsigned int extensionIndex = 0;
extensionIndex < project.GetEventsFunctionsExtensionsCount(); extensionIndex < project.GetEventsFunctionsExtensionsCount();
++extensionIndex) { ++extensionIndex) {
auto &extension = project.GetEventsFunctionsExtension(extensionIndex); auto &extension = project.GetEventsFunctionsExtension(extensionIndex);
extension.SetFullName("");
extension.SetShortDescription("");
extension.SetDescription("");
extension.SetHelpPath("");
extension.SetIconUrl("");
extension.SetPreviewIconUrl("");
extension.SetOrigin("", "");
extension.SetVersion("");
auto &eventsBasedObjects = extension.GetEventsBasedObjects(); auto &eventsBasedObjects = extension.GetEventsBasedObjects();
if (eventsBasedObjects.size() == 0) { if (eventsBasedObjects.size() == 0 &&
extension.GetGlobalVariables().Count() == 0 &&
extension.GetSceneVariables().Count() == 0) {
project.RemoveEventsFunctionsExtension(extension.GetName()); project.RemoveEventsFunctionsExtension(extension.GetName());
extensionIndex--; extensionIndex--;
continue; continue;
@@ -51,6 +63,7 @@ void GD_CORE_API ProjectStripper::StripProjectForExport(gd::Project &project) {
eventsBasedObject.GetPropertyDescriptors().GetInternalVector().clear(); eventsBasedObject.GetPropertyDescriptors().GetInternalVector().clear();
} }
extension.GetEventsBasedBehaviors().Clear(); extension.GetEventsBasedBehaviors().Clear();
extension.ClearEventsFunctions();
} }
} }

View File

@@ -0,0 +1,241 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "VariableInstructionSwitcher.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/IDE/Events/ExpressionVariablePathFinder.h"
#include "GDCore/Project/Variable.h"
namespace gd {
const gd::String VariableInstructionSwitcher::variableGetterIdentifier =
"NumberVariable";
const gd::String VariableInstructionSwitcher::variableSetterIdentifier =
"SetNumberVariable";
const gd::String VariableInstructionSwitcher::variablePushIdentifier =
"PushNumber";
const gd::String VariableInstructionSwitcher::objectVariableGetterIdentifier =
"NumberObjectVariable";
const gd::String VariableInstructionSwitcher::objectVariableSetterIdentifier =
"SetNumberObjectVariable";
const gd::String VariableInstructionSwitcher::objectVariablePushIdentifier =
"PushNumberToObjectVariable";
const gd::String VariableInstructionSwitcher::unknownInstructionIdentifier = "";
bool VariableInstructionSwitcher::IsSwitchableVariableInstruction(
const gd::String &instructionType) {
return instructionType == "NumberVariable" ||
instructionType == "StringVariable" ||
instructionType == "BooleanVariable" ||
instructionType == "SetNumberVariable" ||
instructionType == "SetStringVariable" ||
instructionType == "SetBooleanVariable" ||
instructionType == "PushNumber" || instructionType == "PushString" ||
instructionType == "PushBoolean" ||
IsSwitchableObjectVariableInstruction(instructionType);
}
bool VariableInstructionSwitcher::IsSwitchableObjectVariableInstruction(
const gd::String &instructionType) {
return instructionType == "NumberObjectVariable" ||
instructionType == "StringObjectVariable" ||
instructionType == "BooleanObjectVariable" ||
instructionType == "SetNumberObjectVariable" ||
instructionType == "SetStringObjectVariable" ||
instructionType == "SetBooleanObjectVariable" ||
instructionType == "PushNumberToObjectVariable" ||
instructionType == "PushStringToObjectVariable" ||
instructionType == "PushBooleanToObjectVariable";
}
const gd::String &
VariableInstructionSwitcher::GetSwitchableVariableInstructionIdentifier(
const gd::String &instructionType) {
return instructionType == "NumberVariable" ||
instructionType == "StringVariable" ||
instructionType == "BooleanVariable"
? VariableInstructionSwitcher::variableGetterIdentifier
:
instructionType == "SetNumberVariable" ||
instructionType == "SetStringVariable" ||
instructionType == "SetBooleanVariable"
? VariableInstructionSwitcher::variableSetterIdentifier
:
instructionType == "PushNumber" || instructionType == "PushString" ||
instructionType == "PushBoolean"
? VariableInstructionSwitcher::variablePushIdentifier
:
instructionType == "NumberObjectVariable" ||
instructionType == "StringObjectVariable" ||
instructionType == "BooleanObjectVariable"
? VariableInstructionSwitcher::objectVariableGetterIdentifier
:
instructionType == "SetNumberObjectVariable" ||
instructionType == "SetStringObjectVariable" ||
instructionType == "SetBooleanObjectVariable"
? VariableInstructionSwitcher::objectVariableSetterIdentifier
:
instructionType == "PushNumberToObjectVariable" ||
instructionType == "PushStringToObjectVariable" ||
instructionType == "PushBooleanToObjectVariable"
? VariableInstructionSwitcher::objectVariablePushIdentifier
:
VariableInstructionSwitcher::unknownInstructionIdentifier;
}
const gd::Variable::Type
VariableInstructionSwitcher::GetSwitchableInstructionVariableType(
const gd::String &instructionType) {
return instructionType == "NumberVariable" ||
instructionType == "SetNumberVariable" ||
instructionType == "PushNumber" ||
instructionType == "NumberObjectVariable" ||
instructionType == "SetNumberObjectVariable" ||
instructionType == "PushNumberToObjectVariable"
? gd::Variable::Number
:
instructionType == "StringVariable" ||
instructionType == "SetStringVariable" ||
instructionType == "PushString" ||
instructionType == "StringObjectVariable" ||
instructionType == "SetStringObjectVariable" ||
instructionType == "PushStringToObjectVariable"
? gd::Variable::String
:
instructionType == "BooleanVariable" ||
instructionType == "SetBooleanVariable" ||
instructionType == "PushBoolean" ||
instructionType == "BooleanObjectVariable" ||
instructionType == "SetBooleanObjectVariable" ||
instructionType == "PushBooleanToObjectVariable"
? gd::Variable::Boolean
:
gd::Variable::Unknown;
}
void VariableInstructionSwitcher::SwitchVariableInstructionType(
gd::Instruction &instruction, const gd::Variable::Type variableType) {
if (instruction.GetType() == "NumberVariable" ||
instruction.GetType() == "StringVariable" ||
instruction.GetType() == "BooleanVariable") {
if (variableType == gd::Variable::Type::Number) {
instruction.SetType("NumberVariable");
} else if (variableType == gd::Variable::Type::String) {
instruction.SetType("StringVariable");
} else if (variableType == gd::Variable::Type::Boolean) {
instruction.SetType("BooleanVariable");
}
} else if (instruction.GetType() == "SetNumberVariable" ||
instruction.GetType() == "SetStringVariable" ||
instruction.GetType() == "SetBooleanVariable") {
if (variableType == gd::Variable::Type::Number) {
instruction.SetType("SetNumberVariable");
} else if (variableType == gd::Variable::Type::String) {
instruction.SetType("SetStringVariable");
} else if (variableType == gd::Variable::Type::Boolean) {
instruction.SetType("SetBooleanVariable");
}
} else if (instruction.GetType() == "PushNumber" ||
instruction.GetType() == "PushString" ||
instruction.GetType() == "PushBoolean") {
if (variableType == gd::Variable::Type::Number) {
instruction.SetType("PushNumber");
} else if (variableType == gd::Variable::Type::String) {
instruction.SetType("PushString");
} else if (variableType == gd::Variable::Type::Boolean) {
instruction.SetType("PushBoolean");
}
} else if (instruction.GetType() == "NumberObjectVariable" ||
instruction.GetType() == "StringObjectVariable" ||
instruction.GetType() == "BooleanObjectVariable") {
if (variableType == gd::Variable::Type::Number) {
instruction.SetType("NumberObjectVariable");
} else if (variableType == gd::Variable::Type::String) {
instruction.SetType("StringObjectVariable");
} else if (variableType == gd::Variable::Type::Boolean) {
instruction.SetType("BooleanObjectVariable");
}
} else if (instruction.GetType() == "SetNumberObjectVariable" ||
instruction.GetType() == "SetStringObjectVariable" ||
instruction.GetType() == "SetBooleanObjectVariable") {
if (variableType == gd::Variable::Type::Number) {
instruction.SetType("SetNumberObjectVariable");
} else if (variableType == gd::Variable::Type::String) {
instruction.SetType("SetStringObjectVariable");
} else if (variableType == gd::Variable::Type::Boolean) {
instruction.SetType("SetBooleanObjectVariable");
}
} else if (instruction.GetType() == "PushNumberToObjectVariable" ||
instruction.GetType() == "PushStringToObjectVariable" ||
instruction.GetType() == "PushBooleanToObjectVariable") {
if (variableType == gd::Variable::Type::Number) {
instruction.SetType("PushNumberToObjectVariable");
} else if (variableType == gd::Variable::Type::String) {
instruction.SetType("PushStringToObjectVariable");
} else if (variableType == gd::Variable::Type::Boolean) {
instruction.SetType("PushBooleanToObjectVariable");
}
}
}
const gd::Variable::Type
VariableInstructionSwitcher::GetVariableTypeFromParameters(
const gd::Platform &platform,
const gd::ProjectScopedContainers &projectScopedContainers,
const gd::Instruction &instruction) {
if (instruction.GetParametersCount() < 2 ||
!gd::VariableInstructionSwitcher::IsSwitchableVariableInstruction(
instruction.GetType())) {
return gd::Variable::Type::Unknown;
}
const bool isObjectVariable =
gd::VariableInstructionSwitcher::IsSwitchableObjectVariableInstruction(
instruction.GetType());
const gd::String &objectName =
isObjectVariable ? instruction.GetParameter(0).GetPlainString() : "";
const std::size_t variableParameterIndex = isObjectVariable ? 1 : 0;
auto &variableExpressionNode =
*instruction.GetParameter(variableParameterIndex).GetRootNode();
auto variableType = gd::ExpressionVariablePathFinder::GetVariableType(
platform, projectScopedContainers, variableExpressionNode, objectName);
return variableType == gd::Variable::Type::Array
? // "Push" actions need the child type to be able to switch.
gd::ExpressionVariablePathFinder::GetArrayVariableType(
platform, projectScopedContainers, variableExpressionNode,
objectName)
: variableType;
}
void VariableInstructionSwitcher::SwitchBetweenUnifiedInstructionIfNeeded(
const gd::Platform &platform,
const gd::ProjectScopedContainers &projectScopedContainers,
gd::Instruction &instruction) {
const auto variableType =
gd::VariableInstructionSwitcher::GetVariableTypeFromParameters(
platform, projectScopedContainers, instruction);
if (variableType != gd::Variable::Type::Unknown) {
gd::VariableInstructionSwitcher::SwitchVariableInstructionType(
instruction, variableType);
}
}
} // namespace gd

View File

@@ -0,0 +1,91 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/Project/Variable.h"
#include "GDCore/String.h"
namespace gd {
class Instruction;
class Platform;
class ProjectScopedContainers;
} // namespace gd
namespace gd {
/**
* Events set and check variables with sets of 3 instructions for:
* - number
* - string
* - boolean
*
* Users only see 1 instruction. The editor automatically switches between the 3
* instructions according to the variable type.
*/
class GD_CORE_API VariableInstructionSwitcher {
public:
/**
* \brief Return true if the instruction is a variable getter or setter
* (including object variable instructions).
*/
static bool
IsSwitchableVariableInstruction(const gd::String &instructionType);
/**
* \brief Return true if the instruction is an object variable getter or
* setter.
*/
static bool
IsSwitchableObjectVariableInstruction(const gd::String &instructionType);
/**
* \brief Return the common identifier for variable getter or setter or an
* empty string otherwise.
*
* The instruction type of the "number" one is actually used as the common
* identifier.
*/
static const gd::String &
GetSwitchableVariableInstructionIdentifier(const gd::String &instructionType);
/**
* \brief Return the variable type for variable getter or setter.
*/
static const gd::Variable::Type
GetSwitchableInstructionVariableType(const gd::String &instructionType);
/**
* \brief Modify the instruction type to match the given variable type.
*/
static void
SwitchVariableInstructionType(gd::Instruction &instruction,
const gd::Variable::Type variableType);
/**
* \brief Return the variable type of the instruction parameter.
*/
static const gd::Variable::Type GetVariableTypeFromParameters(
const gd::Platform &platform,
const gd::ProjectScopedContainers &projectScopedContainers,
const gd::Instruction &instruction);
/**
* \brief Modify the instruction type to match the variable type of the
* instruction parameter.
*/
static void SwitchBetweenUnifiedInstructionIfNeeded(
const gd::Platform &platform,
const gd::ProjectScopedContainers &projectScopedContainers,
gd::Instruction &instruction);
private:
static const gd::String variableGetterIdentifier;
static const gd::String variableSetterIdentifier;
static const gd::String variablePushIdentifier;
static const gd::String objectVariableGetterIdentifier;
static const gd::String objectVariableSetterIdentifier;
static const gd::String objectVariablePushIdentifier;
static const gd::String unknownInstructionIdentifier;
};
} // namespace gd

View File

@@ -19,12 +19,14 @@
#include "GDCore/IDE/Events/EventsPropertyReplacer.h" #include "GDCore/IDE/Events/EventsPropertyReplacer.h"
#include "GDCore/IDE/Events/EventsRefactorer.h" #include "GDCore/IDE/Events/EventsRefactorer.h"
#include "GDCore/IDE/Events/EventsVariableReplacer.h" #include "GDCore/IDE/Events/EventsVariableReplacer.h"
#include "GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.h"
#include "GDCore/IDE/Events/ExpressionsParameterMover.h" #include "GDCore/IDE/Events/ExpressionsParameterMover.h"
#include "GDCore/IDE/Events/ExpressionsRenamer.h" #include "GDCore/IDE/Events/ExpressionsRenamer.h"
#include "GDCore/IDE/Events/InstructionsParameterMover.h" #include "GDCore/IDE/Events/InstructionsParameterMover.h"
#include "GDCore/IDE/Events/InstructionsTypeRenamer.h" #include "GDCore/IDE/Events/InstructionsTypeRenamer.h"
#include "GDCore/IDE/Events/LinkEventTargetRenamer.h" #include "GDCore/IDE/Events/LinkEventTargetRenamer.h"
#include "GDCore/IDE/Events/ProjectElementRenamer.h" #include "GDCore/IDE/Events/ProjectElementRenamer.h"
#include "GDCore/IDE/Events/BehaviorParametersFiller.h"
#include "GDCore/IDE/EventsFunctionTools.h" #include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h" #include "GDCore/IDE/Project/ArbitraryBehaviorSharedDataWorker.h"
#include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h" #include "GDCore/IDE/Project/ArbitraryEventBasedBehaviorsWorker.h"
@@ -140,7 +142,6 @@ void WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
VariablesChangeset VariablesChangeset
WholeProjectRefactorer::ComputeChangesetForVariablesContainer( WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
gd::Project &project,
const gd::SerializerElement &oldSerializedVariablesContainer, const gd::SerializerElement &oldSerializedVariablesContainer,
const gd::VariablesContainer &newVariablesContainer) { const gd::VariablesContainer &newVariablesContainer) {
gd::VariablesChangeset changeset; gd::VariablesChangeset changeset;
@@ -180,6 +181,20 @@ WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
changeset.oldToNewVariableNames[oldName] = variableName; changeset.oldToNewVariableNames[oldName] = variableName;
} }
const auto &oldVariable = oldVariablesContainer.Get(oldName);
if (gd::WholeProjectRefactorer::HasAnyVariableTypeChanged(oldVariable, variable)) {
changeset.typeChangedVariableNames.insert(variableName);
}
const auto &variablesRenamingChangesetNode =
gd::WholeProjectRefactorer::ComputeChangesetForVariable(oldVariable,
variable);
if (variablesRenamingChangesetNode) {
changeset.modifiedVariables[oldName] =
std::move(variablesRenamingChangesetNode);
}
// Renamed or not, this is not a removed variable. // Renamed or not, this is not a removed variable.
removedUuidAndNames.erase(variable.GetPersistentUuid()); removedUuidAndNames.erase(variable.GetPersistentUuid());
} }
@@ -192,14 +207,124 @@ WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
return changeset; return changeset;
} }
std::shared_ptr<VariablesRenamingChangesetNode>
WholeProjectRefactorer::ComputeChangesetForVariable(
const gd::Variable &oldVariable, const gd::Variable &newVariable) {
if (newVariable.GetChildrenCount() == 0 ||
oldVariable.GetChildrenCount() == 0) {
return std::shared_ptr<VariablesRenamingChangesetNode>(nullptr);
}
std::unordered_map<gd::String, gd::String> oldVariableNamesByUuid;
for (const auto &pair : oldVariable.GetAllChildren()) {
const auto &oldName = pair.first;
const auto oldChild = pair.second;
// All variables are candidate to be removed.
oldVariableNamesByUuid[oldChild->GetPersistentUuid()] = oldName;
}
auto changeset = std::make_shared<VariablesRenamingChangesetNode>();
for (const auto &pair : newVariable.GetAllChildren()) {
const auto &newName = pair.first;
const auto newChild = pair.second;
auto existingOldVariableUuidAndName =
oldVariableNamesByUuid.find(newChild->GetPersistentUuid());
if (existingOldVariableUuidAndName == oldVariableNamesByUuid.end()) {
// This is a new variable.
continue;
}
const gd::String &oldName = existingOldVariableUuidAndName->second;
const auto &oldChild = oldVariable.GetChild(oldName);
if (oldName != newName) {
// This is a renamed child.
changeset->oldToNewVariableNames[oldName] = newName;
}
const auto &childChangeset =
gd::WholeProjectRefactorer::ComputeChangesetForVariable(oldChild,
*newChild);
if (childChangeset) {
changeset->modifiedVariables[oldName] = std::move(childChangeset);
}
}
if (changeset->modifiedVariables.size() == 0 &&
changeset->oldToNewVariableNames.size() == 0) {
return std::shared_ptr<VariablesRenamingChangesetNode>(nullptr);
}
return std::move(changeset);
};
bool WholeProjectRefactorer::HasAnyVariableTypeChanged(
const gd::Variable &oldVariable, const gd::Variable &newVariable) {
if (newVariable.GetType() != oldVariable.GetType()) {
return true;
}
if (newVariable.GetChildrenCount() == 0 ||
oldVariable.GetChildrenCount() == 0) {
return false;
}
std::unordered_map<gd::String, gd::String> oldVariableNamesByUuid;
for (const auto &pair : oldVariable.GetAllChildren()) {
const auto &oldName = pair.first;
const auto oldChild = pair.second;
// All variables are candidate to be removed.
oldVariableNamesByUuid[oldChild->GetPersistentUuid()] = oldName;
}
for (const auto &pair : newVariable.GetAllChildren()) {
const auto &newName = pair.first;
const auto newChild = pair.second;
auto existingOldVariableUuidAndName =
oldVariableNamesByUuid.find(newChild->GetPersistentUuid());
if (existingOldVariableUuidAndName == oldVariableNamesByUuid.end()) {
// This is a new variable.
continue;
}
const gd::String &oldName = existingOldVariableUuidAndName->second;
const auto &oldChild = oldVariable.GetChild(oldName);
if (gd::WholeProjectRefactorer::HasAnyVariableTypeChanged(oldChild,
*newChild)) {
return true;
}
}
return false;
}
void WholeProjectRefactorer::ApplyRefactoringForVariablesContainer( void WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
gd::Project &project, const gd::VariablesContainer &newVariablesContainer, gd::Project &project, gd::VariablesContainer &variablesContainer,
const gd::VariablesChangeset &changeset) { const gd::VariablesChangeset &changeset,
const gd::SerializerElement &originalSerializedVariables) {
// Revert changes
gd::SerializerElement editedSerializedVariables;
variablesContainer.SerializeTo(editedSerializedVariables);
variablesContainer.UnserializeFrom(originalSerializedVariables);
// Rename and remove variables
gd::EventsVariableReplacer eventsVariableReplacer( gd::EventsVariableReplacer eventsVariableReplacer(
project.GetCurrentPlatform(), newVariablesContainer, project.GetCurrentPlatform(), variablesContainer,
changeset.oldToNewVariableNames, changeset.removedVariableNames); changeset, changeset.removedVariableNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(project, gd::ProjectBrowserHelper::ExposeProjectEvents(project,
eventsVariableReplacer); eventsVariableReplacer);
// Apply back changes
variablesContainer.UnserializeFrom(editedSerializedVariables);
// Switch types of instructions
gd::EventsVariableInstructionTypeSwitcher
eventsVariableInstructionTypeSwitcher(project.GetCurrentPlatform(),
variablesContainer,
changeset.typeChangedVariableNames);
gd::ProjectBrowserHelper::ExposeProjectEvents(
project, eventsVariableInstructionTypeSwitcher);
} }
void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedBehavior( void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedBehavior(
@@ -208,7 +333,7 @@ void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedBehavior(
gd::EventsBasedBehavior &eventsBasedBehavior, gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &sourceExtensionName) { const gd::String &sourceExtensionName) {
const EventBasedBehaviorBrowser eventBasedBehaviorExposer( const EventBasedBehaviorBrowser eventBasedBehaviorExposer(
eventsBasedBehavior); eventsFunctionsExtension, eventsBasedBehavior);
WholeProjectRefactorer::RenameEventsFunctionsExtension( WholeProjectRefactorer::RenameEventsFunctionsExtension(
project, eventsFunctionsExtension, sourceExtensionName, project, eventsFunctionsExtension, sourceExtensionName,
eventsFunctionsExtension.GetName(), eventBasedBehaviorExposer); eventsFunctionsExtension.GetName(), eventBasedBehaviorExposer);
@@ -724,7 +849,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
oldPropertyName, newPropertyName); oldPropertyName, newPropertyName);
gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents( gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
project, eventsBasedBehavior, behaviorRenamer); project, eventsFunctionsExtension, eventsBasedBehavior, behaviorRenamer);
} else { } else {
// Properties that represent primitive values will be used through // Properties that represent primitive values will be used through
// their related actions/conditions/expressions. Rename these. // their related actions/conditions/expressions. Rename these.
@@ -794,7 +919,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
oldPropertyName, newPropertyName); oldPropertyName, newPropertyName);
gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents( gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
project, eventsBasedBehavior, behaviorRenamer); project, eventsFunctionsExtension, eventsBasedBehavior, behaviorRenamer);
} else { } else {
// Properties that represent primitive values will be used through // Properties that represent primitive values will be used through
// their related actions/conditions/expressions. Rename these. // their related actions/conditions/expressions. Rename these.
@@ -1391,53 +1516,38 @@ void WholeProjectRefactorer::DoRenameObject(
projectBrowser.ExposeFunctions(project, objectParameterRenamer); projectBrowser.ExposeFunctions(project, objectParameterRenamer);
} }
void WholeProjectRefactorer::ObjectOrGroupRemovedInLayout( void WholeProjectRefactorer::ObjectRemovedInLayout(
gd::Project &project, gd::Layout &layout, const gd::String &objectName, gd::Project &project, gd::Layout &layout, const gd::String &objectName) {
bool isObjectGroup, bool removeEventsAndGroups) {
auto projectScopedContainers = gd::ProjectScopedContainers:: auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout); MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
// Remove object in the current layout // Object groups can't have instances or be in other groups
if (removeEventsAndGroups) { for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(), if (layout.GetObjectGroups()[g].Find(objectName))
projectScopedContainers, layout.GetObjectGroups()[g].RemoveObject(objectName);
layout.GetEvents(), objectName);
}
if (!isObjectGroup) { // Object groups can't have instances or be in other
// groups
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < layout.GetObjectGroups().size(); ++g) {
if (layout.GetObjectGroups()[g].Find(objectName))
layout.GetObjectGroups()[g].RemoveObject(objectName);
}
}
layout.GetInitialInstances().RemoveInitialInstancesOfObject(objectName);
}
// Remove object in external events
if (removeEventsAndGroups) {
for (auto &externalEventsName :
GetAssociatedExternalEvents(project, layout.GetName())) {
auto &externalEvents = project.GetExternalEvents(externalEventsName);
gd::EventsRefactorer::RemoveObjectInEvents(
project.GetCurrentPlatform(), projectScopedContainers,
externalEvents.GetEvents(), objectName);
}
} }
layout.GetInitialInstances().RemoveInitialInstancesOfObject(objectName);
// Remove object in external layouts // Remove object in external layouts
if (!isObjectGroup) { // Object groups can't have instances std::vector<gd::String> externalLayoutsNames =
std::vector<gd::String> externalLayoutsNames = GetAssociatedExternalLayouts(project, layout);
GetAssociatedExternalLayouts(project, layout); for (gd::String name : externalLayoutsNames) {
for (gd::String name : externalLayoutsNames) { auto &externalLayout = project.GetExternalLayout(name);
auto &externalLayout = project.GetExternalLayout(name); externalLayout.GetInitialInstances().RemoveInitialInstancesOfObject(
externalLayout.GetInitialInstances().RemoveInitialInstancesOfObject( objectName);
objectName);
}
} }
} }
void WholeProjectRefactorer::BehaviorsAddedToObjectInLayout(
gd::Project &project, gd::Layout &layout, const gd::String &objectName) {
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
gd::BehaviorParametersFiller behaviorParameterFiller(
project.GetCurrentPlatform(), projectScopedContainers);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
project, layout, behaviorParameterFiller);
}
void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout( void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
gd::Project &project, gd::Layout &layout, const gd::String &oldName, gd::Project &project, gd::Layout &layout, const gd::String &oldName,
const gd::String &newName, bool isObjectGroup) { const gd::String &newName, bool isObjectGroup) {
@@ -1605,45 +1715,28 @@ void WholeProjectRefactorer::RenameObjectEffect(gd::Project &project,
project, layout, projectElementRenamer); project, layout, projectElementRenamer);
} }
void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsBasedObject( void WholeProjectRefactorer::ObjectRemovedInEventsBasedObject(
gd::Project &project, gd::EventsBasedObject &eventsBasedObject, gd::Project &project, gd::EventsBasedObject &eventsBasedObject,
gd::ObjectsContainer &globalObjectsContainer, gd::ObjectsContainer &globalObjectsContainer,
gd::ObjectsContainer &objectsContainer, const gd::String &objectName, gd::ObjectsContainer &objectsContainer, const gd::String &objectName) {
bool isObjectGroup, bool removeEventsAndGroups) {
for (auto &functionUniquePtr : for (auto &functionUniquePtr :
eventsBasedObject.GetEventsFunctions().GetInternalVector()) { eventsBasedObject.GetEventsFunctions().GetInternalVector()) {
auto function = functionUniquePtr.get(); auto function = functionUniquePtr.get();
WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction( WholeProjectRefactorer::ObjectRemovedInEventsFunction(
project, *function, globalObjectsContainer, objectsContainer, project, *function, globalObjectsContainer, objectsContainer,
objectName, isObjectGroup, isObjectGroup); objectName);
} }
} }
void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction( void WholeProjectRefactorer::ObjectRemovedInEventsFunction(
gd::Project &project, gd::EventsFunction &eventsFunction, gd::Project &project, gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &globalObjectsContainer, gd::ObjectsContainer &globalObjectsContainer,
gd::ObjectsContainer &objectsContainer, const gd::String &objectName, gd::ObjectsContainer &objectsContainer, const gd::String &objectName) {
bool isObjectGroup, bool removeEventsAndGroups) {
// In theory we should pass a ProjectScopedContainers to this function so it
// does not have to construct one. In practice, this is ok because we only
// deal with objects.
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(
globalObjectsContainer, objectsContainer);
if (removeEventsAndGroups) { for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size();
gd::EventsRefactorer::RemoveObjectInEvents( ++g) {
project.GetCurrentPlatform(), projectScopedContainers, if (eventsFunction.GetObjectGroups()[g].Find(objectName))
eventsFunction.GetEvents(), objectName); eventsFunction.GetObjectGroups()[g].RemoveObject(objectName);
}
if (!isObjectGroup) { // Object groups can't be in other groups
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size();
++g) {
if (eventsFunction.GetObjectGroups()[g].Find(objectName))
eventsFunction.GetObjectGroups()[g].RemoveObject(objectName);
}
}
} }
} }
@@ -1676,7 +1769,8 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project.GetCurrentPlatform(), projectScopedContainers, project.GetCurrentPlatform(), projectScopedContainers,
eventsFunction.GetEvents(), oldName, newName); eventsFunction.GetEvents(), oldName, newName);
if (!isObjectGroup) { // Object groups can't be in other groups // Object groups can't be in other groups
if (!isObjectGroup) {
for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size(); ++g) { for (std::size_t g = 0; g < eventsFunction.GetObjectGroups().size(); ++g) {
eventsFunction.GetObjectGroups()[g].RenameObject(oldName, newName); eventsFunction.GetObjectGroups()[g].RenameObject(oldName, newName);
} }
@@ -1686,7 +1780,8 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
void WholeProjectRefactorer::GlobalObjectOrGroupRenamed( void WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
gd::Project &project, const gd::String &oldName, const gd::String &newName, gd::Project &project, const gd::String &oldName, const gd::String &newName,
bool isObjectGroup) { bool isObjectGroup) {
if (!isObjectGroup) { // Object groups can't be in other groups // Object groups can't be in other groups
if (!isObjectGroup) {
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) { for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
project.GetObjectGroups()[g].RenameObject(oldName, newName); project.GetObjectGroups()[g].RenameObject(oldName, newName);
} }
@@ -1702,15 +1797,10 @@ void WholeProjectRefactorer::GlobalObjectOrGroupRenamed(
} }
} }
void WholeProjectRefactorer::GlobalObjectOrGroupRemoved( void WholeProjectRefactorer::GlobalObjectRemoved(
gd::Project &project, const gd::String &objectName, bool isObjectGroup, gd::Project &project, const gd::String &objectName) {
bool removeEventsAndGroups) { for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
if (!isObjectGroup) { // Object groups can't be in other groups project.GetObjectGroups()[g].RemoveObject(objectName);
if (removeEventsAndGroups) {
for (std::size_t g = 0; g < project.GetObjectGroups().size(); ++g) {
project.GetObjectGroups()[g].RemoveObject(objectName);
}
}
} }
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) { for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
@@ -1718,8 +1808,18 @@ void WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
if (layout.HasObjectNamed(objectName)) if (layout.HasObjectNamed(objectName))
continue; continue;
ObjectOrGroupRemovedInLayout(project, layout, objectName, isObjectGroup, ObjectRemovedInLayout(project, layout, objectName);
removeEventsAndGroups); }
}
void WholeProjectRefactorer::BehaviorsAddedToGlobalObject(
gd::Project &project, const gd::String &objectName) {
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
gd::Layout &layout = project.GetLayout(i);
if (layout.HasObjectNamed(objectName))
continue;
BehaviorsAddedToObjectInLayout(project, layout, objectName);
} }
} }

View File

@@ -9,7 +9,10 @@
#include <unordered_set> #include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <memory>
#include "GDCore/String.h" #include "GDCore/String.h"
#include "GDCore/Project/Variable.h"
namespace gd { namespace gd {
class Platform; class Platform;
class Project; class Project;
@@ -34,13 +37,26 @@ class BehaviorMetadata;
class UnfilledRequiredBehaviorPropertyProblem; class UnfilledRequiredBehaviorPropertyProblem;
class ProjectBrowser; class ProjectBrowser;
class SerializerElement; class SerializerElement;
struct VariablesRenamingChangesetNode;
} // namespace gd } // namespace gd
namespace gd { namespace gd {
struct VariablesChangeset { struct VariablesRenamingChangesetNode {
std::unordered_set<gd::String> removedVariableNames;
std::unordered_map<gd::String, gd::String> oldToNewVariableNames; std::unordered_map<gd::String, gd::String> oldToNewVariableNames;
std::unordered_map<gd::String, std::shared_ptr<gd::VariablesRenamingChangesetNode>>
modifiedVariables;
};
struct VariablesChangeset : VariablesRenamingChangesetNode {
std::unordered_set<gd::String> removedVariableNames;
/**
* No distinction is done between a change of the variable itself or its
* children. Ensuring that a child is actually the one with a type change
* would take more time than checking the instruction type is rightly set.
*/
std::unordered_set<gd::String> typeChangedVariableNames;
bool HasRemovedVariables() { return !removedVariableNames.empty(); } bool HasRemovedVariables() { return !removedVariableNames.empty(); }
@@ -51,8 +67,8 @@ struct VariablesChangeset {
* \brief Tool functions to do refactoring on the whole project after * \brief Tool functions to do refactoring on the whole project after
* changes like deletion or renaming of an object. * changes like deletion or renaming of an object.
* *
* \TODO Ideally ObjectOrGroupRenamedInLayout, ObjectOrGroupRemovedInLayout, * \TODO Ideally ObjectOrGroupRenamedInLayout, ObjectRemovedInLayout,
* GlobalObjectOrGroupRenamed, GlobalObjectOrGroupRemoved would be implemented * GlobalObjectOrGroupRenamed, GlobalObjectRemoved would be implemented
* using ExposeProjectEvents. * using ExposeProjectEvents.
*/ */
class GD_CORE_API WholeProjectRefactorer { class GD_CORE_API WholeProjectRefactorer {
@@ -62,7 +78,6 @@ class GD_CORE_API WholeProjectRefactorer {
* \brief Compute the changes made on the variables of a variable container. * \brief Compute the changes made on the variables of a variable container.
*/ */
static VariablesChangeset ComputeChangesetForVariablesContainer( static VariablesChangeset ComputeChangesetForVariablesContainer(
gd::Project &project,
const gd::SerializerElement &oldSerializedVariablesContainer, const gd::SerializerElement &oldSerializedVariablesContainer,
const gd::VariablesContainer &newVariablesContainer); const gd::VariablesContainer &newVariablesContainer);
@@ -71,9 +86,9 @@ class GD_CORE_API WholeProjectRefactorer {
* made to variables. * made to variables.
*/ */
static void ApplyRefactoringForVariablesContainer( static void ApplyRefactoringForVariablesContainer(
gd::Project &project, gd::Project &project, gd::VariablesContainer &variablesContainer,
const gd::VariablesContainer &newVariablesContainer, const gd::VariablesChangeset &changeset,
const gd::VariablesChangeset& changeset); const gd::SerializerElement &originalSerializedVariables);
/** /**
* \brief Refactor the project **before** an events function extension is * \brief Refactor the project **before** an events function extension is
@@ -376,11 +391,21 @@ class GD_CORE_API WholeProjectRefactorer {
* This will update the layout, all external layouts associated with it * This will update the layout, all external layouts associated with it
* and all external events associated with it. * and all external events associated with it.
*/ */
static void ObjectOrGroupRemovedInLayout(gd::Project& project, static void ObjectRemovedInLayout(gd::Project& project,
gd::Layout& layout, gd::Layout& layout,
const gd::String& objectName, const gd::String& objectName);
bool isObjectGroup,
bool removeEventsAndGroups = true); /**
* \brief Refactor the project after behaviors are added to an object in a
* layout.
*
* This will update the layout, all external events associated with it.
* The refactor is actually applied to all objects because it allow to handle
* groups.
*/
static void BehaviorsAddedToObjectInLayout(gd::Project &project,
gd::Layout &layout,
const gd::String &objectName);
/** /**
* \brief Refactor the project after an object is removed in an events-based * \brief Refactor the project after an object is removed in an events-based
@@ -388,14 +413,12 @@ class GD_CORE_API WholeProjectRefactorer {
* *
* This will update the events of the function and groups. * This will update the events of the function and groups.
*/ */
static void ObjectOrGroupRemovedInEventsBasedObject( static void ObjectRemovedInEventsBasedObject(
gd::Project& project, gd::Project& project,
gd::EventsBasedObject& eventsBasedObject, gd::EventsBasedObject& eventsBasedObject,
gd::ObjectsContainer& globalObjectsContainer, gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer, gd::ObjectsContainer& objectsContainer,
const gd::String& objectName, const gd::String& objectName);
bool isObjectGroup,
bool removeEventsAndGroups);
/** /**
* \brief Refactor the events function after an object or group is renamed * \brief Refactor the events function after an object or group is renamed
@@ -429,14 +452,12 @@ class GD_CORE_API WholeProjectRefactorer {
* *
* This will update the events of the function and groups. * This will update the events of the function and groups.
*/ */
static void ObjectOrGroupRemovedInEventsFunction( static void ObjectRemovedInEventsFunction(
gd::Project& project, gd::Project& project,
gd::EventsFunction& eventsFunction, gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& globalObjectsContainer, gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer, gd::ObjectsContainer& objectsContainer,
const gd::String& objectName, const gd::String& objectName);
bool isObjectGroup,
bool removeEventsAndGroups = true);
/** /**
* \brief Refactor the project after a global object is renamed. * \brief Refactor the project after a global object is renamed.
@@ -455,10 +476,18 @@ class GD_CORE_API WholeProjectRefactorer {
* This will update all the layouts, all external layouts associated with them * This will update all the layouts, all external layouts associated with them
* and all external events used by the layouts. * and all external events used by the layouts.
*/ */
static void GlobalObjectOrGroupRemoved(gd::Project& project, static void GlobalObjectRemoved(gd::Project& project,
const gd::String& objectName, const gd::String& objectName);
bool isObjectGroup,
bool removeEventsAndGroups = true); /**
* \brief Refactor the project after behaviors are added a global object.
*
* This will update all the layouts, all external events associated with them.
* The refactor is actually applied to all objects because it allow to handle
* groups.
*/
void BehaviorsAddedToGlobalObject(gd::Project &project,
const gd::String &objectName);
/** /**
* \brief Return the set of all the types of the objects that are using the * \brief Return the set of all the types of the objects that are using the
@@ -555,6 +584,13 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& behaviorName, const gd::String& behaviorName,
std::unordered_set<gd::String>& dependentBehaviorNames); std::unordered_set<gd::String>& dependentBehaviorNames);
static std::shared_ptr<VariablesRenamingChangesetNode>
ComputeChangesetForVariable(const gd::Variable &oldVariable,
const gd::Variable &newVariable);
static bool HasAnyVariableTypeChanged(const gd::Variable &oldVariable,
const gd::Variable &newVariable);
static const gd::String behaviorObjectParameterName; static const gd::String behaviorObjectParameterName;
static const gd::String parentObjectParameterName; static const gd::String parentObjectParameterName;

View File

@@ -107,6 +107,7 @@ public:
return Insert(object, position); return Insert(object, position);
} }
void RemoveEventsFunction(const gd::String& name) { return Remove(name); } void RemoveEventsFunction(const gd::String& name) { return Remove(name); }
void ClearEventsFunctions() { return Clear(); }
void MoveEventsFunction(std::size_t oldIndex, std::size_t newIndex) { void MoveEventsFunction(std::size_t oldIndex, std::size_t newIndex) {
return Move(oldIndex, newIndex); return Move(oldIndex, newIndex);
}; };

View File

@@ -15,7 +15,9 @@ namespace gd {
EventsFunctionsExtension::EventsFunctionsExtension() : EventsFunctionsExtension::EventsFunctionsExtension() :
gd::EventsFunctionsContainer( gd::EventsFunctionsContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension) {} gd::EventsFunctionsContainer::FunctionOwner::Extension),
globalVariables(gd::VariablesContainer::SourceType::ExtensionGlobal),
sceneVariables(gd::VariablesContainer::SourceType::ExtensionScene) {}
EventsFunctionsExtension::EventsFunctionsExtension( EventsFunctionsExtension::EventsFunctionsExtension(
const EventsFunctionsExtension& other) : const EventsFunctionsExtension& other) :
@@ -48,6 +50,8 @@ void EventsFunctionsExtension::Init(const gd::EventsFunctionsExtension& other) {
EventsFunctionsContainer::Init(other); EventsFunctionsContainer::Init(other);
eventsBasedBehaviors = other.eventsBasedBehaviors; eventsBasedBehaviors = other.eventsBasedBehaviors;
eventsBasedObjects = other.eventsBasedObjects; eventsBasedObjects = other.eventsBasedObjects;
globalVariables = other.GetGlobalVariables();
sceneVariables = other.GetSceneVariables();
} }
void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const { void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
@@ -82,6 +86,9 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
for (auto& dependency : dependencies) for (auto& dependency : dependencies)
SerializeDependencyTo(dependency, dependenciesElement.AddChild("")); SerializeDependencyTo(dependency, dependenciesElement.AddChild(""));
GetGlobalVariables().SerializeTo(element.AddChild("globalVariables"));
GetSceneVariables().SerializeTo(element.AddChild("sceneVariables"));
SerializeEventsFunctionsTo(element.AddChild("eventsFunctions")); SerializeEventsFunctionsTo(element.AddChild("eventsFunctions"));
eventsBasedBehaviors.SerializeElementsTo( eventsBasedBehaviors.SerializeElementsTo(
"eventsBasedBehavior", element.AddChild("eventsBasedBehaviors")); "eventsBasedBehavior", element.AddChild("eventsBasedBehaviors"));
@@ -147,6 +154,9 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
for (size_t i = 0; i < dependenciesElement.GetChildrenCount(); ++i) for (size_t i = 0; i < dependenciesElement.GetChildrenCount(); ++i)
dependencies.push_back( dependencies.push_back(
UnserializeDependencyFrom(dependenciesElement.GetChild(i))); UnserializeDependencyFrom(dependenciesElement.GetChild(i)));
globalVariables.UnserializeFrom(element.GetChild("globalVariables"));
sceneVariables.UnserializeFrom(element.GetChild("sceneVariables"));
// Only unserialize behaviors and objects names. // Only unserialize behaviors and objects names.
// As event based objects can contains objects using CustomBehavior and/or // As event based objects can contains objects using CustomBehavior and/or

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_EVENTSFUNCTIONEXTENSION_H #pragma once
#define GDCORE_EVENTSFUNCTIONEXTENSION_H
#include <vector> #include <vector>
@@ -12,6 +11,7 @@
#include "GDCore/Project/EventsBasedBehavior.h" #include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h" #include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunctionsContainer.h" #include "GDCore/Project/EventsFunctionsContainer.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h" #include "GDCore/String.h"
#include "GDCore/Tools/SerializableWithNameList.h" #include "GDCore/Tools/SerializableWithNameList.h"
namespace gd { namespace gd {
@@ -216,6 +216,41 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
///@} ///@}
/** \name Variable management
* Members functions related to layout variables management.
*/
///@{
/**
* Return the global variables of the extension (variables scoped to the
* entire game lifetime).
*/
inline const gd::VariablesContainer& GetGlobalVariables() const {
return globalVariables;
}
/**
* Return the global variables of the extension (variables scoped to the
* entire game lifetime).
*/
inline gd::VariablesContainer& GetGlobalVariables() { return globalVariables; }
/**
* Return the global variables of the extension (variables scoped to the
* lifetime of a scene).
*/
inline const gd::VariablesContainer& GetSceneVariables() const {
return sceneVariables;
}
/**
* Return the global variables of the extension (variables scoped to the
* lifetime of a scene).
*/
inline gd::VariablesContainer& GetSceneVariables() { return sceneVariables; }
///@}
/** \name Serialization /** \name Serialization
*/ */
///@{ ///@{
@@ -298,8 +333,9 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors; gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
gd::SerializableWithNameList<EventsBasedObject> eventsBasedObjects; gd::SerializableWithNameList<EventsBasedObject> eventsBasedObjects;
std::vector<gd::DependencyMetadata> dependencies; std::vector<gd::DependencyMetadata> dependencies;
gd::VariablesContainer globalVariables;
gd::VariablesContainer sceneVariables;
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_EVENTSFUNCTIONEXTENSION_H

View File

@@ -15,7 +15,7 @@
namespace gd { namespace gd {
gd::String* InitialInstance::badStringProperyValue = NULL; gd::String* InitialInstance::badStringPropertyValue = NULL;
InitialInstance::InitialInstance() InitialInstance::InitialInstance()
: objectName(""), : objectName(""),
@@ -34,6 +34,7 @@ InitialInstance::InitialInstance()
depth(0), depth(0),
locked(false), locked(false),
sealed(false), sealed(false),
keepRatio(true),
persistentUuid(UUID::MakeUuid4()) {} persistentUuid(UUID::MakeUuid4()) {}
void InitialInstance::UnserializeFrom(const SerializerElement& element) { void InitialInstance::UnserializeFrom(const SerializerElement& element) {
@@ -58,6 +59,7 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
SetLayer(element.GetStringAttribute("layer")); SetLayer(element.GetStringAttribute("layer"));
SetLocked(element.GetBoolAttribute("locked", false)); SetLocked(element.GetBoolAttribute("locked", false));
SetSealed(element.GetBoolAttribute("sealed", false)); SetSealed(element.GetBoolAttribute("sealed", false));
SetShouldKeepRatio(element.GetBoolAttribute("keepRatio", false));
persistentUuid = element.GetStringAttribute("persistentUuid"); persistentUuid = element.GetStringAttribute("persistentUuid");
if (persistentUuid.empty()) ResetPersistentUuid(); if (persistentUuid.empty()) ResetPersistentUuid();
@@ -120,6 +122,7 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
if (HasCustomDepth()) element.SetAttribute("depth", GetCustomDepth()); if (HasCustomDepth()) element.SetAttribute("depth", GetCustomDepth());
if (IsLocked()) element.SetAttribute("locked", IsLocked()); if (IsLocked()) element.SetAttribute("locked", IsLocked());
if (IsSealed()) element.SetAttribute("sealed", IsSealed()); if (IsSealed()) element.SetAttribute("sealed", IsSealed());
if (ShouldKeepRatio()) element.SetAttribute("keepRatio", ShouldKeepRatio());
if (persistentUuid.empty()) persistentUuid = UUID::MakeUuid4(); if (persistentUuid.empty()) persistentUuid = UUID::MakeUuid4();
element.SetStringAttribute("persistentUuid", persistentUuid); element.SetStringAttribute("persistentUuid", persistentUuid);
@@ -188,10 +191,10 @@ double InitialInstance::GetRawDoubleProperty(const gd::String& name) const {
const gd::String& InitialInstance::GetRawStringProperty( const gd::String& InitialInstance::GetRawStringProperty(
const gd::String& name) const { const gd::String& name) const {
if (!badStringProperyValue) badStringProperyValue = new gd::String(""); if (!badStringPropertyValue) badStringPropertyValue = new gd::String("");
const auto& it = stringProperties.find(name); const auto& it = stringProperties.find(name);
return it != stringProperties.end() ? it->second : *badStringProperyValue; return it != stringProperties.end() ? it->second : *badStringPropertyValue;
} }
void InitialInstance::SetRawDoubleProperty(const gd::String& name, void InitialInstance::SetRawDoubleProperty(const gd::String& name,

View File

@@ -206,6 +206,17 @@ class GD_CORE_API InitialInstance {
*/ */
void SetSealed(bool enable = true) { sealed = enable; } void SetSealed(bool enable = true) { sealed = enable; }
/**
* \brief Return true if the dimensions (width, height and depth) should keep
* the same ratio.
*/
bool ShouldKeepRatio() const { return keepRatio; };
/**
* \brief Define if instance's dimensions should keep the same ratio.
*/
void SetShouldKeepRatio(bool enable = true) { keepRatio = enable; }
///@} ///@}
/** \name Variable management /** \name Variable management
@@ -340,11 +351,13 @@ class GD_CORE_API InitialInstance {
gd::VariablesContainer initialVariables; ///< Instance specific variables gd::VariablesContainer initialVariables; ///< Instance specific variables
bool locked; ///< True if the instance is locked bool locked; ///< True if the instance is locked
bool sealed; ///< True if the instance is sealed bool sealed; ///< True if the instance is sealed
bool keepRatio; ///< True if the instance's dimensions
/// should keep the same ratio.
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID, mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
///< useful for hot reloading. /// useful for hot reloading.
static gd::String* static gd::String*
badStringProperyValue; ///< Empty string returned by GetRawStringProperty badStringPropertyValue; ///< Empty string returned by GetRawStringProperty
}; };
} // namespace gd } // namespace gd

View File

@@ -53,7 +53,8 @@ Layout::Layout()
stopSoundsOnStartup(true), stopSoundsOnStartup(true),
standardSortMethod(true), standardSortMethod(true),
disableInputWhenNotFocused(true), disableInputWhenNotFocused(true),
profiler(NULL) profiler(NULL),
variables(gd::VariablesContainer::SourceType::Scene)
{ {
gd::Layer layer; gd::Layer layer;
layer.SetCameraCount(1); layer.SetCameraCount(1);
@@ -518,11 +519,13 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
void GD_CORE_API FilterBehaviorNamesFromObject( void GD_CORE_API FilterBehaviorNamesFromObject(
const gd::Object &object, const gd::String &behaviorType, const gd::Object &object, const gd::String &behaviorType,
std::vector<gd::String> &behaviorNames) { std::vector<gd::String> &behaviorNames) {
for (size_t i = 0; i < behaviorNames.size(); i++) { for (size_t i = 0; i < behaviorNames.size();) {
auto &behaviorName = behaviorNames[i]; auto &behaviorName = behaviorNames[i];
if (!object.HasBehaviorNamed(behaviorName) || if (!object.HasBehaviorNamed(behaviorName) ||
object.GetBehavior(behaviorName).GetTypeName() != behaviorType) { object.GetBehavior(behaviorName).GetTypeName() != behaviorType) {
behaviorNames.erase(behaviorNames.begin() + i); behaviorNames.erase(behaviorNames.begin() + i);
} else {
++i;
} }
} }
} }

View File

@@ -4,8 +4,8 @@
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_LAYOUT_H #pragma once
#define GDCORE_LAYOUT_H
#include <map> #include <map>
#include <memory> #include <memory>
#include <vector> #include <vector>
@@ -504,5 +504,3 @@ GetBehaviorsOfObject(const ObjectsContainer& game,
} // namespace gd } // namespace gd
typedef gd::Layout Scene; typedef gd::Layout Scene;
#endif // GDCORE_LAYOUT_H

View File

@@ -24,14 +24,18 @@ Object::~Object() {}
Object::Object(const gd::String& name_, Object::Object(const gd::String& name_,
const gd::String& type_, const gd::String& type_,
std::unique_ptr<gd::ObjectConfiguration> configuration_) std::unique_ptr<gd::ObjectConfiguration> configuration_)
: name(name_), configuration(std::move(configuration_)) { : name(name_),
configuration(std::move(configuration_)),
objectVariables(gd::VariablesContainer::SourceType::Object) {
SetType(type_); SetType(type_);
} }
Object::Object(const gd::String& name_, Object::Object(const gd::String& name_,
const gd::String& type_, const gd::String& type_,
gd::ObjectConfiguration* configuration_) gd::ObjectConfiguration* configuration_)
: name(name_), configuration(configuration_) { : name(name_),
configuration(configuration_),
objectVariables(gd::VariablesContainer::SourceType::Object) {
SetType(type_); SetType(type_);
} }

View File

@@ -3,8 +3,8 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_OBJECT_H #pragma once
#define GDCORE_OBJECT_H
#include <map> #include <map>
#include <memory> #include <memory>
#include <vector> #include <vector>
@@ -287,5 +287,3 @@ struct ObjectHasName : public std::binary_function<std::unique_ptr<gd::Object>,
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_OBJECT_H

View File

@@ -12,6 +12,12 @@
namespace gd { namespace gd {
ObjectsContainersList
ObjectsContainersList::MakeNewEmptyObjectsContainersList() {
ObjectsContainersList objectsContainersList;
return objectsContainersList;
}
ObjectsContainersList ObjectsContainersList
ObjectsContainersList::MakeNewObjectsContainersListForProjectAndLayout( ObjectsContainersList::MakeNewObjectsContainersListForProjectAndLayout(
const gd::Project& project, const gd::Layout& layout) { const gd::Project& project, const gd::Layout& layout) {
@@ -21,6 +27,14 @@ ObjectsContainersList::MakeNewObjectsContainersListForProjectAndLayout(
return objectsContainersList; return objectsContainersList;
} }
ObjectsContainersList
ObjectsContainersList::MakeNewObjectsContainersListForProject(
const gd::Project& project) {
ObjectsContainersList objectsContainersList;
objectsContainersList.Add(project);
return objectsContainersList;
}
ObjectsContainersList ObjectsContainersList
ObjectsContainersList::MakeNewObjectsContainersListForContainers( ObjectsContainersList::MakeNewObjectsContainersListForContainers(
const gd::ObjectsContainer& globalObjectsContainer, const gd::ObjectsContainer& globalObjectsContainer,
@@ -31,6 +45,14 @@ ObjectsContainersList::MakeNewObjectsContainersListForContainers(
return objectsContainersList; return objectsContainersList;
} }
ObjectsContainersList
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
const gd::ObjectsContainer& objectsContainer) {
ObjectsContainersList objectsContainersList;
objectsContainersList.Add(objectsContainer);
return objectsContainersList;
}
bool ObjectsContainersList::HasObjectOrGroupNamed( bool ObjectsContainersList::HasObjectOrGroupNamed(
const gd::String& name) const { const gd::String& name) const {
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend(); for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
@@ -370,7 +392,7 @@ void ObjectsContainersList::ForEachObject(
gd::String ObjectsContainersList::GetTypeOfObject( gd::String ObjectsContainersList::GetTypeOfObject(
const gd::String& objectName) const { const gd::String& objectName) const {
if (objectsContainers.size() != 2) { if (objectsContainers.size() > 2) {
std::cout << this << std::endl; std::cout << this << std::endl;
std::cout << objectsContainers.size() << std::endl; std::cout << objectsContainers.size() << std::endl;
// TODO: rework forwarded methods so they can work with any number of // TODO: rework forwarded methods so they can work with any number of
@@ -379,19 +401,40 @@ gd::String ObjectsContainersList::GetTypeOfObject(
"ObjectsContainersList::GetTypeOfObject called with objectsContainers " "ObjectsContainersList::GetTypeOfObject called with objectsContainers "
"not being exactly 2. This is a logical error and will crash."); "not being exactly 2. This is a logical error and will crash.");
} }
if (objectsContainers.size() == 0) {
gd::LogWarning("ObjectsContainersList::GetTypeOfObject called without any "
"objectsContainer");
return "";
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
return gd::GetTypeOfObject(emptyObjectsContainer, *objectsContainers[0],
objectName, true);
}
return gd::GetTypeOfObject( return gd::GetTypeOfObject(
*objectsContainers[0], *objectsContainers[1], objectName, true); *objectsContainers[0], *objectsContainers[1], objectName, true);
} }
bool ObjectsContainersList::HasBehaviorInObjectOrGroup( bool ObjectsContainersList::HasBehaviorInObjectOrGroup(
const gd::String& objectOrGroupName, const gd::String& behaviorName) const { const gd::String& objectOrGroupName, const gd::String& behaviorName) const {
if (objectsContainers.size() != 2) { if (objectsContainers.size() > 2) {
// TODO: rework forwarded methods so they can work with any number of // TODO: rework forwarded methods so they can work with any number of
// containers. // containers.
gd::LogFatalError( gd::LogFatalError(
"ObjectsContainersList::GetTypeOfObject called with objectsContainers " "ObjectsContainersList::HasBehaviorInObjectOrGroup called with objectsContainers "
"not being exactly 2. This is a logical error and will crash."); "not being exactly 2. This is a logical error and will crash.");
} }
if (objectsContainers.size() == 0) {
gd::LogWarning("ObjectsContainersList::HasBehaviorInObjectOrGroup called without any "
"objectsContainer");
return false;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
return gd::HasBehaviorInObjectOrGroup(
emptyObjectsContainer, *objectsContainers[0], objectOrGroupName,
behaviorName, true);
}
return gd::HasBehaviorInObjectOrGroup(*objectsContainers[0], return gd::HasBehaviorInObjectOrGroup(*objectsContainers[0],
*objectsContainers[1], *objectsContainers[1],
objectOrGroupName, objectOrGroupName,
@@ -403,13 +446,24 @@ gd::String ObjectsContainersList::GetTypeOfBehaviorInObjectOrGroup(
const gd::String& objectOrGroupName, const gd::String& objectOrGroupName,
const gd::String& behaviorName, const gd::String& behaviorName,
bool searchInGroups) const { bool searchInGroups) const {
if (objectsContainers.size() != 2) { if (objectsContainers.size() > 2) {
// TODO: rework forwarded methods so they can work with any number of // TODO: rework forwarded methods so they can work with any number of
// containers. // containers.
gd::LogFatalError( gd::LogFatalError(
"ObjectsContainersList::GetTypeOfObject called with objectsContainers " "ObjectsContainersList::GetTypeOfBehaviorInObjectOrGroup called with objectsContainers "
"not being exactly 2. This is a logical error and will crash."); "not being exactly 2. This is a logical error and will crash.");
} }
if (objectsContainers.size() == 0) {
gd::LogWarning("ObjectsContainersList::GetTypeOfBehaviorInObjectOrGroup called without any "
"objectsContainer");
return "";
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
return gd::GetTypeOfBehaviorInObjectOrGroup(
emptyObjectsContainer, *objectsContainers[0], objectOrGroupName,
behaviorName, searchInGroups);
}
return gd::GetTypeOfBehaviorInObjectOrGroup(*objectsContainers[0], return gd::GetTypeOfBehaviorInObjectOrGroup(*objectsContainers[0],
*objectsContainers[1], *objectsContainers[1],
objectOrGroupName, objectOrGroupName,
@@ -419,13 +473,23 @@ gd::String ObjectsContainersList::GetTypeOfBehaviorInObjectOrGroup(
gd::String ObjectsContainersList::GetTypeOfBehavior( gd::String ObjectsContainersList::GetTypeOfBehavior(
const gd::String& behaviorName, bool searchInGroups) const { const gd::String& behaviorName, bool searchInGroups) const {
if (objectsContainers.size() != 2) { if (objectsContainers.size() > 2) {
// TODO: rework forwarded methods so they can work with any number of // TODO: rework forwarded methods so they can work with any number of
// containers. // containers.
gd::LogFatalError( gd::LogFatalError(
"ObjectsContainersList::GetTypeOfObject called with objectsContainers " "ObjectsContainersList::GetTypeOfBehavior called with objectsContainers "
"not being exactly 2. This is a logical error and will crash."); "not being exactly 2. This is a logical error and will crash.");
} }
if (objectsContainers.size() == 0) {
gd::LogWarning("ObjectsContainersList::GetTypeOfBehavior called without any "
"objectsContainer");
return "";
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
return gd::GetTypeOfBehavior(emptyObjectsContainer, *objectsContainers[0],
behaviorName, searchInGroups);
}
return gd::GetTypeOfBehavior(*objectsContainers[0], return gd::GetTypeOfBehavior(*objectsContainers[0],
*objectsContainers[1], *objectsContainers[1],
behaviorName, behaviorName,
@@ -434,14 +498,25 @@ gd::String ObjectsContainersList::GetTypeOfBehavior(
std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject( std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject(
const gd::String& objectName, bool searchInGroups) const { const gd::String& objectName, bool searchInGroups) const {
if (objectsContainers.size() != 2) { if (objectsContainers.size() > 2) {
// TODO: rework forwarded methods so they can work with any number of // TODO: rework forwarded methods so they can work with any number of
// containers. // containers.
gd::LogFatalError( gd::LogFatalError(
"ObjectsContainersList::GetTypeOfObject called with objectsContainers " "ObjectsContainersList::GetBehaviorsOfObject called with objectsContainers "
"not being exactly 2. This is a logical error and will crash."); "not being exactly 2. This is a logical error and will crash.");
} }
if (objectsContainers.size() == 0) {
gd::LogWarning("ObjectsContainersList::GetBehaviorsOfObject called without any "
"objectsContainer");
std::vector<gd::String> behaviors;
return behaviors;
}
if (objectsContainers.size() == 1) {
gd::ObjectsContainer emptyObjectsContainer;
return gd::GetBehaviorsOfObject(emptyObjectsContainer,
*objectsContainers[0], objectName,
searchInGroups);
}
return gd::GetBehaviorsOfObject( return gd::GetBehaviorsOfObject(
*objectsContainers[0], *objectsContainers[1], objectName, searchInGroups); *objectsContainers[0], *objectsContainers[1], objectName, searchInGroups);
} }

View File

@@ -30,13 +30,21 @@ class GD_CORE_API ObjectsContainersList {
public: public:
virtual ~ObjectsContainersList(){}; virtual ~ObjectsContainersList(){};
static ObjectsContainersList MakeNewEmptyObjectsContainersList();
static ObjectsContainersList MakeNewObjectsContainersListForProjectAndLayout( static ObjectsContainersList MakeNewObjectsContainersListForProjectAndLayout(
const gd::Project& project, const gd::Layout& layout); const gd::Project& project, const gd::Layout& layout);
static ObjectsContainersList MakeNewObjectsContainersListForProject(
const gd::Project& project);
static ObjectsContainersList MakeNewObjectsContainersListForContainers( static ObjectsContainersList MakeNewObjectsContainersListForContainers(
const gd::ObjectsContainer& globalObjectsContainer, const gd::ObjectsContainer& globalObjectsContainer,
const gd::ObjectsContainer& objectsContainer); const gd::ObjectsContainer& objectsContainer);
static ObjectsContainersList MakeNewObjectsContainersListForContainer(
const gd::ObjectsContainer& objectsContainer);
/** /**
* \brief Check if the specified object or group exists. * \brief Check if the specified object or group exists.
*/ */

View File

@@ -75,7 +75,8 @@ Project::Project()
currentPlatform(NULL), currentPlatform(NULL),
gdMajorVersion(gd::VersionWrapper::Major()), gdMajorVersion(gd::VersionWrapper::Major()),
gdMinorVersion(gd::VersionWrapper::Minor()), gdMinorVersion(gd::VersionWrapper::Minor()),
gdBuildVersion(gd::VersionWrapper::Build()) {} gdBuildVersion(gd::VersionWrapper::Build()),
variables(gd::VariablesContainer::SourceType::Global) {}
Project::~Project() {} Project::~Project() {}

View File

@@ -4,11 +4,12 @@
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_PROJECT_H #pragma once
#define GDCORE_PROJECT_H
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "GDCore/Events/CodeGeneration/DiagnosticReport.h"
#include "GDCore/Project/ExtensionProperties.h" #include "GDCore/Project/ExtensionProperties.h"
#include "GDCore/Project/LoadingScreen.h" #include "GDCore/Project/LoadingScreen.h"
#include "GDCore/Project/ObjectGroupsContainer.h" #include "GDCore/Project/ObjectGroupsContainer.h"
@@ -1050,6 +1051,10 @@ class GD_CORE_API Project : public ObjectsContainer {
std::size_t position = -1); std::size_t position = -1);
///@} ///@}
gd::WholeProjectDiagnosticReport& GetWholeProjectDiagnosticReport() {
return wholeProjectDiagnosticReport;
}
private: private:
/** /**
* Initialize from another game. Used by copy-ctor and assign-op. * Initialize from another game. Used by copy-ctor and assign-op.
@@ -1125,6 +1130,7 @@ class GD_CORE_API Project : public ObjectsContainer {
externalEvents; ///< List of all externals events externalEvents; ///< List of all externals events
ExtensionProperties 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 mutable unsigned int gdMajorVersion; ///< The GD major version used the last
///< time the project was saved. ///< time the project was saved.
mutable unsigned int gdMinorVersion; ///< The GD minor version used the last mutable unsigned int gdMinorVersion; ///< The GD minor version used the last
@@ -1134,5 +1140,3 @@ class GD_CORE_API Project : public ObjectsContainer {
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_PROJECT_H

View File

@@ -0,0 +1,116 @@
#include "ProjectScopedContainers.h"
#include "GDCore/IDE/EventsFunctionTools.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Events/Event.h"
namespace gd {
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForEventsFunctionsExtension(
const gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewEmptyObjectsContainersList(),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
};
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForFreeEventsFunction(
const gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer) {
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
project, eventsFunctionsExtension, eventsFunction, parameterObjectsContainer);
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddParameters(
eventsFunction.GetParametersForEvents(eventsFunctionsExtension));
return projectScopedContainers;
};
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForBehaviorEventsFunction(
const gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer) {
gd::EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
project,
eventsBasedBehavior,
eventsFunction,
parameterObjectsContainer);
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddPropertiesContainer(
eventsBasedBehavior.GetSharedPropertyDescriptors());
projectScopedContainers.AddPropertiesContainer(
eventsBasedBehavior.GetPropertyDescriptors());
projectScopedContainers.AddParameters(eventsFunction.GetParametersForEvents(
eventsBasedBehavior.GetEventsFunctions()));
return projectScopedContainers;
}
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForObjectEventsFunction(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer) {
gd::EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
project, eventsBasedObject, eventsFunction, parameterObjectsContainer);
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(
eventsFunctionsExtension),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddPropertiesContainer(
eventsBasedObject.GetPropertyDescriptors());
projectScopedContainers.AddParameters(eventsFunction.GetParametersForEvents(
eventsBasedObject.GetEventsFunctions()));
return projectScopedContainers;
}
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersWithLocalVariables(
const ProjectScopedContainers &projectScopedContainers,
const gd::BaseEvent &event) {
ProjectScopedContainers newProjectScopedContainers = projectScopedContainers;
newProjectScopedContainers.variablesContainersList =
VariablesContainersList::MakeNewVariablesContainersListPushing(
projectScopedContainers.GetVariablesContainersList(),
event.GetVariables());
return newProjectScopedContainers;
}
} // namespace gd

View File

@@ -13,6 +13,11 @@ class ObjectsContainersList;
class VariablesContainersList; class VariablesContainersList;
class PropertiesContainersList; class PropertiesContainersList;
class NamedPropertyDescriptor; class NamedPropertyDescriptor;
class BaseEvent;
class EventsFunctionsExtension;
class EventsFunction;
class EventsBasedBehavior;
class EventsBasedObject;
} // namespace gd } // namespace gd
namespace gd { namespace gd {
@@ -51,6 +56,21 @@ class ProjectScopedContainers {
return projectScopedContainers; return projectScopedContainers;
} }
static ProjectScopedContainers
MakeNewProjectScopedContainersForProject(const gd::Project &project) {
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForProject(
project),
VariablesContainersList::
MakeNewVariablesContainersListForProject(project),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers;
}
/**
* @deprecated Use another method for an explicit context instead.
*/
static ProjectScopedContainers MakeNewProjectScopedContainersFor( static ProjectScopedContainers MakeNewProjectScopedContainersFor(
const gd::ObjectsContainer &globalObjectsContainers, const gd::ObjectsContainer &globalObjectsContainers,
const gd::ObjectsContainer &objectsContainers) { const gd::ObjectsContainer &objectsContainers) {
@@ -61,7 +81,40 @@ class ProjectScopedContainers {
PropertiesContainersList::MakeNewEmptyPropertiesContainersList()); PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
return projectScopedContainers; return projectScopedContainers;
} };
static ProjectScopedContainers
MakeNewProjectScopedContainersForEventsFunctionsExtension(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension);
static ProjectScopedContainers
MakeNewProjectScopedContainersForFreeEventsFunction(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& parameterObjectsContainer);
static ProjectScopedContainers
MakeNewProjectScopedContainersForBehaviorEventsFunction(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer);
static ProjectScopedContainers
MakeNewProjectScopedContainersForObjectEventsFunction(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer);
static ProjectScopedContainers
MakeNewProjectScopedContainersWithLocalVariables(
const ProjectScopedContainers &projectScopedContainers,
const gd::BaseEvent &event);
ProjectScopedContainers &AddPropertiesContainer( ProjectScopedContainers &AddPropertiesContainer(
const gd::PropertiesContainer &container) { const gd::PropertiesContainer &container) {
@@ -152,6 +205,14 @@ class ProjectScopedContainers {
return variablesContainersList; return variablesContainersList;
}; };
/**
* @brief Allow modification of the variables containers list. This is used
* by code generation which does push and pop of local variable containers.
*/
gd::VariablesContainersList &GetVariablesContainersList() {
return variablesContainersList;
};
const gd::PropertiesContainersList &GetPropertiesContainersList() const { const gd::PropertiesContainersList &GetPropertiesContainersList() const {
return propertiesContainersList; return propertiesContainersList;
}; };

View File

@@ -198,7 +198,24 @@ void Variable::MoveChildInArray(const size_t oldIndex, const size_t newIndex) {
childrenArray.insert(childrenArray.begin() + newIndex, std::move(object)); childrenArray.insert(childrenArray.begin() + newIndex, std::move(object));
} }
Variable& Variable::PushNew() { return GetAtIndex(GetChildrenCount()); }; Variable& Variable::PushNew() {
const size_t count = GetChildrenCount();
auto& variable = GetAtIndex(count);
if (type == Type::Array && count > 0) {
const auto childType = GetAtIndex(count - 1).type;
variable.type = childType;
if (childType == Type::Number) {
variable.SetValue(0);
}
else if (childType == Type::String) {
variable.SetString("");
}
else if (childType == Type::Boolean) {
variable.SetBool(false);
}
}
return variable;
};
void Variable::RemoveAtIndex(const size_t index) { void Variable::RemoveAtIndex(const size_t index) {
if (index >= childrenArray.size()) return; if (index >= childrenArray.size()) return;
@@ -300,6 +317,23 @@ void Variable::UnserializeFrom(const SerializerElement& element) {
Variable& Variable::ResetPersistentUuid() { Variable& Variable::ResetPersistentUuid() {
persistentUuid = UUID::MakeUuid4(); persistentUuid = UUID::MakeUuid4();
for (auto& it : children) {
it.second->ResetPersistentUuid();
}
for (auto& it : childrenArray) {
it->ResetPersistentUuid();
}
return *this;
}
Variable& Variable::ClearPersistentUuid() {
persistentUuid = "";
for (auto& it : children) {
it.second->ClearPersistentUuid();
}
for (auto& it : childrenArray) {
it->ClearPersistentUuid();
}
return *this; return *this;
} }

View File

@@ -30,6 +30,8 @@ class GD_CORE_API Variable {
public: public:
static gd::Variable badVariable; static gd::Variable badVariable;
enum Type { enum Type {
Unknown,
// Primitive types // Primitive types
String, String,
Number, Number,
@@ -349,7 +351,7 @@ class GD_CORE_API Variable {
* \brief Remove the persistent UUID - when the variable no * \brief Remove the persistent UUID - when the variable no
* longer needs to be recognized between serializations. * longer needs to be recognized between serializations.
*/ */
Variable& ClearPersistentUuid() { persistentUuid = ""; return *this; }; Variable& ClearPersistentUuid();
/** /**
* \brief Get the persistent UUID used to recognize * \brief Get the persistent UUID used to recognize

View File

@@ -35,7 +35,13 @@ class VariableHasName {
}; };
} // namespace } // namespace
VariablesContainer::VariablesContainer() {} VariablesContainer::VariablesContainer()
: sourceType(VariablesContainer::SourceType::Unknown) {}
VariablesContainer::VariablesContainer(
VariablesContainer::SourceType sourceType_) {
sourceType = sourceType_;
}
bool VariablesContainer::Has(const gd::String& name) const { bool VariablesContainer::Has(const gd::String& name) const {
auto i = auto i =
@@ -229,6 +235,7 @@ VariablesContainer& VariablesContainer::operator=(
} }
void VariablesContainer::Init(const gd::VariablesContainer& other) { void VariablesContainer::Init(const gd::VariablesContainer& other) {
sourceType = other.sourceType;
persistentUuid = other.persistentUuid; persistentUuid = other.persistentUuid;
variables.clear(); variables.clear();
for (auto& it : other.variables) { for (auto& it : other.variables) {

View File

@@ -4,8 +4,7 @@
* reserved. This project is released under the MIT License. * reserved. This project is released under the MIT License.
*/ */
#ifndef GDCORE_VARIABLESCONTAINER_H #pragma once
#define GDCORE_VARIABLESCONTAINER_H
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "GDCore/Project/Variable.h" #include "GDCore/Project/Variable.h"
@@ -29,12 +28,25 @@ namespace gd {
*/ */
class GD_CORE_API VariablesContainer { class GD_CORE_API VariablesContainer {
public: public:
enum SourceType {
Unknown,
Global,
Scene,
Object,
Local,
ExtensionGlobal,
ExtensionScene
};
VariablesContainer(); VariablesContainer();
VariablesContainer(const SourceType sourceType);
VariablesContainer(const VariablesContainer&); VariablesContainer(const VariablesContainer&);
virtual ~VariablesContainer(){}; virtual ~VariablesContainer(){};
VariablesContainer& operator=(const VariablesContainer& rhs); VariablesContainer& operator=(const VariablesContainer& rhs);
SourceType GetSourceType() const { return sourceType; }
/** \name Variables management /** \name Variables management
* Members functions related to variables management. * Members functions related to variables management.
*/ */
@@ -89,7 +101,6 @@ class GD_CORE_API VariablesContainer {
*/ */
const gd::String& GetNameAt(std::size_t index) const; const gd::String& GetNameAt(std::size_t index) const;
#if defined(GD_IDE_ONLY)
/** /**
* \brief return the position of the variable called "name" in the variable * \brief return the position of the variable called "name" in the variable
* list * list
@@ -131,7 +142,6 @@ class GD_CORE_API VariablesContainer {
* \brief Move the specified variable at a new position in the list. * \brief Move the specified variable at a new position in the list.
*/ */
void Move(std::size_t oldIndex, std::size_t newIndex); void Move(std::size_t oldIndex, std::size_t newIndex);
#endif
/** /**
* \brief Clear all variables of the container. * \brief Clear all variables of the container.
@@ -178,6 +188,7 @@ class GD_CORE_API VariablesContainer {
///@} ///@}
private: private:
SourceType sourceType;
std::vector<std::pair<gd::String, std::shared_ptr<gd::Variable>>> variables; std::vector<std::pair<gd::String, std::shared_ptr<gd::Variable>>> variables;
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID, mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
///< useful for computing changesets. ///< useful for computing changesets.
@@ -192,5 +203,3 @@ class GD_CORE_API VariablesContainer {
}; };
} // namespace gd } // namespace gd
#endif // GDCORE_VARIABLESCONTAINER_H

View File

@@ -5,20 +5,50 @@
#include "GDCore/Project/Layout.h" #include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h" #include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h" #include "GDCore/Project/Variable.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
namespace gd { namespace gd {
Variable VariablesContainersList::badVariable; Variable VariablesContainersList::badVariable;
VariablesContainer VariablesContainersList::badVariablesContainer;
VariablesContainersList VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListForProjectAndLayout( VariablesContainersList::MakeNewVariablesContainersListForProjectAndLayout(
const gd::Project& project, const gd::Layout& layout) { const gd::Project& project, const gd::Layout& layout) {
VariablesContainersList variablesContainersList; VariablesContainersList variablesContainersList;
variablesContainersList.Add(project.GetVariables()); variablesContainersList.Push(project.GetVariables());
variablesContainersList.Add(layout.GetVariables()); variablesContainersList.Push(layout.GetVariables());
variablesContainersList.firstLocalVariableContainerIndex = 2;
return variablesContainersList; return variablesContainersList;
} }
VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListForProject(
const gd::Project& project) {
VariablesContainersList variablesContainersList;
variablesContainersList.Push(project.GetVariables());
variablesContainersList.firstLocalVariableContainerIndex = 1;
return variablesContainersList;
}
VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListForEventsFunctionsExtension(
const gd::EventsFunctionsExtension &extension) {
VariablesContainersList variablesContainersList;
variablesContainersList.Push(extension.GetGlobalVariables());
variablesContainersList.Push(extension.GetSceneVariables());
variablesContainersList.firstLocalVariableContainerIndex = 2;
return variablesContainersList;
}
VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListPushing(
const VariablesContainersList& variablesContainersList, const gd::VariablesContainer& variablesContainer) {
VariablesContainersList newVariablesContainersList(variablesContainersList);
newVariablesContainersList.Push(variablesContainer);
return newVariablesContainersList;
}
VariablesContainersList VariablesContainersList
VariablesContainersList::MakeNewEmptyVariablesContainersList() { VariablesContainersList::MakeNewEmptyVariablesContainersList() {
VariablesContainersList variablesContainersList; VariablesContainersList variablesContainersList;
@@ -43,6 +73,37 @@ const Variable& VariablesContainersList::Get(const gd::String& name) const {
return badVariable; return badVariable;
} }
const VariablesContainer &
VariablesContainersList::GetVariablesContainerFromVariableName(
const gd::String &variableName) const {
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
++it) {
if ((*it)->Has(variableName))
return **it;
}
return badVariablesContainer;
}
std::size_t
VariablesContainersList::GetVariablesContainerPositionFromVariableName(
const gd::String &variableName) const {
for (std::size_t i = variablesContainers.size() - 1; i >= 0 ; --i) {
if (variablesContainers[i]->Has(variableName))
return i;
}
return gd::String::npos;
}
std::size_t VariablesContainersList::GetLocalVariablesContainerPosition(
const gd::VariablesContainer &localVariableContainer) const {
for (std::size_t i = firstLocalVariableContainerIndex;
i < variablesContainers.size(); ++i) {
if (variablesContainers[i] == &localVariableContainer)
return i - firstLocalVariableContainerIndex;
}
return gd::String::npos;
}
bool VariablesContainersList::HasVariablesContainer(const gd::VariablesContainer& variablesContainer) const { bool VariablesContainersList::HasVariablesContainer(const gd::VariablesContainer& variablesContainer) const {
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend(); for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
++it) { ++it) {

View File

@@ -8,6 +8,7 @@ class Project;
class Layout; class Layout;
class VariablesContainer; class VariablesContainer;
class Variable; class Variable;
class EventsFunctionsExtension;
} // namespace gd } // namespace gd
namespace gd { namespace gd {
@@ -30,6 +31,20 @@ class GD_CORE_API VariablesContainersList {
MakeNewVariablesContainersListForProjectAndLayout(const gd::Project& project, MakeNewVariablesContainersListForProjectAndLayout(const gd::Project& project,
const gd::Layout& layout); const gd::Layout& layout);
static VariablesContainersList
MakeNewVariablesContainersListForProject(const gd::Project& project);
static VariablesContainersList
MakeNewVariablesContainersListForEventsFunctionsExtension(
const gd::EventsFunctionsExtension &extension);
static VariablesContainersList MakeNewVariablesContainersListPushing(
const VariablesContainersList &variablesContainersList,
const gd::VariablesContainer &variablesContainer);
/**
* @deprecated Use another method for an explicit context instead.
*/
static VariablesContainersList MakeNewEmptyVariablesContainersList(); static VariablesContainersList MakeNewEmptyVariablesContainersList();
/** /**
@@ -47,6 +62,10 @@ class GD_CORE_API VariablesContainersList {
*/ */
bool HasVariablesContainer(const gd::VariablesContainer& variablesContainer) const; bool HasVariablesContainer(const gd::VariablesContainer& variablesContainer) const;
// TODO: Rename GetTopMostVariablesContainer and GetBottomMostVariablesContainer
// to give a clearer access to segments of the container list.
// For instance, a project tree segment and an event tree segment.
/** /**
* Get the variables container at the top of the scope (so the most "global" one). * Get the variables container at the top of the scope (so the most "global" one).
* \brief Avoid using apart when a scope must be forced. * \brief Avoid using apart when a scope must be forced.
@@ -57,29 +76,76 @@ class GD_CORE_API VariablesContainersList {
}; };
/** /**
* Get the variables container at the bottom of the scope (so the most "local" one). * Get the variables container at the bottom of the scope
* (so the most "local" one) excluding local variables.
* \brief Avoid using apart when a scope must be forced. * \brief Avoid using apart when a scope must be forced.
*/ */
const VariablesContainer* GetBottomMostVariablesContainer() const { const VariablesContainer* GetBottomMostVariablesContainer() const {
if (variablesContainers.empty()) return nullptr; if (variablesContainers.empty()) return nullptr;
return variablesContainers.back(); return variablesContainers.at(firstLocalVariableContainerIndex - 1);
} }
/**
* Get the variables container for a given variable.
*/
const VariablesContainer &
GetVariablesContainerFromVariableName(const gd::String &variableName) const;
/**
* Get the variables container index for a given variable.
*/
std::size_t GetVariablesContainerPositionFromVariableName(
const gd::String &variableName) const;
/**
* \brief Get the index of the given local variables container.
*/
std::size_t GetLocalVariablesContainerPosition(
const gd::VariablesContainer &localVariableContainer) const;
/**
* \brief Get the variable container at the specified index in the list.
*
* \warning Trying to access to a not existing variable container will result
* in undefined behavior.
*/
const gd::VariablesContainer& GetVariablesContainer(std::size_t index) const {
return *variablesContainers.at(index);
}
/**
* \brief Return the number variable containers.
*/
std::size_t GetVariablesContainersCount() const { return variablesContainers.size(); }
/** /**
* \brief Call the callback for each variable having a name matching the specified search. * \brief Call the callback for each variable having a name matching the specified search.
*/ */
void ForEachVariableMatchingSearch(const gd::String& search, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const; void ForEachVariableMatchingSearch(const gd::String& search, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const;
/** Do not use - should be private but accessible to let Emscripten create a temporary. */ /**
VariablesContainersList() {}; * \brief Push a new variables container to the context.
private: */
void Push(const gd::VariablesContainer& variablesContainer) {
void Add(const gd::VariablesContainer& variablesContainer) {
variablesContainers.push_back(&variablesContainer); variablesContainers.push_back(&variablesContainer);
}; };
/**
* \brief Pop a variables container from the context.
*/
void Pop() {
variablesContainers.pop_back();
};
/** Do not use - should be private but accessible to let Emscripten create a temporary. */
VariablesContainersList(): firstLocalVariableContainerIndex(0) {};
private:
std::vector<const gd::VariablesContainer*> variablesContainers; std::vector<const gd::VariablesContainer*> variablesContainers;
std::size_t firstLocalVariableContainerIndex;
static Variable badVariable; static Variable badVariable;
static VariablesContainer badVariablesContainer;
}; };
} // namespace gd } // namespace gd

View File

@@ -80,7 +80,7 @@ TEST_CASE("EventsList", "[common][events]") {
#if defined(WINDOWS) #if defined(WINDOWS)
REQUIRE(3000 >= endMemory - startMemory); REQUIRE(3000 >= endMemory - startMemory);
#else #else
REQUIRE(1500 >= endMemory - startMemory); REQUIRE(1600 >= endMemory - startMemory);
#endif #endif
} }
} }

View File

@@ -126,6 +126,46 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddParameter("object", _("Object"), "") .AddParameter("object", _("Object"), "")
.SetFunctionName("getFromBaseExpression"); .SetFunctionName("getFromBaseExpression");
baseObject.AddAction("SetNumberObjectVariable",
"Do something with number object variables",
"This does something with variables",
"Do something with variables",
"",
"",
"")
.AddParameter("object", "Object")
.AddParameter("objectvar", "Variable")
.AddParameter("operator", "Operator", "number")
.AddParameter("number", "Value");
baseObject.AddAction("SetStringObjectVariable",
"Do something with string object variables",
"This does something with variables",
"Do something with variables",
"",
"",
"")
.AddParameter("object", "Object")
.AddParameter("objectvar", "Variable")
.AddParameter("operator", "Operator", "string")
.AddParameter("string", "Value")
.SetRelevantForLayoutEventsOnly();
baseObject.AddAction("SetBooleanObjectVariable",
"Do something with boolean object variables",
"This does something with object variables",
"Do something with object variables",
"",
"",
"")
.AddParameter("object", "Object")
.AddParameter("objectvar", "Variable")
.AddParameter("operator", "Value", "boolean")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("yesorno", "Value")
.SetRelevantForLayoutEventsOnly();
// Declare default behaviors that are used by event-based objects to avoid // Declare default behaviors that are used by event-based objects to avoid
// warnings. // warnings.
{ {
@@ -204,6 +244,66 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.SetHidden(); .SetHidden();
platform.AddExtension(extension); platform.AddExtension(extension);
} }
{
// Create an extension without namespace to match the switchable variable instructions.
std::shared_ptr<gd::PlatformExtension> extension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
extension->SetExtensionInformation(
"BuiltinVariables", "My testing extension for variables", "", "", "");
extension
->AddAction("SetNumberVariable",
"Do something with number variables",
"This does something with variables",
"Do something with variables",
"",
"",
"")
.AddParameter("variable", "Variable")
.AddParameter("operator", "Operator", "number")
.AddParameter("number", "Value")
.SetFunctionName("setNumberVariable");
extension
->AddAction("SetStringVariable",
"Do something with string variables",
"This does something with variables",
"Do something with variables",
"",
"",
"")
.AddParameter("variable", "Variable")
.AddParameter("operator", "Operator", "string")
.AddParameter("string", "Value")
.SetFunctionName("setStringVariable");
extension
->AddAction("SetBooleanVariable",
"Do something with boolean variables",
"This does something with variables",
"Do something with variables",
"",
"",
"")
.AddParameter("variable", "Variable")
.AddParameter("operator", "Operator", "boolean")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("trueorfalse", "")
.SetFunctionName("setBooleanVariable");
extension->AddStrExpression("ToString",
"",
"",
"",
"")
.AddParameter("number", "")
.SetFunctionName("toString");
platform.AddExtension(extension);
}
// Create an extension with various stuff inside. // Create an extension with various stuff inside.
std::shared_ptr<gd::PlatformExtension> extension = std::shared_ptr<gd::PlatformExtension> extension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension); std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);

View File

@@ -273,7 +273,7 @@ TEST_CASE("EventsVariablesFinder (FindAllObjectVariables)", "[common]") {
UseObjectVariable("MyObject", "MyObjectVariable")); UseObjectVariable("MyObject", "MyObjectVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables( auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object); platform, project, layout, "MyObject");
REQUIRE(variableNames.size() == 1); REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable"); REQUIRE(*(variableNames.begin()) == "MyObjectVariable");
@@ -290,7 +290,7 @@ TEST_CASE("EventsVariablesFinder (FindAllObjectVariables)", "[common]") {
UseObjectVariableInExpression("MyObject", "MyObjectVariable")); UseObjectVariableInExpression("MyObject", "MyObjectVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables( auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object); platform, project, layout, "MyObject");
REQUIRE(variableNames.size() == 1); REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable"); REQUIRE(*(variableNames.begin()) == "MyObjectVariable");
@@ -309,7 +309,7 @@ TEST_CASE("EventsVariablesFinder (FindAllObjectVariables)", "[common]") {
UseExternalEvents(layout, externalEvents); UseExternalEvents(layout, externalEvents);
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables( auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object); platform, project, layout, "MyObject");
REQUIRE(variableNames.size() == 1); REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable"); REQUIRE(*(variableNames.begin()) == "MyObjectVariable");
@@ -329,7 +329,7 @@ TEST_CASE("EventsVariablesFinder (FindAllObjectVariables)", "[common]") {
UseObjectVariable("MyObject2", "MyObjectVariable2")); UseObjectVariable("MyObject2", "MyObjectVariable2"));
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables( auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object1); platform, project, layout, "MyObject1");
REQUIRE(variableNames.size() == 1); REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable1"); REQUIRE(*(variableNames.begin()) == "MyObjectVariable1");
@@ -352,7 +352,7 @@ TEST_CASE("EventsVariablesFinder (FindAllObjectVariables)", "[common]") {
UseExternalEvents(layout, externalEvents); UseExternalEvents(layout, externalEvents);
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables( auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object1); platform, project, layout, "MyObject1");
REQUIRE(variableNames.size() == 1); REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable1"); REQUIRE(*(variableNames.begin()) == "MyObjectVariable1");

View File

@@ -627,7 +627,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneVariable).getAsNumber() + 1");
} }
{ {
auto node = auto node =
@@ -639,7 +639,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsNumber() + getLayoutVariable(MySceneVariable2).getAsNumber()"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneVariable).getAsNumber() + getAnyVariable(MySceneVariable2).getAsNumber()");
} }
} }
SECTION("Scene variables (conflict with a global variable)") { SECTION("Scene variables (conflict with a global variable)") {
@@ -653,7 +653,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(SceneVariableWithNameReused).getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(SceneVariableWithNameReused).getAsNumber() + 1");
} }
} }
SECTION("Scene variables (2 levels)") { SECTION("Scene variables (2 levels)") {
@@ -667,7 +667,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + 1");
} }
{ {
auto node = auto node =
@@ -679,7 +679,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getAnyVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, string)") { SECTION("Scene variables (2 levels with bracket accessor, string)") {
@@ -693,7 +693,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + 1");
} }
{ {
auto node = auto node =
@@ -705,7 +705,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getAnyVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, number)") { SECTION("Scene variables (2 levels with bracket accessor, number)") {
@@ -719,7 +719,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(3).getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(3).getAsNumber() + 1");
} }
{ {
auto node = auto node =
@@ -731,7 +731,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(3).getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(3).getAsNumber()"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(3).getAsNumber() + getAnyVariable(MySceneStructureVariable2).getChild(3).getAsNumber()");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, using a number variable as index)") { SECTION("Scene variables (2 levels with bracket accessor, using a number variable as index)") {
@@ -745,7 +745,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneVariable).getAsNumber()).getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(getAnyVariable(MySceneVariable).getAsNumber()).getAsNumber() + 1");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, using a string variable as index)") { SECTION("Scene variables (2 levels with bracket accessor, using a string variable as index)") {
@@ -759,7 +759,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStringVariable).getAsString()).getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(getAnyVariable(MySceneStringVariable).getAsString()).getAsNumber() + 1");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, using a non string/number variable as index)") { SECTION("Scene variables (2 levels with bracket accessor, using a non string/number variable as index)") {
@@ -773,7 +773,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneBooleanVariable).getAsNumberOrString()).getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(getAnyVariable(MySceneBooleanVariable).getAsNumberOrString()).getAsNumber() + 1");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type as index)") { SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type as index)") {
@@ -787,7 +787,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumberOrString()).getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumberOrString()).getAsNumber() + 1");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type and an operator with a number as index)") { SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type and an operator with a number as index)") {
@@ -801,7 +801,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumber() + 2).getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumber() + 2).getAsNumber() + 1");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type and an operator with a string as index)") { SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type and an operator with a string as index)") {
@@ -815,7 +815,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsString() + \"Test\").getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsString() + \"Test\").getAsNumber() + 1");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type as index) (expression type: number|string)") { SECTION("Scene variables (2 levels with bracket accessor, using a unknown variable type as index) (expression type: number|string)") {
@@ -829,7 +829,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumberOrString()).getAsNumberOrString()"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumberOrString()).getAsNumberOrString()");
} }
} }
SECTION("Scene variables (2 levels with bracket accessor, using a number variable casted to string as index)") { SECTION("Scene variables (2 levels with bracket accessor, using a number variable casted to string as index)") {
@@ -843,7 +843,74 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"\" + getLayoutVariable(MySceneVariable).getAsString()).getAsNumber() + 1"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStructureVariable).getChild(\"\" + getAnyVariable(MySceneVariable).getAsString()).getAsNumber() + 1");
}
}
SECTION("Variable with bracket accessor and operators") {
{
auto node = parser.ParseExpression(
"MySceneStructureVariable[\"MyUndeclaredChild\"] * 2");
gd::ExpressionCodeGenerator expressionCodeGenerator(
"number", "", codeGenerator, context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getAnyVariable(MySceneStructureVariable).getChild("
"\"MyUndeclaredChild\").getAsNumber() * 2");
}
{
auto node =
parser.ParseExpression("MySceneStructureVariable[\"MyUndeclaredChild\"] / 2");
gd::ExpressionCodeGenerator expressionCodeGenerator(
"number", "", codeGenerator, context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getAnyVariable(MySceneStructureVariable).getChild("
"\"MyUndeclaredChild\").getAsNumber() / 2");
}
{
auto node =
parser.ParseExpression("MySceneStructureVariable[\"MyUndeclaredChild\"] - 2");
gd::ExpressionCodeGenerator expressionCodeGenerator(
"number", "", codeGenerator, context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getAnyVariable(MySceneStructureVariable).getChild("
"\"MyUndeclaredChild\").getAsNumber() - 2");
}
{
auto node = parser.ParseExpression(
"MySceneStructureVariable["
"MySceneStructureVariable2[\"MyUndeclaredChild\"]] * 2");
gd::ExpressionCodeGenerator expressionCodeGenerator(
"number", "", codeGenerator, context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getAnyVariable(MySceneStructureVariable).getChild("
"getAnyVariable(MySceneStructureVariable2).getChild("
"\"MyUndeclaredChild\").getAsNumberOrString()).getAsNumber() * 2");
}
{
auto node = parser.ParseExpression(
"MySceneStructureVariable[\"Child\" + "
"ToString(MySceneStructureVariable2[\"MyUndeclaredChild\"] * 2)] * 2");
gd::ExpressionCodeGenerator expressionCodeGenerator(
"number", "", codeGenerator, context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() ==
"getAnyVariable(MySceneStructureVariable).getChild("
"\"Child\" + "
"toString(getAnyVariable(MySceneStructureVariable2).getChild("
"\"MyUndeclaredChild\").getAsNumber() * 2)).getAsNumber() * 2");
} }
} }
SECTION("Object variable with non existing object (invalid)") { SECTION("Object variable with non existing object (invalid)") {
@@ -1023,80 +1090,83 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
"fakeBadVariable"); "fakeBadVariable");
} }
} }
SECTION("Valid variables (upcoming, new 'variable' type working for any variable)") {
// When implemented, copy the test cases from the next section, like this:
// SECTION("simple variable") {
// REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
// codeGenerator, context, "variable", "MySceneVariable", "")
// == "getLayoutVariable(MySceneVariable)");
// }
// SECTION("simple (global) variable") {
// REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
// codeGenerator, context, "variable", "MyGlobalNumberVariable", "")
// == "getProjectVariable(MyGlobalNumberVariable)");
// }
}
SECTION("Valid variables (legacy, pre-scoped variables)") { SECTION("Valid variables (legacy, pre-scoped variables)") {
// Check that the scope is forwarded by the parser.
SECTION("simple variable") { SECTION("simple variable") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable", "") codeGenerator, context, "scenevar", "MySceneVariable", "")
== "getLayoutVariable(myVariable)"); == "getLayoutVariable(MySceneVariable)");
}
SECTION("simple (global) variable") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "globalvar", "MyGlobalNumberVariable", "")
== "getProjectVariable(MyGlobalNumberVariable)");
}
}
SECTION("Valid variables") {
// getAnyVariable is a mocked value. The function doesn't actually exist.
// The actual scope switching is done by GDJS and tested by GDevelop.js
// integration tests.
SECTION("simple variable") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "variable", "MySceneVariable", "")
== "getAnyVariable(MySceneVariable)");
} }
SECTION("child dot accessor") { SECTION("child dot accessor") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable.myChild", "") codeGenerator, context, "variable", "MySceneVariable.myChild", "")
== "getLayoutVariable(myVariable).getChild(\"myChild\")"); == "getAnyVariable(MySceneVariable).getChild(\"myChild\")");
} }
SECTION("2 children") { SECTION("2 children") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable.child1.child2", "") codeGenerator, context, "variable", "MySceneVariable.child1.child2", "")
== "getLayoutVariable(myVariable).getChild(\"child1\").getChild(\"child2\")"); == "getAnyVariable(MySceneVariable).getChild(\"child1\").getChild(\"child2\")");
} }
SECTION("bracket access") { SECTION("bracket access") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[ \"hello\" + " codeGenerator, context, "variable", "MySceneVariable[ \"hello\" + "
"\"world\" ]", "") "\"world\" ]", "")
== "getLayoutVariable(myVariable).getChild(\"hello\" + \"world\")"); == "getAnyVariable(MySceneVariable).getChild(\"hello\" + \"world\")");
} }
SECTION("bracket access (using a string object variable inside)") { SECTION("bracket access (using a string object variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[MySpriteObject.MyStringVariable]", "") codeGenerator, context, "variable", "MySceneVariable[MySpriteObject.MyStringVariable]", "")
== "getLayoutVariable(myVariable).getChild(getVariableForObject(MySpriteObject, MyStringVariable).getAsString())"); == "getAnyVariable(MySceneVariable).getChild(getVariableForObject(MySpriteObject, MyStringVariable).getAsString())");
} }
SECTION("bracket access (using a number object variable inside)") { SECTION("bracket access (using a number object variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[MySpriteObject.MyNumberVariable]", "") codeGenerator, context, "variable", "MySceneVariable[MySpriteObject.MyNumberVariable]", "")
== "getLayoutVariable(myVariable).getChild(getVariableForObject(MySpriteObject, MyNumberVariable).getAsNumber())"); == "getAnyVariable(MySceneVariable).getChild(getVariableForObject(MySpriteObject, MyNumberVariable).getAsNumber())");
} }
SECTION("bracket access (using a string variable inside)") { SECTION("bracket access (using a string variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[MySceneStringVariable]", "") codeGenerator, context, "variable", "MySceneVariable[MySceneStringVariable]", "")
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneStringVariable).getAsString())"); == "getAnyVariable(MySceneVariable).getChild(getAnyVariable(MySceneStringVariable).getAsString())");
} }
SECTION("bracket access (using a number variable inside)") { SECTION("bracket access (using a number variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[MySceneVariable]", "") codeGenerator, context, "variable", "MySceneVariable[MySceneVariable]", "")
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneVariable).getAsNumber())"); == "getAnyVariable(MySceneVariable).getChild(getAnyVariable(MySceneVariable).getAsNumber())");
} }
SECTION("bracket access (using a string global variable inside)") { SECTION("bracket access (using a string global variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[MyGlobalStringVariable]", "") codeGenerator, context, "variable", "MySceneVariable[MyGlobalStringVariable]", "")
== "getLayoutVariable(myVariable).getChild(getProjectVariable(MyGlobalStringVariable).getAsString())"); == "getAnyVariable(MySceneVariable).getChild(getAnyVariable(MyGlobalStringVariable).getAsString())");
} }
SECTION("bracket access (using a number global variable inside)") { SECTION("bracket access (using a number global variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[MyGlobalNumberVariable]", "") codeGenerator, context, "variable", "MySceneVariable[MyGlobalNumberVariable]", "")
== "getLayoutVariable(myVariable).getChild(getProjectVariable(MyGlobalNumberVariable).getAsNumber())"); == "getAnyVariable(MySceneVariable).getChild(getAnyVariable(MyGlobalNumberVariable).getAsNumber())");
} }
SECTION("bracket access (using a boolean variable inside)") { SECTION("bracket access (using a boolean variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[MySceneBooleanVariable]", "") codeGenerator, context, "variable", "MySceneVariable[MySceneBooleanVariable]", "")
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneBooleanVariable).getAsNumberOrString())"); == "getAnyVariable(MySceneVariable).getChild(getAnyVariable(MySceneBooleanVariable).getAsNumberOrString())");
} }
SECTION("bracket access (using a structure variable inside)") { SECTION("bracket access (using a structure variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "scenevar", "myVariable[MySceneStructureVariable.MyChild.SubChild]", "") codeGenerator, context, "variable", "MySceneVariable[MySceneStructureVariable.MyChild.SubChild]", "")
== "getLayoutVariable(myVariable).getChild(getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"SubChild\").getAsNumberOrString())"); == "getAnyVariable(MySceneVariable).getChild(getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"SubChild\").getAsNumberOrString())");
} }
SECTION("object variable") { SECTION("object variable") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
@@ -1106,7 +1176,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
SECTION("object variable with bracket access (using a structure variable inside)") { SECTION("object variable with bracket access (using a structure variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator, context, "objectvar", "myVariable[MySceneStringVariable]", "MySpriteObject") codeGenerator, context, "objectvar", "myVariable[MySceneStringVariable]", "MySpriteObject")
== "getVariableForObject(MySpriteObject, myVariable).getChild(getLayoutVariable(MySceneStringVariable).getAsString())"); == "getVariableForObject(MySpriteObject, myVariable).getChild(getAnyVariable(MySceneStringVariable).getAsString())");
} }
SECTION("object variable with bracket access (using an object variable inside)") { SECTION("object variable with bracket access (using an object variable inside)") {
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode( REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
@@ -1322,7 +1392,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"You have \" + getLayoutVariable(MySceneVariable).getAsString() + \" points\""); REQUIRE(expressionCodeGenerator.GetOutput() == "\"You have \" + getAnyVariable(MySceneVariable).getAsString() + \" points\"");
} }
{ {
auto node = auto node =
@@ -1334,7 +1404,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsString() + getLayoutVariable(MySceneStringVariable).getAsString()"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneVariable).getAsString() + getAnyVariable(MySceneStringVariable).getAsString()");
} }
} }
SECTION("Expression/parent type is 'string' (with an unknown variable)") { SECTION("Expression/parent type is 'string' (with an unknown variable)") {
@@ -1348,7 +1418,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"You have \" + getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsString() + \" points\""); REQUIRE(expressionCodeGenerator.GetOutput() == "\"You have \" + getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsString() + \" points\"");
} }
} }
SECTION("Expression/parent type is 'string' (2 number variables)") { SECTION("Expression/parent type is 'string' (2 number variables)") {
@@ -1362,7 +1432,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsString() + getLayoutVariable(MySceneVariable2).getAsString() + \"world\""); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneVariable).getAsString() + getAnyVariable(MySceneVariable2).getAsString() + \"world\"");
} }
{ {
auto node = auto node =
@@ -1374,7 +1444,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsString() + getLayoutVariable(MySceneVariable2).getAsString() + getLayoutVariable(MySceneStringVariable).getAsString()"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneVariable).getAsString() + getAnyVariable(MySceneVariable2).getAsString() + getAnyVariable(MySceneStringVariable).getAsString()");
} }
} }
SECTION("Expression/parent type is 'string' (array variable)") { SECTION("Expression/parent type is 'string' (array variable)") {
@@ -1388,7 +1458,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getLayoutVariable(MySceneNumberArrayVariable).getChild(2).getAsString() + \"world\""); REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getAnyVariable(MySceneNumberArrayVariable).getChild(2).getAsString() + \"world\"");
} }
{ {
auto node = auto node =
@@ -1400,7 +1470,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getLayoutVariable(MySceneEmptyArrayVariable).getChild(2).getAsString() + \"world\""); REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getAnyVariable(MySceneEmptyArrayVariable).getChild(2).getAsString() + \"world\"");
} }
} }
@@ -1415,7 +1485,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getLayoutVariable(MySceneVariable).getAsNumber() + 456"); REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getAnyVariable(MySceneVariable).getAsNumber() + 456");
} }
{ {
auto node = auto node =
@@ -1427,7 +1497,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStringVariable).getAsNumber() + getLayoutVariable(MySceneVariable).getAsNumber()"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStringVariable).getAsNumber() + getAnyVariable(MySceneVariable).getAsNumber()");
} }
} }
SECTION("Expression/parent type is 'string' (with an unknown variable)") { SECTION("Expression/parent type is 'string' (with an unknown variable)") {
@@ -1441,7 +1511,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumber() + 456"); REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getAnyVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumber() + 456");
} }
} }
SECTION("Expression/parent type is 'number' (2 string variables)") { SECTION("Expression/parent type is 'number' (2 string variables)") {
@@ -1455,7 +1525,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStringVariable).getAsNumber() + getLayoutVariable(MySceneStringVariable).getAsNumber() + 456"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStringVariable).getAsNumber() + getAnyVariable(MySceneStringVariable).getAsNumber() + 456");
} }
{ {
auto node = auto node =
@@ -1467,7 +1537,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStringVariable).getAsNumber() + getLayoutVariable(MySceneStringVariable).getAsNumber() + getLayoutVariable(MySceneVariable).getAsNumber()"); REQUIRE(expressionCodeGenerator.GetOutput() == "getAnyVariable(MySceneStringVariable).getAsNumber() + getAnyVariable(MySceneStringVariable).getAsNumber() + getAnyVariable(MySceneVariable).getAsNumber()");
} }
} }
SECTION("Expression/parent type is 'number' (array variable)") { SECTION("Expression/parent type is 'number' (array variable)") {
@@ -1481,7 +1551,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getLayoutVariable(MySceneNumberArrayVariable).getChild(2).getAsNumber() + 456"); REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getAnyVariable(MySceneNumberArrayVariable).getChild(2).getAsNumber() + 456");
} }
{ {
auto node = auto node =
@@ -1493,7 +1563,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getLayoutVariable(MySceneEmptyArrayVariable).getChild(2).getAsNumber() + 456"); REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getAnyVariable(MySceneEmptyArrayVariable).getChild(2).getAsNumber() + 456");
} }
} }
@@ -1509,7 +1579,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getLayoutVariable(MySceneNumberArrayVariable).getChild(2 + getLayoutVariable(MySceneStringVariable).getAsNumber()).getAsString() + \"world\" + getLayoutVariable(MySceneVariable).getAsString() + \"world 2\""); REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getAnyVariable(MySceneNumberArrayVariable).getChild(2 + getAnyVariable(MySceneStringVariable).getAsNumber()).getAsString() + \"world\" + getAnyVariable(MySceneVariable).getAsString() + \"world 2\"");
} }
{ {
auto node = auto node =
@@ -1521,7 +1591,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(node); REQUIRE(node);
node->Visit(expressionCodeGenerator); node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getLayoutVariable(MySceneNumberArrayVariable).getChild(\"foo\" + getLayoutVariable(MySceneVariable).getAsString() + \"bar\").getAsString() + \"world\" + getLayoutVariable(MySceneVariable).getAsString() + \"world 2\""); REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getAnyVariable(MySceneNumberArrayVariable).getChild(\"foo\" + getAnyVariable(MySceneVariable).getAsString() + \"bar\").getAsString() + \"world\" + getAnyVariable(MySceneVariable).getAsString() + \"world 2\"");
} }
} }
} }

View File

@@ -55,8 +55,8 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Object or expression completions when type is string") { SECTION("Object or expression completions when type is string") {
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }", "{ 0, string, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }", "{ 3, no type, 2, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "My", 0, 2).ToString() gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "My", 0, 2).ToString()
}; };
// clang-format on // clang-format on
@@ -67,8 +67,8 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Object or expression completions when type is number") { SECTION("Object or expression completions when type is number") {
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 0, number, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }", "{ 0, number, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }", "{ 3, no type, 2, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number", "My", 0, 2).ToString() gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number", "My", 0, 2).ToString()
}; };
// clang-format on // clang-format on
@@ -79,8 +79,8 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Object or expression completions when type is number|string") { SECTION("Object or expression completions when type is number|string") {
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 0, number|string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }", "{ 0, number|string, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }", "{ 3, no type, 2, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number|string", "My", 0, 2).ToString() gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number|string", "My", 0, 2).ToString()
}; };
// clang-format on // clang-format on
@@ -94,8 +94,8 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Object or expression completions in a variable name") { SECTION("Object or expression completions in a variable name") {
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }", "{ 0, string, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }", "{ 3, no type, 2, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "My", 0, 2).ToString() gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "My", 0, 2).ToString()
}; };
// clang-format on // clang-format on
@@ -115,8 +115,8 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Object or expression completions in a variable index") { SECTION("Object or expression completions in a variable index") {
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 0, number, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }", "{ 0, number, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }", "{ 3, no type, 2, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number", "My", 0, 2).ToString() gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number", "My", 0, 2).ToString()
}; };
// clang-format on // clang-format on
@@ -136,7 +136,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Object when type is an object") { SECTION("Object when type is an object") {
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 0, object, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }", "{ 0, object, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
}; };
// clang-format on // clang-format on
REQUIRE(getCompletionsFor("object", "My", 0) == expectedCompletions); REQUIRE(getCompletionsFor("object", "My", 0) == expectedCompletions);
@@ -149,7 +149,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
// result in different code generation): // result in different code generation):
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 0, objectPtr, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }", "{ 0, objectPtr, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
}; };
// clang-format on // clang-format on
REQUIRE(getCompletionsFor("objectPtr", "My", 0) == expectedCompletions); REQUIRE(getCompletionsFor("objectPtr", "My", 0) == expectedCompletions);
@@ -204,8 +204,8 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 0, unknown, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }", "{ 0, unknown, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }", "{ 3, no type, 2, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("unknown", "My", 9, 10).ToString() gd::ExpressionCompletionDescription::ForExpressionWithPrefix("unknown", "My", 9, 10).ToString()
}; };
// clang-format on // clang-format on
@@ -215,7 +215,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Function with a Variable as argument") { SECTION("Function with a Variable as argument") {
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }", "{ 3, no type, 2, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
}; };
// clang-format on // clang-format on
REQUIRE(getCompletionsFor("number", REQUIRE(getCompletionsFor("number",
@@ -225,7 +225,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Object function with a Variable as argument") { SECTION("Object function with a Variable as argument") {
// clang-format off // clang-format off
std::vector<gd::String> expectedCompletions{ std::vector<gd::String> expectedCompletions{
"{ 3, no type, 1, no prefix, myObjectVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }", "{ 3, no type, 2, no prefix, myObjectVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
}; };
// clang-format on // clang-format on
REQUIRE(getCompletionsFor("number", REQUIRE(getCompletionsFor("number",
@@ -254,7 +254,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test with string type") { SECTION("Test with string type") {
// clang-format off // clang-format off
std::vector<gd::String> expectedObjectCompletions{ std::vector<gd::String> expectedObjectCompletions{
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }" "{ 0, string, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
}; };
std::vector<gd::String> expectedBehaviorOrFunctionCompletions{ std::vector<gd::String> expectedBehaviorOrFunctionCompletions{
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("Func", 9, 13, "MyObject").ToString(), gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("Func", 9, 13, "MyObject").ToString(),
@@ -277,7 +277,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test with 'number|string' type") { SECTION("Test with 'number|string' type") {
// clang-format off // clang-format off
std::vector<gd::String> expectedObjectCompletions{ std::vector<gd::String> expectedObjectCompletions{
"{ 0, number|string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }" "{ 0, number|string, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
}; };
std::vector<gd::String> expectedBehaviorOrFunctionCompletions{ std::vector<gd::String> expectedBehaviorOrFunctionCompletions{
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("Func", 9, 13, "MyObject").ToString(), gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("Func", 9, 13, "MyObject").ToString(),
@@ -303,7 +303,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test 1") { SECTION("Test 1") {
// clang-format off // clang-format off
std::vector<gd::String> expectedObjectCompletions{ std::vector<gd::String> expectedObjectCompletions{
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }" "{ 0, string, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
}; };
std::vector<gd::String> expectedBehaviorOrFunctionCompletions{ std::vector<gd::String> expectedBehaviorOrFunctionCompletions{
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("Func", 9, 13, "MyObject").ToString(), gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("Func", 9, 13, "MyObject").ToString(),
@@ -336,7 +336,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test 1") { SECTION("Test 1") {
// clang-format off // clang-format off
std::vector<gd::String> expectedObjectCompletions{ std::vector<gd::String> expectedObjectCompletions{
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }" "{ 0, string, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
}; };
std::vector<gd::String> expectedBehaviorCompletions{ std::vector<gd::String> expectedBehaviorCompletions{
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("MyBehavior", 9, 19, "MyObject").ToString()}; gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("MyBehavior", 9, 19, "MyObject").ToString()};
@@ -366,7 +366,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test 2") { SECTION("Test 2") {
// clang-format off // clang-format off
std::vector<gd::String> expectedObjectCompletions{ std::vector<gd::String> expectedObjectCompletions{
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }" "{ 0, string, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
}; };
std::vector<gd::String> expectedBehaviorCompletions{ std::vector<gd::String> expectedBehaviorCompletions{
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("MyBehavior", 9, 19, "MyObject").ToString() gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("MyBehavior", 9, 19, "MyObject").ToString()
@@ -396,7 +396,7 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
SECTION("Test 1") { SECTION("Test 1") {
// clang-format off // clang-format off
std::vector<gd::String> expectedObjectCompletions{ std::vector<gd::String> expectedObjectCompletions{
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }" "{ 0, string, 2, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
}; };
std::vector<gd::String> expectedBehaviorCompletions{ std::vector<gd::String> expectedBehaviorCompletions{
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("MyBehavior", 9, 19, "MyObject").ToString() gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("MyBehavior", 9, 19, "MyObject").ToString()

View File

@@ -11,7 +11,7 @@
#include "GDCore/IDE/Events/ExpressionValidator.h" #include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h" #include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h" #include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableParentFinder.h" #include "GDCore/IDE/Events/ExpressionVariablePathFinder.h"
#include "GDCore/Project/Layout.h" #include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h" #include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h" #include "GDCore/Project/ProjectScopedContainers.h"
@@ -22,6 +22,8 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
gd::Project project; gd::Project project;
gd::Platform platform; gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform); SetupProjectWithDummyPlatform(project, platform);
project.GetVariables().InsertNew("MyProjectVariable");
auto &layout1 = project.InsertNewLayout("Layout1", 0); auto &layout1 = project.InsertNewLayout("Layout1", 0);
layout1.GetVariables().InsertNew("MySceneVariable"); layout1.GetVariables().InsertNew("MySceneVariable");
layout1.GetVariables().InsertNew("MySceneVariable2"); layout1.GetVariables().InsertNew("MySceneVariable2");
@@ -204,7 +206,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(node != nullptr); REQUIRE(node != nullptr);
// Also check that if we try to find the last parent of node, it is not defined. // Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfNode = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node); platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr); REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr); REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
@@ -232,7 +234,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(node != nullptr); REQUIRE(node != nullptr);
// Also check that if we try to find the last parent of node, it is not defined. // Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfNode = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node); platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr); REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr); REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
@@ -786,7 +788,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(node != nullptr); REQUIRE(node != nullptr);
// Also check that if we try to find the last parent of node, it is not defined. // Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfNode = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node); platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr); REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr); REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
@@ -1519,13 +1521,61 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
} }
} }
SECTION("Variable with bracket accessor and operators") {
{
auto node =
parser.ParseExpression("MySceneStructureVariable[\"MyUndeclaredChild\"] * 2");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
{
auto node =
parser.ParseExpression("MySceneStructureVariable[\"MyUndeclaredChild\"] / 2");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
{
auto node =
parser.ParseExpression("MySceneStructureVariable[\"MyUndeclaredChild\"] - 2");
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
{
auto node = parser.ParseExpression(
"MySceneStructureVariable["
"MySceneStructureVariable2[\"MyUndeclaredChild\"]] * 2");
gd::ExpressionValidator validator(platform, projectScopedContainers,
"number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
{
// TODO `ToString` should not be required here.
auto node = parser.ParseExpression(
"MySceneStructureVariable[\"Child\" + "
"ToString(MySceneStructureVariable2[\"MyUndeclaredChild\"] * 2)] * 2");
gd::ExpressionValidator validator(platform, projectScopedContainers,
"number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
}
SECTION("Invalid scene variables (1 level, variable does not exist)") { SECTION("Invalid scene variables (1 level, variable does not exist)") {
{ {
auto node = auto node =
parser.ParseExpression("MyNonExistingSceneVariable"); parser.ParseExpression("MyNonExistingSceneVariable");
// Also check that if we try to find the last parent of node, it is not defined. // Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfNode = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node); platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr); REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr); REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
@@ -1557,7 +1607,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
parser.ParseExpression("MyNonExistingSceneVariable.MyNonExistingChild"); parser.ParseExpression("MyNonExistingSceneVariable.MyNonExistingChild");
// Also check that if we try to find the last parent of node, it is not defined. // Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfNode = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node); platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr); REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr); REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
@@ -1641,7 +1691,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
parser.ParseExpression("MyNonExistingSpriteObject.MyVariable"); parser.ParseExpression("MyNonExistingSpriteObject.MyVariable");
// Also check that if we try to find the last parent of node, it is not defined. // Also check that if we try to find the last parent of node, it is not defined.
auto lastParentOfNode = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfNode = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, *node); platform, projectScopedContainers, *node);
REQUIRE(lastParentOfNode.parentVariable == nullptr); REQUIRE(lastParentOfNode.parentVariable == nullptr);
REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr); REQUIRE(lastParentOfNode.parentVariablesContainer == nullptr);
@@ -2711,6 +2761,103 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
"You entered a text, but this type was expected: variable"); "You entered a text, but this type was expected: variable");
} }
} }
SECTION("Variable declaration") {
SECTION("Undeclared variable") {
auto node = parser.ParseExpression("MyUndeclaredVariable");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "variable");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"No variable with this name found.");
}
SECTION("Undeclared variable with children") {
auto node = parser.ParseExpression("MyUndeclaredVariable.MyChild.MyChild");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "variable");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"No variable with this name found.");
}
SECTION("Declared scene variable") {
auto node = parser.ParseExpression("MySceneVariable");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "variable");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
SECTION("Declared scene variable with children") {
// Children themselves don't need to be declared.
auto node = parser.ParseExpression("MySceneVariable.MyChild.MyChild");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "variable");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
SECTION("Declared project variable") {
auto node = parser.ParseExpression("MyProjectVariable");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "variable");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
SECTION("Declared project variable with children") {
auto node = parser.ParseExpression("MyProjectVariable.MyChild.MyChild");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "variable");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
SECTION("Undeclared legacy pre-scope scene variable") {
auto node = parser.ParseExpression("MyUndeclaredVariable");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "scenevar");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
SECTION("Undeclared legacy pre-scope scene variable with children") {
auto node = parser.ParseExpression("MyUndeclaredVariable.MyChild.MyChild");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "scenevar");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
SECTION("Undeclared legacy pre-scope project variable") {
auto node = parser.ParseExpression("MyUndeclaredVariable");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "globalvar");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
SECTION("Undeclared legacy pre-scope project variable with children") {
auto node = parser.ParseExpression("MyUndeclaredVariable.MyChild.MyChild");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "globalvar");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
}
SECTION("Valid variables") { SECTION("Valid variables") {
SECTION("simple variable") { SECTION("simple variable") {
@@ -3218,10 +3365,10 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(variable2ObjectName == "MySpriteObject2"); REQUIRE(variable2ObjectName == "MySpriteObject2");
// Also check the ability to find the last parent of the variables: // Also check the ability to find the last parent of the variables:
auto lastParentOfVariable1Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfVariable1Node = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable1Node); platform, projectScopedContainers, variable1Node);
REQUIRE(lastParentOfVariable1Node.parentVariablesContainer == &mySpriteObject.GetVariables()); REQUIRE(lastParentOfVariable1Node.parentVariablesContainer == &mySpriteObject.GetVariables());
auto lastParentOfVariable2Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfVariable2Node = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable2Node); platform, projectScopedContainers, variable2Node);
REQUIRE(lastParentOfVariable2Node.parentVariablesContainer == &mySpriteObject2.GetVariables()); REQUIRE(lastParentOfVariable2Node.parentVariablesContainer == &mySpriteObject2.GetVariables());
@@ -3257,10 +3404,10 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(variable2ObjectName == "MySpriteObject"); REQUIRE(variable2ObjectName == "MySpriteObject");
// Also check the ability to find the last parent of the variables: // Also check the ability to find the last parent of the variables:
auto lastParentOfVariable1Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfVariable1Node = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable1Node); platform, projectScopedContainers, variable1Node);
REQUIRE(lastParentOfVariable1Node.parentVariablesContainer == &mySpriteObject.GetVariables()); REQUIRE(lastParentOfVariable1Node.parentVariablesContainer == &mySpriteObject.GetVariables());
auto lastParentOfVariable2Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfVariable2Node = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable2Node); platform, projectScopedContainers, variable2Node);
REQUIRE(lastParentOfVariable2Node.parentVariablesContainer == &mySpriteObject.GetVariables()); REQUIRE(lastParentOfVariable2Node.parentVariablesContainer == &mySpriteObject.GetVariables());
@@ -3286,7 +3433,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(variable1ObjectName == "MySpriteObject"); REQUIRE(variable1ObjectName == "MySpriteObject");
// Also check the ability to find the last parent of the variable: // Also check the ability to find the last parent of the variable:
auto lastParentOfVariable1Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfVariable1Node = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable1Node); platform, projectScopedContainers, variable1Node);
REQUIRE(lastParentOfVariable1Node.parentVariablesContainer == &mySpriteObject.GetVariables()); REQUIRE(lastParentOfVariable1Node.parentVariablesContainer == &mySpriteObject.GetVariables());
@@ -3313,7 +3460,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
REQUIRE(variable1ObjectName == "MySpriteObject"); REQUIRE(variable1ObjectName == "MySpriteObject");
// Also check the ability to find the last parent of the variable: // Also check the ability to find the last parent of the variable:
auto lastParentOfVariable1Node = gd::ExpressionVariableParentFinder::GetLastParentOfNode( auto lastParentOfVariable1Node = gd::ExpressionVariablePathFinder::GetLastParentOfNode(
platform, projectScopedContainers, variable1Node); platform, projectScopedContainers, variable1Node);
REQUIRE(lastParentOfVariable1Node.parentVariable == &mySpriteObject.GetVariables().Get("MyVariable")); REQUIRE(lastParentOfVariable1Node.parentVariable == &mySpriteObject.GetVariables().Get("MyVariable"));

View File

@@ -1214,10 +1214,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0); layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object2", 0); layout1.InsertNewObject(project, "MyExtension::Sprite", "Object2", 0);
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout( gd::WholeProjectRefactorer::ObjectRemovedInLayout(
project, layout1, "Object1", /* isObjectGroup =*/false); project, layout1, "Object1");
gd::WholeProjectRefactorer::GlobalObjectOrGroupRemoved( gd::WholeProjectRefactorer::GlobalObjectRemoved(
project, "GlobalObject1", /* isObjectGroup =*/false); project, "GlobalObject1");
REQUIRE(layout1.GetObjectGroups()[0].Find("Object1") == false); REQUIRE(layout1.GetObjectGroups()[0].Find("Object1") == false);
REQUIRE(layout1.GetObjectGroups()[0].Find("Object2") == true); REQUIRE(layout1.GetObjectGroups()[0].Find("Object2") == true);
REQUIRE(layout1.GetObjectGroups()[0].Find("NotExistingObject") == true); REQUIRE(layout1.GetObjectGroups()[0].Find("NotExistingObject") == true);
@@ -1243,10 +1243,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
layout1.GetInitialInstances().InsertInitialInstance(instance2); layout1.GetInitialInstances().InsertInitialInstance(instance2);
layout1.GetInitialInstances().InsertInitialInstance(instance3); layout1.GetInitialInstances().InsertInitialInstance(instance3);
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout( gd::WholeProjectRefactorer::ObjectRemovedInLayout(
project, layout1, "Object1", /* isObjectGroup =*/false); project, layout1, "Object1");
gd::WholeProjectRefactorer::GlobalObjectOrGroupRemoved( gd::WholeProjectRefactorer::GlobalObjectRemoved(
project, "GlobalObject1", /* isObjectGroup =*/false); project, "GlobalObject1");
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object1") == REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object1") ==
false); false);
REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object2") == REQUIRE(layout1.GetInitialInstances().HasInstancesOfObject("Object2") ==
@@ -1285,10 +1285,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
externalLayout2.GetInitialInstances().InsertInitialInstance(instance2); externalLayout2.GetInitialInstances().InsertInitialInstance(instance2);
externalLayout2.GetInitialInstances().InsertInitialInstance(instance3); externalLayout2.GetInitialInstances().InsertInitialInstance(instance3);
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout( gd::WholeProjectRefactorer::ObjectRemovedInLayout(
project, layout1, "Object1", /* isObjectGroup =*/false); project, layout1, "Object1");
gd::WholeProjectRefactorer::GlobalObjectOrGroupRemoved( gd::WholeProjectRefactorer::GlobalObjectRemoved(
project, "GlobalObject1", /* isObjectGroup =*/false); project, "GlobalObject1");
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject( REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
"Object1") == false); "Object1") == false);
REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject( REQUIRE(externalLayout1.GetInitialInstances().HasInstancesOfObject(
@@ -1302,29 +1302,6 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(externalLayout2.GetInitialInstances().HasInstancesOfObject( REQUIRE(externalLayout2.GetInitialInstances().HasInstancesOfObject(
"GlobalObject1") == false); "GlobalObject1") == false);
} }
SECTION("Events") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &layout = project.GetLayout("Scene");
// Trigger the refactoring after removing an object
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
project, layout, "ObjectWithMyBehavior", /* isObjectGroup=*/false);
for (auto *eventsList : GetEventsListsAssociatedToScene(project)) {
// Check actions with the object in parameters have been removed.
REQUIRE(
AreActionsEmpty(eventsList->GetEvent(FreeFunctionWithObjects)));
// Check actions with the object in expressions have been removed.
REQUIRE(AreActionsEmpty(
eventsList->GetEvent(FreeFunctionWithObjectExpression)));
}
}
} }
SECTION("Object renamed (in layout)") { SECTION("Object renamed (in layout)") {
@@ -1471,30 +1448,6 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
} }
} }
SECTION("Group deleted (in layout)") {
SECTION("Events") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &layout = project.GetLayout("Scene");
// Trigger the refactoring after removing a group
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
project, layout, "GroupWithMyBehavior", /* isObjectGroup=*/true);
for (auto *eventsList : GetEventsListsAssociatedToScene(project)) {
// Check actions with the group in parameters have been removed.
REQUIRE(AreActionsEmpty(eventsList->GetEvent(FreeFunctionWithGroup)));
// Check actions with the group in expressions have been removed.
REQUIRE(AreActionsEmpty(
eventsList->GetEvent(FreeFunctionWithObjectExpressionOnGroup)));
}
}
}
SECTION("Group renamed (in layout)") { SECTION("Group renamed (in layout)") {
SECTION("Events") { SECTION("Events") {
gd::Project project; gd::Project project;
@@ -1522,6 +1475,41 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
} }
} }
SECTION("Behaviors added to an object (in layout)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &scene = project.InsertNewLayout("Scene", 0);
auto &object =
scene.InsertNewObject(project, "MyExtension::Sprite", "Object", 0);
gd::StandardEvent &event =
dynamic_cast<gd::StandardEvent &>(scene.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard"));
// Add a behavior instruction using an object that doesn't have the
// behavior.
{
gd::Instruction action;
action.SetType("MyExtension::BehaviorDoSomething");
action.SetParametersCount(3);
action.SetParameter(0, gd::Expression("Object"));
// The behavior parameter is left empty.
action.SetParameter(1, gd::Expression(""));
action.SetParameter(2, gd::Expression("0"));
event.GetActions().Insert(action);
}
// Attach the behavior to the object.
object.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior");
gd::WholeProjectRefactorer::BehaviorsAddedToObjectInLayout(project, scene,
"Object");
// The behavior parameter is now filled.
REQUIRE(event.GetActions()[0].GetParameter(1).GetPlainString() ==
"MyBehavior");
}
SECTION("Object renamed (in events function)") { SECTION("Object renamed (in events function)") {
SECTION("Group") { SECTION("Group") {
gd::Project project; gd::Project project;
@@ -1664,10 +1652,9 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// test) // test)
// Trigger the refactoring after the renaming of an object // Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction( gd::WholeProjectRefactorer::ObjectRemovedInEventsFunction(
project, eventsFunction, globalObjectsContainer, objectsContainer, project, eventsFunction, globalObjectsContainer, objectsContainer,
"Object1", "Object1");
/* isObjectGroup=*/false);
REQUIRE(objectGroup.Find("Object1") == false); REQUIRE(objectGroup.Find("Object1") == false);
REQUIRE(objectGroup.Find("Object2") == true); REQUIRE(objectGroup.Find("Object2") == true);

View File

@@ -13,6 +13,23 @@ namespace gdjs {
const getValidDimensionValue = (value: float | undefined) => const getValidDimensionValue = (value: float | undefined) =>
value === undefined ? 100 : value <= 0 ? 1 : value; value === undefined ? 100 : value <= 0 ? 1 : value;
type Object3DNetworkSyncDataType = {
// z is position on the Z axis, different from zo, which is Z order
z: number;
w: number;
h: number;
d: number;
rx: number;
ry: number;
// no need for rz, as it is the angle from gdjs.RuntimeObject
flipX: boolean;
flipY: boolean;
flipZ: boolean;
};
export type Object3DNetworkSyncData = ObjectNetworkSyncData &
Object3DNetworkSyncDataType;
/** /**
* Base class for 3D objects. * Base class for 3D objects.
*/ */
@@ -109,6 +126,39 @@ namespace gdjs {
return true; return true;
} }
getObjectNetworkSyncData(): Object3DNetworkSyncData {
return {
...super.getObjectNetworkSyncData(),
z: this.getZ(),
w: this.getWidth(),
h: this.getHeight(),
d: this.getDepth(),
rx: this.getRotationX(),
ry: this.getRotationY(),
flipX: this.isFlippedX(),
flipY: this.isFlippedY(),
flipZ: this.isFlippedZ(),
};
}
updateFromObjectNetworkSyncData(networkSyncData: Object3DNetworkSyncData) {
super.updateFromObjectNetworkSyncData(networkSyncData);
if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
if (networkSyncData.w !== undefined) this.setWidth(networkSyncData.w);
if (networkSyncData.h !== undefined) this.setHeight(networkSyncData.h);
if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
if (networkSyncData.rx !== undefined)
this.setRotationX(networkSyncData.rx);
if (networkSyncData.ry !== undefined)
this.setRotationY(networkSyncData.ry);
if (networkSyncData.flipX !== undefined)
this.flipX(networkSyncData.flipX);
if (networkSyncData.flipY !== undefined)
this.flipY(networkSyncData.flipY);
if (networkSyncData.flipZ !== undefined)
this.flipZ(networkSyncData.flipZ);
}
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) { extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
if (initialInstanceData.customSize) { if (initialInstanceData.customSize) {
this.setWidth(initialInstanceData.width); this.setWidth(initialInstanceData.width);

View File

@@ -1,4 +1,8 @@
namespace gdjs { namespace gdjs {
interface AmbientLightFilterNetworkSyncData {
i: number;
c: number;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::AmbientLight', 'Scene3D::AmbientLight',
new (class implements gdjs.PixiFiltersTools.FilterCreator { new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -86,6 +90,16 @@ namespace gdjs {
return 0; return 0;
} }
updateBooleanParameter(parameterName: string, value: boolean): void {} updateBooleanParameter(parameterName: string, value: boolean): void {}
getNetworkSyncData(): AmbientLightFilterNetworkSyncData {
return {
i: this.light.intensity,
c: this.light.color.getHex(),
};
}
updateFromNetworkSyncData(data: AmbientLightFilterNetworkSyncData) {
this.light.intensity = data.i;
this.light.color.setHex(data.c);
}
})(); })();
} }
})() })()

View File

@@ -1,4 +1,9 @@
namespace gdjs { namespace gdjs {
interface BloomFilterNetworkSyncData {
s: number;
r: number;
t: number;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::Bloom', 'Scene3D::Bloom',
new (class implements gdjs.PixiFiltersTools.FilterCreator { new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -82,6 +87,18 @@ namespace gdjs {
return 0; return 0;
} }
updateBooleanParameter(parameterName: string, value: boolean): void {} updateBooleanParameter(parameterName: string, value: boolean): void {}
getNetworkSyncData(): BloomFilterNetworkSyncData {
return {
s: this.shaderPass.strength,
r: this.shaderPass.radius,
t: this.shaderPass.threshold,
};
}
updateFromNetworkSyncData(data: BloomFilterNetworkSyncData) {
this.shaderPass.strength = data.s;
this.shaderPass.radius = data.r;
this.shaderPass.threshold = data.t;
}
})(); })();
} }
})() })()

View File

@@ -1,4 +1,8 @@
namespace gdjs { namespace gdjs {
interface BrightnessAndContrastFilterNetworkSyncData {
b: number;
c: number;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::BrightnessAndContrast', 'Scene3D::BrightnessAndContrast',
new (class implements gdjs.PixiFiltersTools.FilterCreator { new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -73,6 +77,18 @@ namespace gdjs {
return 0; return 0;
} }
updateBooleanParameter(parameterName: string, value: boolean): void {} updateBooleanParameter(parameterName: string, value: boolean): void {}
getNetworkSyncData(): BrightnessAndContrastFilterNetworkSyncData {
return {
b: this.shaderPass.uniforms.brightness.value,
c: this.shaderPass.uniforms.contrast.value,
};
}
updateFromNetworkSyncData(
data: BrightnessAndContrastFilterNetworkSyncData
) {
this.shaderPass.uniforms.brightness.value = data.b;
this.shaderPass.uniforms.contrast.value = data.c;
}
})(); })();
} }
})() })()

View File

@@ -38,6 +38,18 @@ namespace gdjs {
bottom: 5, bottom: 5,
}; };
type Cube3DObjectNetworkSyncDataType = {
fo: 'Y' | 'Z';
bfu: 'X' | 'Y';
vfb: integer;
trfb: integer;
frn: [string, string, string, string, string, string];
mt: number;
};
type Cube3DObjectNetworkSyncData = Object3DNetworkSyncData &
Cube3DObjectNetworkSyncDataType;
/** /**
* Shows a 3D box object. * Shows a 3D box object.
*/ */
@@ -402,6 +414,70 @@ namespace gdjs {
return true; return true;
} }
getObjectNetworkSyncData(): Cube3DObjectNetworkSyncData {
return {
...super.getObjectNetworkSyncData(),
mt: this._materialType,
fo: this._facesOrientation,
bfu: this._backFaceUpThroughWhichAxisRotation,
vfb: this._visibleFacesBitmask,
trfb: this._textureRepeatFacesBitmask,
frn: this._faceResourceNames,
};
}
updateFromObjectNetworkSyncData(
networkSyncData: Cube3DObjectNetworkSyncData
): void {
super.updateFromObjectNetworkSyncData(networkSyncData);
if (networkSyncData.mt !== undefined) {
this._materialType = networkSyncData.mt;
}
if (networkSyncData.fo !== undefined) {
if (this._facesOrientation !== networkSyncData.fo) {
this.setFacesOrientation(networkSyncData.fo);
}
}
if (networkSyncData.bfu !== undefined) {
if (this._backFaceUpThroughWhichAxisRotation !== networkSyncData.bfu) {
this.setBackFaceUpThroughWhichAxisRotation(networkSyncData.bfu);
}
}
if (networkSyncData.vfb !== undefined) {
// If it is different, update all the faces.
if (this._visibleFacesBitmask !== networkSyncData.vfb) {
this._visibleFacesBitmask = networkSyncData.vfb;
for (let i = 0; i < this._faceResourceNames.length; i++) {
this._renderer.updateFace(i);
}
}
}
if (networkSyncData.trfb !== undefined) {
// If it is different, update all the faces.
if (this._textureRepeatFacesBitmask !== networkSyncData.trfb) {
this._textureRepeatFacesBitmask = networkSyncData.trfb;
for (let i = 0; i < this._faceResourceNames.length; i++) {
this._renderer.updateFace(i);
}
}
}
if (networkSyncData.frn !== undefined) {
// If one element is different, update all the faces.
if (
!this._faceResourceNames.every(
(value, index) => value === networkSyncData.frn[index]
)
) {
this._faceResourceNames = networkSyncData.frn;
// Update all faces. (Could optimize to only update the changed ones)
for (let i = 0; i < this._faceResourceNames.length; i++) {
this._renderer.updateFace(i);
}
}
}
}
/** /**
* Return true if the texture transparency should be enabled. * Return true if the texture transparency should be enabled.
*/ */

View File

@@ -1,4 +1,11 @@
namespace gdjs { namespace gdjs {
interface DirectionalLightFilterNetworkSyncData {
i: number;
c: number;
e: number;
r: number;
t: string;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::DirectionalLight', 'Scene3D::DirectionalLight',
new (class implements gdjs.PixiFiltersTools.FilterCreator { new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -118,6 +125,23 @@ namespace gdjs {
this.rotationObject.rotation.z = -gdjs.toRad(this.elevation); this.rotationObject.rotation.z = -gdjs.toRad(this.elevation);
} }
} }
getNetworkSyncData(): DirectionalLightFilterNetworkSyncData {
return {
i: this.light.intensity,
c: this.light.color.getHex(),
e: this.elevation,
r: this.rotation,
t: this.top,
};
}
updateFromNetworkSyncData(syncData: any): void {
this.light.intensity = syncData.i;
this.light.color.setHex(syncData.c);
this.elevation = syncData.e;
this.rotation = syncData.r;
this.top = syncData.t;
this.updateRotation();
}
})(); })();
} }
})() })()

View File

@@ -1,4 +1,8 @@
namespace gdjs { namespace gdjs {
interface ExponentialFogFilterNetworkSyncData {
d: number;
c: number;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::ExponentialFog', 'Scene3D::ExponentialFog',
new (class implements gdjs.PixiFiltersTools.FilterCreator { new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -83,6 +87,18 @@ namespace gdjs {
return 0; return 0;
} }
updateBooleanParameter(parameterName: string, value: boolean): void {} updateBooleanParameter(parameterName: string, value: boolean): void {}
getNetworkSyncData(): ExponentialFogFilterNetworkSyncData {
return {
d: this.fog.density,
c: this.fog.color.getHex(),
};
}
updateFromNetworkSyncData(
syncData: ExponentialFogFilterNetworkSyncData
): void {
this.fog.density = syncData.d;
this.fog.color.setHex(syncData.c);
}
})(); })();
} }
})() })()

View File

@@ -1,4 +1,7 @@
namespace gdjs { namespace gdjs {
interface ExposureFilterNetworkSyncData {
e: number;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::Exposure', 'Scene3D::Exposure',
new (class implements gdjs.PixiFiltersTools.FilterCreator { new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -67,6 +70,14 @@ namespace gdjs {
return 0; return 0;
} }
updateBooleanParameter(parameterName: string, value: boolean): void {} updateBooleanParameter(parameterName: string, value: boolean): void {}
getNetworkSyncData(): ExposureFilterNetworkSyncData {
return { e: this.shaderPass.uniforms.exposure.value };
}
updateFromNetworkSyncData(
syncData: ExposureFilterNetworkSyncData
): void {
this.shaderPass.uniforms.exposure.value = syncData.e;
}
})(); })();
} }
})() })()

View File

@@ -1,4 +1,12 @@
namespace gdjs { namespace gdjs {
interface HemisphereLightFilterNetworkSyncData {
i: number;
sc: number;
gc: number;
e: number;
r: number;
t: string;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::HemisphereLight', 'Scene3D::HemisphereLight',
new (class implements gdjs.PixiFiltersTools.FilterCreator { new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -129,6 +137,27 @@ namespace gdjs {
this.rotationObject.rotation.z = -gdjs.toRad(this.elevation); this.rotationObject.rotation.z = -gdjs.toRad(this.elevation);
} }
} }
getNetworkSyncData(): HemisphereLightFilterNetworkSyncData {
return {
i: this.light.intensity,
sc: this.light.color.getHex(),
gc: this.light.groundColor.getHex(),
e: this.elevation,
r: this.rotation,
t: this.top,
};
}
updateFromNetworkSyncData(
syncData: HemisphereLightFilterNetworkSyncData
): void {
this.light.intensity = syncData.i;
this.light.color.setHex(syncData.sc);
this.light.groundColor.setHex(syncData.gc);
this.elevation = syncData.e;
this.rotation = syncData.r;
this.top = syncData.t;
this.updateRotation();
}
})(); })();
} }
})() })()

View File

@@ -1,4 +1,8 @@
namespace gdjs { namespace gdjs {
interface HueAndSaturationFilterExtra {
h: number;
s: number;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::HueAndSaturation', 'Scene3D::HueAndSaturation',
new (class implements gdjs.PixiFiltersTools.FilterCreator { new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -73,6 +77,18 @@ namespace gdjs {
return 0; return 0;
} }
updateBooleanParameter(parameterName: string, value: boolean): void {} updateBooleanParameter(parameterName: string, value: boolean): void {}
getNetworkSyncData(): HueAndSaturationFilterExtra {
return {
h: this.shaderPass.uniforms.hue.value,
s: this.shaderPass.uniforms.saturation.value,
};
}
updateFromNetworkSyncData(
syncData: HueAndSaturationFilterExtra
): void {
this.shaderPass.uniforms.hue.value = syncData.h;
this.shaderPass.uniforms.saturation.value = syncData.s;
}
})(); })();
} }
})() })()

View File

@@ -3134,14 +3134,51 @@ module.exports = {
modelDepth < epsilon modelDepth < epsilon
? Number.POSITIVE_INFINITY ? Number.POSITIVE_INFINITY
: originalDepth / modelDepth; : originalDepth / modelDepth;
let scaleRatio = Math.min(widthRatio, heightRatio, depthRatio); const minScaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
if (!Number.isFinite(scaleRatio)) { if (!Number.isFinite(minScaleRatio)) {
scaleRatio = 1; this._defaultWidth = modelWidth;
} this._defaultHeight = modelHeight;
this._defaultDepth = modelDepth;
} else {
if (widthRatio === minScaleRatio) {
this._defaultWidth = originalWidth;
this._defaultHeight = Rendered3DInstance.applyRatio({
oldReferenceValue: modelWidth,
newReferenceValue: originalWidth,
valueToApplyTo: modelHeight,
});
this._defaultDepth = Rendered3DInstance.applyRatio({
oldReferenceValue: modelWidth,
newReferenceValue: originalWidth,
valueToApplyTo: modelDepth,
});
} else if (heightRatio === minScaleRatio) {
this._defaultWidth = Rendered3DInstance.applyRatio({
oldReferenceValue: modelHeight,
newReferenceValue: originalHeight,
valueToApplyTo: modelWidth,
});
this._defaultWidth = scaleRatio * modelWidth; this._defaultHeight = originalHeight;
this._defaultHeight = scaleRatio * modelHeight; this._defaultDepth = Rendered3DInstance.applyRatio({
this._defaultDepth = scaleRatio * modelDepth; oldReferenceValue: modelHeight,
newReferenceValue: originalHeight,
valueToApplyTo: modelDepth,
});
} else {
this._defaultWidth = Rendered3DInstance.applyRatio({
oldReferenceValue: modelDepth,
newReferenceValue: originalDepth,
valueToApplyTo: modelWidth,
});
this._defaultHeight = Rendered3DInstance.applyRatio({
oldReferenceValue: modelDepth,
newReferenceValue: originalDepth,
valueToApplyTo: modelHeight,
});
this._defaultDepth = originalDepth;
}
}
} }
} }

View File

@@ -1,4 +1,9 @@
namespace gdjs { namespace gdjs {
interface LinearFogFilterNetworkSyncData {
n: number;
f: number;
c: number;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::LinearFog', 'Scene3D::LinearFog',
new (class implements gdjs.PixiFiltersTools.FilterCreator { new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -87,6 +92,20 @@ namespace gdjs {
return 0; return 0;
} }
updateBooleanParameter(parameterName: string, value: boolean): void {} updateBooleanParameter(parameterName: string, value: boolean): void {}
getNetworkSyncData(): LinearFogFilterNetworkSyncData {
return {
n: this.fog.near,
f: this.fog.far,
c: this.fog.color.getHex(),
};
}
updateFromNetworkSyncData(
data: LinearFogFilterNetworkSyncData
): void {
this.fog.near = data.n;
this.fog.far = data.f;
this.fog.color.setHex(data.c);
}
})(); })();
} }
})() })()

View File

@@ -1,6 +1,19 @@
namespace gdjs { namespace gdjs {
type Model3DAnimation = { name: string; source: string; loop: boolean }; type Model3DAnimation = { name: string; source: string; loop: boolean };
type Model3DObjectNetworkSyncDataType = {
mt: number;
op: FloatPoint3D | null;
cp: FloatPoint3D | null;
anis: Model3DAnimation[];
ai: integer;
ass: float;
ap: boolean;
};
type Model3DObjectNetworkSyncData = Object3DNetworkSyncData &
Model3DObjectNetworkSyncDataType;
/** Base parameters for {@link gdjs.Cube3DRuntimeObject} */ /** Base parameters for {@link gdjs.Cube3DRuntimeObject} */
export interface Model3DObjectData extends Object3DData { export interface Model3DObjectData extends Object3DData {
/** The base parameters of the Model3D object */ /** The base parameters of the Model3D object */
@@ -162,6 +175,49 @@ namespace gdjs {
return true; return true;
} }
getObjectNetworkSyncData(): Model3DObjectNetworkSyncData {
return {
...super.getObjectNetworkSyncData(),
mt: this._materialType,
op: this._originPoint,
cp: this._centerPoint,
anis: this._animations,
ai: this._currentAnimationIndex,
ass: this._animationSpeedScale,
ap: this._animationPaused,
};
}
updateFromObjectNetworkSyncData(
networkSyncData: Model3DObjectNetworkSyncData
): void {
super.updateFromObjectNetworkSyncData(networkSyncData);
if (networkSyncData.mt !== undefined) {
this._materialType = networkSyncData.mt;
}
if (networkSyncData.op !== undefined) {
this._originPoint = networkSyncData.op;
}
if (networkSyncData.cp !== undefined) {
this._centerPoint = networkSyncData.cp;
}
if (networkSyncData.anis !== undefined) {
this._animations = networkSyncData.anis;
}
if (networkSyncData.ai !== undefined) {
this.setAnimationIndex(networkSyncData.ai);
}
if (networkSyncData.ass !== undefined) {
this.setAnimationSpeedScale(networkSyncData.ass);
}
if (networkSyncData.ap !== undefined) {
if (networkSyncData.ap !== this.isAnimationPaused()) {
networkSyncData.ap ? this.pauseAnimation() : this.resumeAnimation();
}
}
}
_updateModel(objectData: Model3DObjectData) { _updateModel(objectData: Model3DObjectData) {
const rotationX = objectData.content.rotationX || 0; const rotationX = objectData.content.rotationX || 0;
const rotationY = objectData.content.rotationY || 0; const rotationY = objectData.content.rotationY || 0;

View File

@@ -6,31 +6,34 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
const anchorBehaviorName = 'Anchor'; const anchorBehaviorName = 'Anchor';
const runtimeScene = new gdjs.RuntimeScene(runtimeGame); const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({ runtimeScene.loadFromScene({
layers: [ sceneData: {
{ layers: [
name: '', {
visibility: true, name: '',
cameras: [], visibility: true,
effects: [], cameras: [],
ambientLightColorR: 127, effects: [],
ambientLightColorB: 127, ambientLightColorR: 127,
ambientLightColorG: 127, ambientLightColorB: 127,
isLightingLayer: false, ambientLightColorG: 127,
followBaseLayerCamera: false, isLightingLayer: false,
}, followBaseLayerCamera: false,
], },
variables: [], ],
r: 0, variables: [],
v: 0, r: 0,
b: 0, v: 0,
mangledName: 'Scene1', b: 0,
name: 'Scene1', mangledName: 'Scene1',
stopSoundsOnStartup: false, name: 'Scene1',
title: '', stopSoundsOnStartup: false,
behaviorsSharedData: [], title: '',
objects: [], behaviorsSharedData: [],
instances: [], objects: [],
usedResources: [], instances: [],
usedResources: [],
},
usedExtensionsWithVariablesData: [],
}); });
function createObject(behaviorProperties) { function createObject(behaviorProperties) {

View File

@@ -23,6 +23,21 @@ namespace gdjs {
}; };
export type BBTextObjectData = ObjectData & BBTextObjectDataType; export type BBTextObjectData = ObjectData & BBTextObjectDataType;
export type BBTextObjectNetworkSyncDataType = {
text: string;
o: float;
c: number[];
ff: string;
fs: number;
wwrap: boolean;
wwidth: float;
align: string;
hidden: boolean;
};
export type BBTextObjectNetworkSyncData = ObjectNetworkSyncData &
BBTextObjectNetworkSyncDataType;
/** /**
* Displays a rich text using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows). * Displays a rich text using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows).
*/ */
@@ -116,6 +131,55 @@ namespace gdjs {
return true; return true;
} }
getObjectNetworkSyncData(): BBTextObjectNetworkSyncData {
return {
...super.getObjectNetworkSyncData(),
text: this._text,
o: this._opacity,
c: this._color,
ff: this._fontFamily,
fs: this._fontSize,
wwrap: this._wordWrap,
wwidth: this._wrappingWidth,
align: this._align,
hidden: this.hidden,
};
}
updateFromObjectNetworkSyncData(
networkSyncData: BBTextObjectNetworkSyncData
): void {
super.updateFromObjectNetworkSyncData(networkSyncData);
if (this._text !== undefined) {
this.setBBText(networkSyncData.text);
}
if (this._opacity !== undefined) {
this.setOpacity(networkSyncData.o);
}
if (this._color !== undefined) {
this._color = networkSyncData.c;
this._renderer.updateColor();
}
if (this._fontFamily !== undefined) {
this.setFontFamily(networkSyncData.ff);
}
if (this._fontSize !== undefined) {
this.setFontSize(networkSyncData.fs);
}
if (this._wordWrap !== undefined) {
this.setWordWrap(networkSyncData.wwrap);
}
if (this._wrappingWidth !== undefined) {
this.setWrappingWidth(networkSyncData.wwidth);
}
if (this._align !== undefined) {
this.setAlignment(networkSyncData.align);
}
if (this.hidden !== undefined) {
this.hide(networkSyncData.hidden);
}
}
/** /**
* Initialize the extra parameters that could be set for an instance. * Initialize the extra parameters that could be set for an instance.
*/ */

View File

@@ -25,6 +25,21 @@ namespace gdjs {
}; };
export type BitmapTextObjectData = ObjectData & BitmapTextObjectDataType; export type BitmapTextObjectData = ObjectData & BitmapTextObjectDataType;
export type BitmapTextObjectNetworkSyncDataType = {
text: string;
opa: float;
tint: number[];
bfrn: string;
tarn: string;
scale: number;
wwrap: boolean;
wwidth: float;
align: string;
};
export type BitmapTextObjectNetworkSyncData = ObjectNetworkSyncData &
BitmapTextObjectNetworkSyncDataType;
/** /**
* Displays a text using a "Bitmap Font", generated in a external editor like bmFont. * Displays a text using a "Bitmap Font", generated in a external editor like bmFont.
* This is more efficient/faster to render than a traditional text (which needs * This is more efficient/faster to render than a traditional text (which needs
@@ -132,6 +147,55 @@ namespace gdjs {
return true; return true;
} }
getObjectNetworkSyncData(): BitmapTextObjectNetworkSyncData {
return {
...super.getObjectNetworkSyncData(),
text: this._text,
opa: this._opacity,
tint: this._tint,
bfrn: this._bitmapFontResourceName,
tarn: this._textureAtlasResourceName,
scale: this.getScale(),
wwrap: this._wordWrap,
wwidth: this._wrappingWidth,
align: this._align,
};
}
updateFromObjectNetworkSyncData(
networkSyncData: BitmapTextObjectNetworkSyncData
): void {
super.updateFromObjectNetworkSyncData(networkSyncData);
if (this._text !== undefined) {
this.setText(networkSyncData.text);
}
if (this._opacity !== undefined) {
this.setOpacity(networkSyncData.opa);
}
if (this._tint !== undefined) {
this._tint = networkSyncData.tint;
this._renderer.updateTint();
}
if (this._bitmapFontResourceName !== undefined) {
this.setBitmapFontResourceName(networkSyncData.bfrn);
}
if (this._textureAtlasResourceName !== undefined) {
this.setTextureAtlasResourceName(networkSyncData.tarn);
}
if (this._scaleX !== undefined) {
this.setScale(networkSyncData.scale);
}
if (this._wordWrap !== undefined) {
this.setWordWrap(networkSyncData.wwrap);
}
if (this._wrappingWidth !== undefined) {
this.setWrappingWidth(networkSyncData.wwidth);
}
if (this._align !== undefined) {
this.setAlignment(networkSyncData.align);
}
}
/** /**
* Initialize the extra parameters that could be set for an instance. * Initialize the extra parameters that could be set for an instance.
*/ */

View File

@@ -5,8 +5,7 @@ Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
namespace gdjs { namespace gdjs {
/** /**
* The DestroyOutsideRuntimeBehavior represents a behavior allowing objects to be * The DestroyOutsideRuntimeBehavior represents a behavior that destroys the object when it leaves the screen.
* moved using the mouse.
*/ */
export class DestroyOutsideRuntimeBehavior extends gdjs.RuntimeBehavior { export class DestroyOutsideRuntimeBehavior extends gdjs.RuntimeBehavior {
_extraBorder: any; _extraBorder: any;

View File

@@ -3,31 +3,34 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
const runtimeGame = gdjs.getPixiRuntimeGame(); const runtimeGame = gdjs.getPixiRuntimeGame();
var runtimeScene = new gdjs.RuntimeScene(runtimeGame); var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({ runtimeScene.loadFromScene({
layers: [ sceneData: {
{ layers: [
name: '', {
visibility: true, name: '',
cameras: [], visibility: true,
effects: [], cameras: [],
ambientLightColorR: 127, effects: [],
ambientLightColorB: 127, ambientLightColorR: 127,
ambientLightColorG: 127, ambientLightColorB: 127,
isLightingLayer: false, ambientLightColorG: 127,
followBaseLayerCamera: false, isLightingLayer: false,
}, followBaseLayerCamera: false,
], },
variables: [], ],
r: 0, variables: [],
v: 0, r: 0,
b: 0, v: 0,
mangledName: 'Scene1', b: 0,
name: 'Scene1', mangledName: 'Scene1',
stopSoundsOnStartup: false, name: 'Scene1',
title: '', stopSoundsOnStartup: false,
behaviorsSharedData: [], title: '',
objects: [], behaviorsSharedData: [],
instances: [], objects: [],
usedResources: [], instances: [],
usedResources: [],
},
usedExtensionsWithVariablesData: [],
}); });
var object = new gdjs.TestRuntimeObject(runtimeScene, { var object = new gdjs.TestRuntimeObject(runtimeScene, {

View File

@@ -1,4 +1,14 @@
namespace gdjs { namespace gdjs {
interface AdjustmentFilterNetworkSyncData {
ga: number;
sa: number;
co: number;
br: number;
r: number;
g: number;
b: number;
a: number;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'Adjustment', 'Adjustment',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator { new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -77,6 +87,33 @@ namespace gdjs {
parameterName: string, parameterName: string,
value: boolean value: boolean
) {} ) {}
getNetworkSyncData(filter: PIXI.Filter): AdjustmentFilterNetworkSyncData {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
return {
ga: adjustmentFilter.gamma,
sa: adjustmentFilter.saturation,
co: adjustmentFilter.contrast,
br: adjustmentFilter.brightness,
r: adjustmentFilter.red,
g: adjustmentFilter.green,
b: adjustmentFilter.blue,
a: adjustmentFilter.alpha,
};
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
data: AdjustmentFilterNetworkSyncData
): void {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
adjustmentFilter.gamma = data.ga;
adjustmentFilter.saturation = data.sa;
adjustmentFilter.contrast = data.co;
adjustmentFilter.brightness = data.br;
adjustmentFilter.red = data.r;
adjustmentFilter.green = data.g;
adjustmentFilter.blue = data.b;
adjustmentFilter.alpha = data.a;
}
})() })()
); );
} }

View File

@@ -1,4 +1,12 @@
namespace gdjs { namespace gdjs {
interface AdvancedBloomFilterNetworkSyncData {
th: number;
bs: number;
bn: number;
b: number;
q: number;
p: number;
}
gdjs.PixiFiltersTools.registerFilterCreator( gdjs.PixiFiltersTools.registerFilterCreator(
'AdvancedBloom', 'AdvancedBloom',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator { new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
@@ -67,6 +75,31 @@ namespace gdjs {
parameterName: string, parameterName: string,
value: boolean value: boolean
) {} ) {}
getNetworkSyncData(
filter: PIXI.Filter
): AdvancedBloomFilterNetworkSyncData {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
return {
th: advancedBloomFilter.threshold,
bs: advancedBloomFilter.bloomScale,
bn: advancedBloomFilter.brightness,
b: advancedBloomFilter.blur,
q: advancedBloomFilter.quality,
p: advancedBloomFilter.padding,
};
}
updateFromNetworkSyncData(
filter: PIXI.Filter,
syncData: AdvancedBloomFilterNetworkSyncData
) {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
advancedBloomFilter.threshold = syncData.th;
advancedBloomFilter.bloomScale = syncData.bs;
advancedBloomFilter.brightness = syncData.bn;
advancedBloomFilter.blur = syncData.b;
advancedBloomFilter.quality = syncData.q;
advancedBloomFilter.padding = syncData.p;
}
})() })()
); );
} }

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