Compare commits

...

75 Commits

Author SHA1 Message Date
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
github-actions[bot]
5c66623631 Update translations [skip ci] (#6487)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2024-03-28 12:07:38 +01:00
AlexandreS
5637642e1b Compute max discount instead of using hardcoded value (#6488)
Don't show in changelog
2024-03-28 11:05:13 +01:00
Florian Rival
7e8b44af2e Fix expressions involving a variable (or property) of type number/string wrongly rejected when an operator like + was used after them (#6467)
* For example, "Your score is " + NumberVariable + " points" was rejected, because the second operator type was not properly inferred.
* If something does not work in your game anymore, double check if your expressions in the events sheets are not underlined in red. Sometimes, adding `ToString(` or `ToNumber(` around it might be required.
2024-03-28 09:42:41 +01:00
Clément Pasteau
0dd4650aae Bump to 5.3.196 (#6486)
Do not show in changelog
2024-03-28 09:35:22 +01:00
github-actions[bot]
c7cac31830 Update translations [skip ci] (#6457)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2024-03-28 08:57:13 +01:00
Clément Pasteau
56cb8581c4 Fix plugin-consent version for admob (#6485) 2024-03-27 19:09:42 +01:00
Florian Rival
1993040b70 Add community leaderboard to highlight members doing great feedbacks (#6484) 2024-03-27 18:47:14 +01:00
AlexandreS
883991081a Define app theme related CSS variables at HTML body level (#6483)
Only show in developer changelog
2024-03-27 17:48:19 +01:00
D8H
7d8afef1ad Fix collision mask of rotated Spine objects (#6482)
- Fixes rotation of Spine objects in the editor.
2024-03-27 16:35:55 +01:00
Florian Rival
8178595546 Use a CSS module for SimpleTextField, to avoid name clashes (#6481)
Only show in developer changelog
2024-03-27 12:06:53 +01:00
AlexandreS
a478068c64 Add possibility to subscribe to GDevelop on a yearly basis (#6462) 2024-03-27 11:26:44 +01:00
Tristan Rhodes
368da1b610 New action for Physics behavior: Set the linear velocity towards an angle (#5670) 2024-03-27 08:27:00 +01:00
D8H
4ee43202e9 Fix sprite with resource tests (#6479)
- Don't show in changelog
2024-03-26 15:34:31 +01:00
Florian Rival
602fdf4bfd Fix warning
Don't show in changelog
2024-03-26 11:40:16 +01:00
Florian Rival
6110acafcc Add buttons to rate the quality of feedbacks (#6478)
* When you receive a feedback on a game, go to the Game Dashboard (or check the email notification for the feedback) to rank the comment as great, good or not useful. If a comment is abusive, spammy or harmful, you can also report it as such.
* Users making the best comments will be showcased in a leaderboard on the community page and on gd.games
* As a game creator, you're also benefit from this: games with the most rated feedbacks will be displayed in a leaderboard and on gd.games. We encourage you to take the time to rate the feedbacks you're receiving so your game becomes more visible to the community!
2024-03-26 11:28:45 +01:00
D8H
a3696ca9d1 Allow custom objects to use animations (#6426) 2024-03-25 10:47:28 +01:00
Clément Pasteau
1bb473b0b0 Display questions when canceling a subscription (#6471) 2024-03-22 14:07:59 +01:00
Florian Rival
4376b4f36e Improve first screen layouts (#6468) 2024-03-22 10:33:42 +01:00
Florian Rival
6ecbae9c35 Display the coordinates of the center point of a Sprite even when set automatically
Fix #6472
2024-03-22 10:33:05 +01:00
D8H
93e9fc6aed Fix missing expressions for text object (#6470) 2024-03-21 10:30:25 +01:00
Clément Pasteau
7c0a7a4152 Game templates in changelog (#6458)
Do not show in changelog
2024-03-14 16:42:38 +01:00
Clément Pasteau
d4cdb3ff83 Bump to 5.3.195 (#6455) 2024-03-14 16:20:15 +01:00
AlexandreS
a7cac91e45 Fix warning about end of subscription (#6456)
Do not show in changelog
2024-03-14 15:49:09 +01:00
D8H
6a6d72cd9a Fix games from crashing when a scene contains a wrapped text object with a big font size (#6453) 2024-03-14 15:36:34 +01:00
github-actions[bot]
11d74b3ea5 Update translations [skip ci] (#6442)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2024-03-14 15:31:27 +01:00
Clément Pasteau
5b65cf84eb Fix survey mobile (#6454)
Do not show in changelog
2024-03-14 15:00:57 +01:00
Clément Pasteau
6b7af0474f Improve Guided lessons (#6446)
* Lessons have been slightly reworked overall
* Most languages are now supported
* A new option allows to fill a value automatically with a button, speeding up the typing part
2024-03-14 14:40:12 +01:00
D8H
99f7e55044 Fix the object effect color tween action (#6452) 2024-03-14 14:13:28 +01:00
AlexandreS
6b522b1c31 Add support for game sessions achievements notifications (#6449) 2024-03-14 12:18:42 +01:00
D8H
0f35e48690 Allow to change text of custom objects like any text object (#6450) 2024-03-14 11:50:55 +01:00
D8H
53d19dd628 Fix custom object capability changes to be applied right away (#6447) 2024-03-14 10:49:33 +01:00
Florian Rival
155dc1bec8 Display redesigned subscription plans (#6440)
Don't show in changelog
2024-03-14 10:45:29 +01:00
D8H
be26e39eae Fix 3D custom objects CenterZ expression and condition (#6448) 2024-03-13 14:07:24 +01:00
TRP
90fa5ea8e8 Add action to draw a fillet rectangle with Shape painter (rounded rectangle with inverted corners) (#6433) 2024-03-12 16:11:02 +01:00
Florian Rival
4845251f78 Fix broken editor panels drag'n'drop (#6444) 2024-03-11 11:34:27 +01:00
Arthur Pacaud (arthuro555)
31ef3fec58 Add TypeScript type checking to JsExtension.js files (#6321) 2024-03-09 17:22:12 +01:00
580 changed files with 24708 additions and 8435 deletions

View File

@@ -56,6 +56,7 @@ blocks:
- name: GDJS typing and documentation generation
commands:
- checkout
- cache restore newIDE-app-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum newIDE/app/package-lock.json)
- cache restore GDJS-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum GDJS/package-lock.json)
- cache restore GDJS-tests-node_modules-$SEMAPHORE_GIT_BRANCH-revision-$(checksum GDJS/tests/package-lock.json)
- cd GDJS

1
.vscode/tasks.json vendored
View File

@@ -8,6 +8,7 @@
"group": "build",
"label": "Start development server",
"detail": "Starts the GDevelop development server.",
"options": { "env": { "NODE_OPTIONS": "--max-old-space-size=8192" } },
"problemMatcher": [
{
"owner": "cra",

View File

@@ -6,8 +6,6 @@
#include "GDCore/Extensions/Builtin/SpriteExtension/Animation.h"
#include <vector>
#include "GDCore/Extensions/Builtin/SpriteExtension/Direction.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/Sprite.h"
#include "GDCore/String.h"
namespace gd {

View File

@@ -4,13 +4,11 @@
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_ANIMATION_H
#define GDCORE_ANIMATION_H
#pragma once
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Direction;
}
#include "GDCore/Extensions/Builtin/SpriteExtension/Direction.h"
namespace gd {
@@ -93,4 +91,3 @@ class GD_CORE_API Animation {
};
} // namespace gd
#endif // GDCORE_ANIMATION_H

View File

@@ -3,12 +3,12 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_DIRECTION_H
#define GDCORE_DIRECTION_H
#pragma once
#include <vector>
#include "GDCore/String.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/Sprite.h"
namespace gd {
class Sprite;
class SerializerElement;
}
@@ -142,4 +142,3 @@ class GD_CORE_API Direction {
};
} // namespace gd
#endif // GDCORE_DIRECTION_H

View File

@@ -0,0 +1,157 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteAnimationList.h"
#include <algorithm>
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/Animation.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/Direction.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/InitialInstance.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
Animation SpriteAnimationList::badAnimation;
SpriteAnimationList::SpriteAnimationList()
: adaptCollisionMaskAutomatically(true) {}
SpriteAnimationList::~SpriteAnimationList(){};
void SpriteAnimationList::UnserializeFrom(const gd::SerializerElement& element) {
adaptCollisionMaskAutomatically =
element.GetBoolAttribute("adaptCollisionMaskAutomatically", false);
RemoveAllAnimations();
const gd::SerializerElement& animationsElement =
element.GetChild("animations", 0, "Animations");
animationsElement.ConsiderAsArrayOf("animation", "Animation");
for (std::size_t i = 0; i < animationsElement.GetChildrenCount(); ++i) {
const gd::SerializerElement& animationElement =
animationsElement.GetChild(i);
Animation newAnimation;
newAnimation.useMultipleDirections = animationElement.GetBoolAttribute(
"useMultipleDirections", false, "typeNormal");
newAnimation.SetName(animationElement.GetStringAttribute("name", ""));
// Compatibility with GD <= 3.3
if (animationElement.HasChild("Direction")) {
for (std::size_t j = 0;
j < animationElement.GetChildrenCount("Direction");
++j) {
Direction direction;
direction.UnserializeFrom(animationElement.GetChild("Direction", j));
newAnimation.SetDirectionsCount(newAnimation.GetDirectionsCount() + 1);
newAnimation.SetDirection(direction,
newAnimation.GetDirectionsCount() - 1);
}
}
// End of compatibility code
else {
const gd::SerializerElement& directionsElement =
animationElement.GetChild("directions");
directionsElement.ConsiderAsArrayOf("direction");
for (std::size_t j = 0; j < directionsElement.GetChildrenCount(); ++j) {
Direction direction;
direction.UnserializeFrom(directionsElement.GetChild(j));
newAnimation.SetDirectionsCount(newAnimation.GetDirectionsCount() + 1);
newAnimation.SetDirection(direction,
newAnimation.GetDirectionsCount() - 1);
}
}
AddAnimation(newAnimation);
}
}
void SpriteAnimationList::SerializeTo(gd::SerializerElement& element) const {
element.SetAttribute("adaptCollisionMaskAutomatically",
adaptCollisionMaskAutomatically);
// Animations
gd::SerializerElement& animationsElement = element.AddChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
for (std::size_t k = 0; k < GetAnimationsCount(); k++) {
gd::SerializerElement& animationElement =
animationsElement.AddChild("animation");
animationElement.SetAttribute("useMultipleDirections",
GetAnimation(k).useMultipleDirections);
animationElement.SetAttribute("name", GetAnimation(k).GetName());
gd::SerializerElement& directionsElement =
animationElement.AddChild("directions");
directionsElement.ConsiderAsArrayOf("direction");
for (std::size_t l = 0; l < GetAnimation(k).GetDirectionsCount(); l++) {
GetAnimation(k).GetDirection(l).SerializeTo(
directionsElement.AddChild("direction"));
}
}
}
void SpriteAnimationList::ExposeResources(gd::ArbitraryResourceWorker& worker) {
for (std::size_t j = 0; j < GetAnimationsCount(); j++) {
for (std::size_t k = 0; k < GetAnimation(j).GetDirectionsCount(); k++) {
for (std::size_t l = 0;
l < GetAnimation(j).GetDirection(k).GetSpritesCount();
l++) {
worker.ExposeImage(
GetAnimation(j).GetDirection(k).GetSprite(l).GetImageName());
}
}
}
}
const Animation& SpriteAnimationList::GetAnimation(std::size_t nb) const {
if (nb >= animations.size()) return badAnimation;
return animations[nb];
}
Animation& SpriteAnimationList::GetAnimation(std::size_t nb) {
if (nb >= animations.size()) return badAnimation;
return animations[nb];
}
void SpriteAnimationList::AddAnimation(const Animation& animation) {
animations.push_back(animation);
}
bool SpriteAnimationList::RemoveAnimation(std::size_t nb) {
if (nb >= GetAnimationsCount()) return false;
animations.erase(animations.begin() + nb);
return true;
}
void SpriteAnimationList::SwapAnimations(std::size_t firstIndex,
std::size_t secondIndex) {
if (firstIndex < animations.size() && secondIndex < animations.size() &&
firstIndex != secondIndex)
std::swap(animations[firstIndex], animations[secondIndex]);
}
void SpriteAnimationList::MoveAnimation(std::size_t oldIndex, std::size_t newIndex) {
if (oldIndex >= animations.size() || newIndex >= animations.size()) return;
auto animation = animations[oldIndex];
animations.erase(animations.begin() + oldIndex);
animations.insert(animations.begin() + newIndex, animation);
}
} // namespace gd

View File

@@ -0,0 +1,119 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/Extensions/Builtin/SpriteExtension/Animation.h"
namespace gd {
class InitialInstance;
class SerializerElement;
class PropertyDescriptor;
class ArbitraryResourceWorker;
} // namespace gd
namespace gd {
/**
* \brief A list of animations, containing directions with images and collision mask.
*
* It's used in the configuration of object that implements image-based animations.
*
* \see Animation
* \see Direction
* \see Sprite
* \ingroup SpriteObjectExtension
*/
class GD_CORE_API SpriteAnimationList {
public:
SpriteAnimationList();
virtual ~SpriteAnimationList();
void ExposeResources(gd::ArbitraryResourceWorker& worker);
/**
* \brief Return the animation at the specified index.
* If the index is out of bound, a "bad animation" object is returned.
*/
const Animation& GetAnimation(std::size_t nb) const;
/**
* \brief Return the animation at the specified index.
* If the index is out of bound, a "bad animation" object is returned.
*/
Animation& GetAnimation(std::size_t nb);
/**
* \brief Return the number of animations this object has.
*/
std::size_t GetAnimationsCount() const { return animations.size(); };
/**
* \brief Add an animation at the end of the existing ones.
*/
void AddAnimation(const Animation& animation);
/**
* \brief Remove an animation.
*/
bool RemoveAnimation(std::size_t nb);
/**
* \brief Remove all animations.
*/
void RemoveAllAnimations() { animations.clear(); }
/**
* \brief Return true if the object hasn't any animation.
*/
bool HasNoAnimations() const { return animations.empty(); }
/**
* \brief Swap the position of two animations
*/
void SwapAnimations(std::size_t firstIndex, std::size_t secondIndex);
/**
* \brief Change the position of the specified animation
*/
void MoveAnimation(std::size_t oldIndex, std::size_t newIndex);
/**
* \brief Return a read-only reference to the vector containing all the
* animation of the object.
*/
const std::vector<Animation>& GetAllAnimations() const { return animations; }
/**
* @brief Check if the collision mask adapts automatically to the animation.
*/
bool AdaptCollisionMaskAutomatically() const {
return adaptCollisionMaskAutomatically;
}
/**
* @brief Set if the collision mask adapts automatically to the animation.
*/
void SetAdaptCollisionMaskAutomatically(bool enable) {
adaptCollisionMaskAutomatically = enable;
}
void UnserializeFrom(const gd::SerializerElement& element);
void SerializeTo(gd::SerializerElement& element) const;
private:
mutable std::vector<Animation> animations;
static Animation badAnimation; //< Bad animation when an out of bound
// animation is requested.
bool adaptCollisionMaskAutomatically; ///< If set to true, the collision
///< mask will be automatically
///< adapted to the animation of the
///< object.
};
} // namespace gd

View File

@@ -23,88 +23,20 @@
namespace gd {
Animation SpriteObject::badAnimation;
SpriteObject::SpriteObject()
: updateIfNotVisible(false), adaptCollisionMaskAutomatically(true) {}
: updateIfNotVisible(false) {}
SpriteObject::~SpriteObject(){};
void SpriteObject::DoUnserializeFrom(gd::Project& project,
const gd::SerializerElement& element) {
updateIfNotVisible = element.GetBoolAttribute("updateIfNotVisible", true);
adaptCollisionMaskAutomatically =
element.GetBoolAttribute("adaptCollisionMaskAutomatically", false);
RemoveAllAnimations();
const gd::SerializerElement& animationsElement =
element.GetChild("animations", 0, "Animations");
animationsElement.ConsiderAsArrayOf("animation", "Animation");
for (std::size_t i = 0; i < animationsElement.GetChildrenCount(); ++i) {
const gd::SerializerElement& animationElement =
animationsElement.GetChild(i);
Animation newAnimation;
newAnimation.useMultipleDirections = animationElement.GetBoolAttribute(
"useMultipleDirections", false, "typeNormal");
newAnimation.SetName(animationElement.GetStringAttribute("name", ""));
// Compatibility with GD <= 3.3
if (animationElement.HasChild("Direction")) {
for (std::size_t j = 0;
j < animationElement.GetChildrenCount("Direction");
++j) {
Direction direction;
direction.UnserializeFrom(animationElement.GetChild("Direction", j));
newAnimation.SetDirectionsCount(newAnimation.GetDirectionsCount() + 1);
newAnimation.SetDirection(direction,
newAnimation.GetDirectionsCount() - 1);
}
}
// End of compatibility code
else {
const gd::SerializerElement& directionsElement =
animationElement.GetChild("directions");
directionsElement.ConsiderAsArrayOf("direction");
for (std::size_t j = 0; j < directionsElement.GetChildrenCount(); ++j) {
Direction direction;
direction.UnserializeFrom(directionsElement.GetChild(j));
newAnimation.SetDirectionsCount(newAnimation.GetDirectionsCount() + 1);
newAnimation.SetDirection(direction,
newAnimation.GetDirectionsCount() - 1);
}
}
AddAnimation(newAnimation);
}
animations.UnserializeFrom(element);
}
void SpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
element.SetAttribute("updateIfNotVisible", updateIfNotVisible);
element.SetAttribute("adaptCollisionMaskAutomatically",
adaptCollisionMaskAutomatically);
// Animations
gd::SerializerElement& animationsElement = element.AddChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
for (std::size_t k = 0; k < GetAnimationsCount(); k++) {
gd::SerializerElement& animationElement =
animationsElement.AddChild("animation");
animationElement.SetAttribute("useMultipleDirections",
GetAnimation(k).useMultipleDirections);
animationElement.SetAttribute("name", GetAnimation(k).GetName());
gd::SerializerElement& directionsElement =
animationElement.AddChild("directions");
directionsElement.ConsiderAsArrayOf("direction");
for (std::size_t l = 0; l < GetAnimation(k).GetDirectionsCount(); l++) {
GetAnimation(k).GetDirection(l).SerializeTo(
directionsElement.AddChild("direction"));
}
}
animations.SerializeTo(element);
}
std::map<gd::String, gd::PropertyDescriptor> SpriteObject::GetProperties()
@@ -127,16 +59,7 @@ bool SpriteObject::UpdateProperty(const gd::String& name,
}
void SpriteObject::ExposeResources(gd::ArbitraryResourceWorker& worker) {
for (std::size_t j = 0; j < GetAnimationsCount(); j++) {
for (std::size_t k = 0; k < GetAnimation(j).GetDirectionsCount(); k++) {
for (std::size_t l = 0;
l < GetAnimation(j).GetDirection(k).GetSpritesCount();
l++) {
worker.ExposeImage(
GetAnimation(j).GetDirection(k).GetSprite(l).GetImageName());
}
}
}
animations.ExposeResources(worker);
}
std::map<gd::String, gd::PropertyDescriptor>
@@ -168,42 +91,12 @@ bool SpriteObject::UpdateInitialInstanceProperty(
return true;
}
const Animation& SpriteObject::GetAnimation(std::size_t nb) const {
if (nb >= animations.size()) return badAnimation;
return animations[nb];
const SpriteAnimationList& SpriteObject::GetAnimations() const {
return animations;
}
Animation& SpriteObject::GetAnimation(std::size_t nb) {
if (nb >= animations.size()) return badAnimation;
return animations[nb];
}
void SpriteObject::AddAnimation(const Animation& animation) {
animations.push_back(animation);
}
bool SpriteObject::RemoveAnimation(std::size_t nb) {
if (nb >= GetAnimationsCount()) return false;
animations.erase(animations.begin() + nb);
return true;
}
void SpriteObject::SwapAnimations(std::size_t firstIndex,
std::size_t secondIndex) {
if (firstIndex < animations.size() && secondIndex < animations.size() &&
firstIndex != secondIndex)
std::swap(animations[firstIndex], animations[secondIndex]);
}
void SpriteObject::MoveAnimation(std::size_t oldIndex, std::size_t newIndex) {
if (oldIndex >= animations.size() || newIndex >= animations.size()) return;
auto animation = animations[oldIndex];
animations.erase(animations.begin() + oldIndex);
animations.insert(animations.begin() + newIndex, animation);
SpriteAnimationList& SpriteObject::GetAnimations() {
return animations;
}
} // namespace gd

View File

@@ -4,18 +4,15 @@
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_SPRITEOBJECT_H
#define GDCORE_SPRITEOBJECT_H
#include "GDCore/Extensions/Builtin/SpriteExtension/Animation.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/Direction.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/Sprite.h"
#pragma once
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteAnimationList.h"
#include "GDCore/Project/Object.h"
namespace gd {
class InitialInstance;
class Object;
class Layout;
class Sprite;
class Animation;
class SerializerElement;
class PropertyDescriptor;
} // namespace gd
@@ -59,76 +56,15 @@ class GD_CORE_API SpriteObject : public gd::ObjectConfiguration {
gd::Project& project,
gd::Layout& scene) override;
/** \name Animations
* Methods related to animations management
*/
///@{
/**
* \brief Return the animation at the specified index.
* If the index is out of bound, a "bad animation" object is returned.
* \brief Return the animation configuration.
*/
const Animation& GetAnimation(std::size_t nb) const;
const SpriteAnimationList& GetAnimations() const;
/**
* \brief Return the animation at the specified index.
* If the index is out of bound, a "bad animation" object is returned.
* @brief Return the animation configuration.
*/
Animation& GetAnimation(std::size_t nb);
/**
* \brief Return the number of animations this object has.
*/
std::size_t GetAnimationsCount() const { return animations.size(); };
/**
* \brief Add an animation at the end of the existing ones.
*/
void AddAnimation(const Animation& animation);
/**
* \brief Remove an animation.
*/
bool RemoveAnimation(std::size_t nb);
/**
* \brief Remove all animations.
*/
void RemoveAllAnimations() { animations.clear(); }
/**
* \brief Return true if the object hasn't any animation.
*/
bool HasNoAnimations() const { return animations.empty(); }
/**
* \brief Swap the position of two animations
*/
void SwapAnimations(std::size_t firstIndex, std::size_t secondIndex);
/**
* \brief Change the position of the specified animation
*/
void MoveAnimation(std::size_t oldIndex, std::size_t newIndex);
/**
* \brief Return a read-only reference to the vector containing all the
* animation of the object.
*/
const std::vector<Animation>& GetAllAnimations() const { return animations; }
/**
* @brief Check if the collision mask adapts automatically to the animation.
*/
bool AdaptCollisionMaskAutomatically() const {
return adaptCollisionMaskAutomatically;
}
/**
* @brief Set if the collision mask adapts automatically to the animation.
*/
void SetAdaptCollisionMaskAutomatically(bool enable) {
adaptCollisionMaskAutomatically = enable;
}
SpriteAnimationList& GetAnimations();
/**
* \brief Set if the object animation should be played even if the object is
@@ -143,25 +79,17 @@ class GD_CORE_API SpriteObject : public gd::ObjectConfiguration {
* is hidden or far from the camera (false by default).
*/
bool GetUpdateIfNotVisible() const { return updateIfNotVisible; }
///@}
private:
void DoUnserializeFrom(gd::Project& project,
const gd::SerializerElement& element) override;
void DoSerializeTo(gd::SerializerElement& element) const override;
mutable std::vector<Animation> animations;
SpriteAnimationList animations;
bool updateIfNotVisible; ///< If set to true, ask the game engine to play
///< object animation even if hidden or far from
///< the screen.
static Animation badAnimation; //< Bad animation when an out of bound
// animation is requested.
bool adaptCollisionMaskAutomatically; ///< If set to true, the collision
///< mask will be automatically
///< adapted to the animation of the
///< object.
};
} // namespace gd
#endif // GDCORE_SPRITEOBJECT_H

View File

@@ -86,8 +86,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
void OnVisitOperatorNode(OperatorNode& node) override {
ReportAnyError(node);
// The "required" type ("parentType") will be used when visiting the first operand.
// Note that it may be refined thanks to this first operand (see later).
node.leftHandSide->Visit(*this);
const Type leftType = childType;
const Type leftType = childType; // Store the type of the first operand.
if (leftType == Type::Number) {
if (node.op == ' ') {
@@ -120,15 +122,19 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
node.rightHandSide->location);
}
parentType = leftType;
// The "required" type ("parentType") of the second operator is decided by:
// - the parent type. Unless it can (`number|string`) or should (`unknown`) be refined, then:
// - the first operand.
parentType = ShouldTypeBeRefined(parentType) ? leftType : parentType;
node.rightHandSide->Visit(*this);
const Type rightType = childType;
// The type is decided by the first operand, unless it can (`number|string`)
// or should (`unknown`) be refined, in which case we go for the right
// operand (which got visited knowing the type of the first operand, so it's
// The type of the overall operator ("childType") is decided by:
// - the parent type. Unless it can (`number|string`) or should (`unknown`) be refined, then:
// - the first operand. Unless it can (`number|string`) or should (`unknown`) be refined, then:
// - the right operand (which got visited knowing the type of the first operand, so it's
// equal or strictly more precise than the left operand).
childType = (leftType == Type::Unknown || leftType == Type::NumberOrString) ? leftType : rightType;
childType = ShouldTypeBeRefined(parentType) ? (ShouldTypeBeRefined(leftType) ? leftType : rightType) : parentType;
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
ReportAnyError(node);
@@ -395,6 +401,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
}
}
static bool ShouldTypeBeRefined(Type type) {
return (type == Type::Unknown || type == Type::NumberOrString);
}
static Type StringToType(const gd::String &type);
static const gd::String &TypeToString(Type type);
static const gd::String unknownTypeString;

View File

@@ -13,12 +13,14 @@
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Project/CustomConfigurationHelper.h"
#include "GDCore/Project/InitialInstance.h"
using namespace gd;
void CustomObjectConfiguration::Init(const gd::CustomObjectConfiguration& objectConfiguration) {
project = objectConfiguration.project;
objectContent = objectConfiguration.objectContent;
animations = objectConfiguration.animations;
// There is no default copy for a map of unique_ptr like childObjectConfigurations.
childObjectConfigurations.clear();
@@ -88,23 +90,38 @@ bool CustomObjectConfiguration::UpdateProperty(const gd::String& propertyName,
std::map<gd::String, gd::PropertyDescriptor>
CustomObjectConfiguration::GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) {
return std::map<gd::String, gd::PropertyDescriptor>();
const gd::InitialInstance &initialInstance, gd::Project &project,
gd::Layout &scene) {
std::map<gd::String, gd::PropertyDescriptor> properties;
if (!animations.HasNoAnimations()) {
properties["animation"] =
gd::PropertyDescriptor(
gd::String::From(initialInstance.GetRawDoubleProperty("animation")))
.SetLabel(_("Animation"))
.SetType("number");
}
return properties;
}
bool CustomObjectConfiguration::UpdateInitialInstanceProperty(
gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& scene) {
return false;
gd::InitialInstance &initialInstance, const gd::String &name,
const gd::String &value, gd::Project &project, gd::Layout &scene) {
if (name == "animation") {
initialInstance.SetRawDoubleProperty(
"animation", std::max(0, value.empty() ? 0 : value.To<int>()));
}
return true;
}
void CustomObjectConfiguration::DoSerializeTo(SerializerElement& element) const {
element.AddChild("content") = objectContent;
if (!animations.HasNoAnimations()) {
auto &animatableElement = element.AddChild("animatable");
animations.SerializeTo(animatableElement);
}
auto &childrenContentElement = element.AddChild("childrenContent");
for (auto &pair : childObjectConfigurations) {
auto &childName = pair.first;
@@ -116,6 +133,12 @@ void CustomObjectConfiguration::DoSerializeTo(SerializerElement& element) const
void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
const SerializerElement& element) {
objectContent = element.GetChild("content");
if (element.HasChild("animatable")) {
auto &animatableElement = element.GetChild("animatable");
animations.UnserializeFrom(animatableElement);
}
auto &childrenContentElement = element.GetChild("childrenContent");
for (auto &pair : childrenContentElement.GetAllChildren()) {
auto &childName = pair.first;
@@ -126,6 +149,8 @@ void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
}
void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& worker) {
animations.ExposeResources(worker);
std::map<gd::String, gd::PropertyDescriptor> properties = GetProperties();
for (auto& property : properties) {
@@ -178,3 +203,11 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
configuration.ExposeResources(worker);
}
}
const SpriteAnimationList& CustomObjectConfiguration::GetAnimations() const {
return animations;
}
SpriteAnimationList& CustomObjectConfiguration::GetAnimations() {
return animations;
}

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_CUSTOMOBJECTCONFIGURATION_H
#define GDCORE_CUSTOMOBJECTCONFIGURATION_H
#pragma once
#include "GDCore/Project/ObjectConfiguration.h"
@@ -16,7 +15,7 @@
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteAnimationList.h"
using namespace gd;
@@ -72,7 +71,17 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
gd::ObjectConfiguration &GetChildObjectConfiguration(const gd::String& objectName);
protected:
/**
* \brief Return the animation configuration for Animatable custom objects.
*/
const SpriteAnimationList& GetAnimations() const;
/**
* @brief Return the animation configuration for Animatable custom objects.
*/
SpriteAnimationList& GetAnimations();
protected:
void DoSerializeTo(SerializerElement& element) const override;
void DoUnserializeFrom(Project& project, const SerializerElement& element) override;
@@ -84,6 +93,8 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
static gd::ObjectConfiguration badObjectConfiguration;
SpriteAnimationList animations;
/**
* Initialize configuration using another configuration. Used by copy-ctor
* and assign-op.
@@ -95,6 +106,5 @@ class CustomObjectConfiguration : public gd::ObjectConfiguration {
*/
void Init(const gd::CustomObjectConfiguration& object);
};
} // namespace gd
#endif // GDCORE_CUSTOMOBJECTCONFIGURATION_H
} // namespace gd

View File

@@ -14,7 +14,9 @@ EventsBasedObject::EventsBasedObject()
"MyObject",
gd::EventsFunctionsContainer::FunctionOwner::Object),
ObjectsContainer(),
isRenderedIn3D(false) {
isRenderedIn3D(false),
isAnimatable(false),
isTextContainer(false) {
}
EventsBasedObject::~EventsBasedObject() {}
@@ -31,6 +33,12 @@ void EventsBasedObject::SerializeTo(SerializerElement& element) const {
if (isRenderedIn3D) {
element.SetBoolAttribute("is3D", true);
}
if (isAnimatable) {
element.SetBoolAttribute("isAnimatable", true);
}
if (isTextContainer) {
element.SetBoolAttribute("isTextContainer", true);
}
AbstractEventsBasedEntity::SerializeTo(element);
SerializeObjectsTo(element.AddChild("objects"));
@@ -41,6 +49,8 @@ void EventsBasedObject::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
defaultName = element.GetStringAttribute("defaultName");
isRenderedIn3D = element.GetBoolAttribute("is3D", false);
isAnimatable = element.GetBoolAttribute("isAnimatable", false);
isTextContainer = element.GetBoolAttribute("isTextContainer", false);
AbstractEventsBasedEntity::UnserializeFrom(project, element);
UnserializeObjectsFrom(project, element.GetChild("objects"));

View File

@@ -85,6 +85,32 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
*/
bool IsRenderedIn3D() const { return isRenderedIn3D; }
/**
* \brief Declare an Animatable capability.
*/
EventsBasedObject& MarkAsAnimatable(bool isAnimatable_) {
isAnimatable = isAnimatable_;
return *this;
}
/**
* \brief Return true if the object needs an Animatable capability.
*/
bool IsAnimatable() const { return isAnimatable; }
/**
* \brief Declare a TextContainer capability.
*/
EventsBasedObject& MarkAsTextContainer(bool isTextContainer_) {
isTextContainer = isTextContainer_;
return *this;
}
/**
* \brief Return true if the object needs a TextContainer capability.
*/
bool IsTextContainer() const { return isTextContainer; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(gd::Project& project,
@@ -93,6 +119,8 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
private:
gd::String defaultName;
bool isRenderedIn3D;
bool isAnimatable;
bool isTextContainer;
};
} // namespace gd

View File

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

View File

@@ -206,6 +206,17 @@ class GD_CORE_API InitialInstance {
*/
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
@@ -340,11 +351,13 @@ class GD_CORE_API InitialInstance {
gd::VariablesContainer initialVariables; ///< Instance specific variables
bool locked; ///< True if the instance is locked
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,
///< useful for hot reloading.
/// useful for hot reloading.
static gd::String*
badStringProperyValue; ///< Empty string returned by GetRawStringProperty
badStringPropertyValue; ///< Empty string returned by GetRawStringProperty
};
} // namespace gd

View File

@@ -102,30 +102,19 @@ std::unique_ptr<gd::Object> Project::CreateObject(
behavior->SetDefaultBehavior(true);
};
if (project.HasEventsBasedObject(objectType)) {
// During project deserialization, event-based object metadata are not yet
// generated.
addDefaultBehavior("EffectCapability::EffectBehavior");
addDefaultBehavior("ResizableCapability::ResizableBehavior");
addDefaultBehavior("ScalableCapability::ScalableBehavior");
addDefaultBehavior("FlippableCapability::FlippableBehavior");
auto& eventBasedObject = project.GetEventsBasedObject(objectType);
if (eventBasedObject.IsRenderedIn3D()) {
addDefaultBehavior("Scene3D::Base3DBehavior");
}
else {
addDefaultBehavior("OpacityCapability::OpacityBehavior");
}
} else {
auto& objectMetadata =
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
if (MetadataProvider::IsBadObjectMetadata(objectMetadata)) {
gd::LogWarning("Object: " + name + " has an unknown type: " + objectType);
}
for (auto& behaviorType : objectMetadata.GetDefaultBehaviors()) {
auto &objectMetadata =
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
if (!MetadataProvider::IsBadObjectMetadata(objectMetadata)) {
for (auto &behaviorType : objectMetadata.GetDefaultBehaviors()) {
addDefaultBehavior(behaviorType);
}
}
// During project deserialization, event-based object metadata are not yet
// generated. Default behaviors will be added by
// MetadataDeclarationHelper::UpdateCustomObjectDefaultBehaviors
else if (!project.HasEventsBasedObject(objectType)) {
gd::LogWarning("Object: " + name + " has an unknown type: " + objectType);
}
return std::move(object);
}

View File

@@ -91,7 +91,7 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
sprite.SetImageName("res1");
anim.SetDirectionsCount(1);
anim.GetDirection(0).AddSprite(sprite);
spriteConfiguration.AddAnimation(anim);
spriteConfiguration.GetAnimations().AddAnimation(anim);
gd::Object obj("myObject", "", spriteConfiguration.Clone());
project.InsertObject(obj, 0);
@@ -138,7 +138,7 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
sprite.SetImageName("res1");
anim.SetDirectionsCount(1);
anim.GetDirection(0).AddSprite(sprite);
spriteConfiguration.AddAnimation(anim);
spriteConfiguration.GetAnimations().AddAnimation(anim);
gd::Object obj("myObject", "", spriteConfiguration.Clone());
layout.InsertObject(obj, 0);
@@ -437,7 +437,7 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
sprite.SetImageName("res1");
anim.SetDirectionsCount(1);
anim.GetDirection(0).AddSprite(sprite);
spriteConfiguration.AddAnimation(anim);
spriteConfiguration.GetAnimations().AddAnimation(anim);
gd::Object obj("myObject", "", spriteConfiguration.Clone());
layout.InsertObject(obj, 0);

View File

@@ -30,6 +30,21 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
layout1.GetVariables().InsertNew("MySceneBooleanVariable").SetBool(true);
layout1.GetVariables().InsertNew("MySceneStructureVariable").GetChild("MyChild");
layout1.GetVariables().InsertNew("MySceneStructureVariable2").GetChild("MyChild");
layout1.GetVariables().InsertNew("MySceneEmptyArrayVariable").CastTo(gd::Variable::Type::Array);
{
auto& variable = layout1.GetVariables().InsertNew("MySceneNumberArrayVariable");
variable.CastTo(gd::Variable::Type::Array);
variable.PushNew().SetValue(1);
variable.PushNew().SetValue(2);
variable.PushNew().SetValue(3);
}
{
auto& variable = layout1.GetVariables().InsertNew("MySceneStringArrayVariable");
variable.CastTo(gd::Variable::Type::Array);
variable.PushNew().SetString("1");
variable.PushNew().SetString("2");
variable.PushNew().SetString("3");
}
auto &mySpriteObject = layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
mySpriteObject.GetVariables().InsertNew("MyNumberVariable").SetValue(123);
@@ -1295,6 +1310,221 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
REQUIRE(output == "getVariableForObject(MySpriteObject, MyOtherSpriteObject).getChild(\"Child\").getChild(\"Grandchild\")");
}
}
SECTION("Type conversions (valid operators with variables having different types than the expression)") {
SECTION("Expression/parent type is 'string'") {
{
auto node =
parser.ParseExpression("\"You have \" + MySceneVariable + \" points\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"You have \" + getLayoutVariable(MySceneVariable).getAsString() + \" points\"");
}
{
auto node =
parser.ParseExpression("MySceneVariable + MySceneStringVariable");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsString() + getLayoutVariable(MySceneStringVariable).getAsString()");
}
}
SECTION("Expression/parent type is 'string' (with an unknown variable)") {
{
auto node =
parser.ParseExpression("\"You have \" + MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + \" points\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"You have \" + getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsString() + \" points\"");
}
}
SECTION("Expression/parent type is 'string' (2 number variables)") {
{
auto node =
parser.ParseExpression("MySceneVariable + MySceneVariable2 + \"world\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsString() + getLayoutVariable(MySceneVariable2).getAsString() + \"world\"");
}
{
auto node =
parser.ParseExpression("MySceneVariable + MySceneVariable2 + MySceneStringVariable");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsString() + getLayoutVariable(MySceneVariable2).getAsString() + getLayoutVariable(MySceneStringVariable).getAsString()");
}
}
SECTION("Expression/parent type is 'string' (array variable)") {
{
auto node =
parser.ParseExpression("\"hello\" + MySceneNumberArrayVariable[2] + \"world\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getLayoutVariable(MySceneNumberArrayVariable).getChild(2).getAsString() + \"world\"");
}
{
auto node =
parser.ParseExpression("\"hello\" + MySceneEmptyArrayVariable[2] + \"world\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getLayoutVariable(MySceneEmptyArrayVariable).getChild(2).getAsString() + \"world\"");
}
}
SECTION("Expression/parent type is 'number'") {
{
auto node =
parser.ParseExpression("123 + MySceneVariable + 456");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getLayoutVariable(MySceneVariable).getAsNumber() + 456");
}
{
auto node =
parser.ParseExpression("MySceneStringVariable + MySceneVariable");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStringVariable).getAsNumber() + getLayoutVariable(MySceneVariable).getAsNumber()");
}
}
SECTION("Expression/parent type is 'string' (with an unknown variable)") {
{
auto node =
parser.ParseExpression("123 + MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + 456");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getChild(\"CantKnownTheTypeSoStayGeneric\").getAsNumber() + 456");
}
}
SECTION("Expression/parent type is 'number' (2 string variables)") {
{
auto node =
parser.ParseExpression("MySceneStringVariable + MySceneStringVariable + 456");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStringVariable).getAsNumber() + getLayoutVariable(MySceneStringVariable).getAsNumber() + 456");
}
{
auto node =
parser.ParseExpression("MySceneStringVariable + MySceneStringVariable + MySceneVariable");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStringVariable).getAsNumber() + getLayoutVariable(MySceneStringVariable).getAsNumber() + getLayoutVariable(MySceneVariable).getAsNumber()");
}
}
SECTION("Expression/parent type is 'number' (array variable)") {
{
auto node =
parser.ParseExpression("123 + MySceneNumberArrayVariable[2] + 456");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getLayoutVariable(MySceneNumberArrayVariable).getChild(2).getAsNumber() + 456");
}
{
auto node =
parser.ParseExpression("123 + MySceneEmptyArrayVariable[2] + 456");
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "123 + getLayoutVariable(MySceneEmptyArrayVariable).getChild(2).getAsNumber() + 456");
}
}
SECTION("Multiple type conversions in sub expressions or same expression") {
{
auto node =
parser.ParseExpression("\"hello\" + MySceneNumberArrayVariable[2 + MySceneStringVariable] + \"world\" + MySceneVariable + \"world 2\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getLayoutVariable(MySceneNumberArrayVariable).getChild(2 + getLayoutVariable(MySceneStringVariable).getAsNumber()).getAsString() + \"world\" + getLayoutVariable(MySceneVariable).getAsString() + \"world 2\"");
}
{
auto node =
parser.ParseExpression("\"hello\" + MySceneNumberArrayVariable[\"foo\" + MySceneVariable + \"bar\"] + \"world\" + MySceneVariable + \"world 2\"");
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
"",
codeGenerator,
context);
REQUIRE(node);
node->Visit(expressionCodeGenerator);
REQUIRE(expressionCodeGenerator.GetOutput() == "\"hello\" + getLayoutVariable(MySceneNumberArrayVariable).getChild(\"foo\" + getLayoutVariable(MySceneVariable).getAsString() + \"bar\").getAsString() + \"world\" + getLayoutVariable(MySceneVariable).getAsString() + \"world 2\"");
}
}
}
SECTION("Mixed test (1)") {
{
auto node = parser.ParseExpression("-+-MyExtension::MouseX(,)");

View File

@@ -2954,6 +2954,238 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Valid operators with variables having different types than the expression") {
SECTION("Expression/parent type is 'string'") {
// A trivial test (everything is a string).
{
auto node = parser.ParseExpression("\"You have \" + MySceneStringVariable + \" points\"");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A string concatenated with a number variable (will have to be casted to a string in code generation)
{
auto node = parser.ParseExpression("\"You have \" + MySceneNumberVariable");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A string concatenated with a number variable (will have to be casted to a string in code generation)
// and then with a string again.
{
auto node = parser.ParseExpression("\"You have \" + MySceneNumberVariable + \" points\"");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A string concatenated with an unknown variable (will have to be casted to a string in code generation)
// and then with a string again.
{
auto node = parser.ParseExpression("\"You have \" + MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + \" points\"");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
}
SECTION("Expression/parent type is 'number'") {
// A trivial test (everything is a string).
{
auto node = parser.ParseExpression("123 + MySceneNumberVariable + 456");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A number concatenated with a string variable (will have to be casted to a number in code generation)
{
auto node = parser.ParseExpression("123 + MySceneStringVariable");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A number concatenated with a string variable (will have to be casted to a number in code generation)
// and then with a number again.
{
auto node = parser.ParseExpression("123 + MySceneStringVariable + 456");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A number concatenated with an unknown variable (will have to be casted to a number in code generation)
// and then with a number again.
{
auto node = parser.ParseExpression("123 + MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + 456");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
}
SECTION("Expression/parent type is 'number|string'") {
SECTION("Expression/parent inferred type is 'string'") {
// A trivial test (everything is a string).
{
auto node = parser.ParseExpression("\"You have \" + MySceneStringVariable + \" points\"");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A string concatenated with a number variable (will have to be casted to a string in code generation)
{
auto node = parser.ParseExpression("\"You have \" + MySceneNumberVariable");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A string concatenated with a number variable (will have to be casted to a string in code generation)
// and then with a string again.
{
auto node = parser.ParseExpression("\"You have \" + MySceneNumberVariable + \" points\"");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A string concatenated with an unknown variable (will have to be casted to a string in code generation)
// and then with a string again.
{
auto node = parser.ParseExpression("\"You have \" + MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + \" points\"");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
}
SECTION("Expression/parent inferred type is 'number'") {
// A trivial test (everything is a string).
{
auto node = parser.ParseExpression("123 + MySceneNumberVariable + 456");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A number concatenated with a string variable (will have to be casted to a number in code generation)
{
auto node = parser.ParseExpression("123 + MySceneStringVariable");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A number concatenated with a string variable (will have to be casted to a number in code generation)
// and then with a number again.
{
auto node = parser.ParseExpression("123 + MySceneStringVariable + 456");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
// A number concatenated with an unknown variable (will have to be casted to a number in code generation)
// and then with a number again.
{
auto node = parser.ParseExpression("123 + MySceneStructureVariable.MyChild.CantKnownTheTypeSoStayGeneric + 456");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 0);
}
}
}
}
SECTION("Invalid operators with variables having different types than the expression") {
// Try to do a sum between numbers in a string expression
{
auto node = parser.ParseExpression("\"You have \" + MySceneNumberVariable + 2 + \" points\"");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You entered a number, but a text was expected (in quotes).");
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 38);
REQUIRE(validator.GetFatalErrors()[0]->GetEndPosition() == 39);
}
// Try to do a sum between numbers in a number|string expression (that is inferred as a string with the first operand)
{
auto node = parser.ParseExpression("\"You have \" + MySceneNumberVariable + 2 + \" points\"");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You entered a number, but a text was expected (in quotes).");
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 38);
REQUIRE(validator.GetFatalErrors()[0]->GetEndPosition() == 39);
}
// Try to do a string concatenation in a number expression
{
auto node = parser.ParseExpression("123 + MySceneStringVariable + \"hello\" + 456");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You entered a text, but a number was expected.");
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 30);
REQUIRE(validator.GetFatalErrors()[0]->GetEndPosition() == 37);
}
// Try to do a string concatenation in a number|string expression (that is inferred as a number with the first operand)
{
auto node = parser.ParseExpression("123 + MySceneStringVariable + \"hello\" + 456");
REQUIRE(node != nullptr);
gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == "You entered a text, but a number was expected.");
REQUIRE(validator.GetFatalErrors()[0]->GetStartPosition() == 30);
REQUIRE(validator.GetFatalErrors()[0]->GetEndPosition() == 37);
}
}
SECTION("Valid function call with object variable") {
{
// Note that in this test we need to use an expression with "objectvar",

View File

@@ -71,7 +71,7 @@ TEST_CASE("ObjectAssetSerializer", "[common]") {
frame.SetImageName("assets/Idle.png");
direction.AddSprite(frame);
spriteConfiguration->AddAnimation(animation);
spriteConfiguration->GetAnimations().AddAnimation(animation);
}
SerializerElement assetElement;

View File

@@ -35,7 +35,7 @@ void SetupSpriteConfiguration(gd::ObjectConfiguration &configuration) {
REQUIRE(spriteConfiguration != nullptr);
gd::Animation animation;
animation.SetName("Idle");
spriteConfiguration->AddAnimation(animation);
spriteConfiguration->GetAnimations().AddAnimation(animation);
};
gd::Object &SetupProjectWithSprite(gd::Project &project,
@@ -83,9 +83,9 @@ void CheckSpriteConfigurationInProjectElement(
void CheckSpriteConfiguration(gd::ObjectConfiguration &configuration) {
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(&configuration);
REQUIRE(spriteConfiguration);
REQUIRE(spriteConfiguration->GetAnimationsCount() == 1);
REQUIRE(spriteConfiguration->GetAnimations().GetAnimationsCount() == 1);
auto &animation = spriteConfiguration->GetAnimation(0);
auto &animation = spriteConfiguration->GetAnimations().GetAnimation(0);
REQUIRE(animation.GetName() == "Idle");
};

View File

@@ -105,7 +105,7 @@ namespace gdjs {
if (this._isUntransformedHitBoxesDirty) {
this._updateUntransformedHitBoxes();
}
return this._minZ;
return this._z + this._minZ;
}
/**

View File

@@ -1,4 +1,7 @@
namespace gdjs {
export interface PixiImageManager {
_threeAnimationFrameTextureManager: ThreeAnimationFrameTextureManager;
}
/**
* The renderer for a {@link gdjs.CustomRuntimeObject3D} using Three.js.
*/
@@ -80,7 +83,7 @@ namespace gdjs {
threeObject3D.visible = !this._object.hidden;
this._isContainerDirty = true;
this._isContainerDirty = false;
}
/**
@@ -131,5 +134,46 @@ namespace gdjs {
setLayerIndex(layer: gdjs.RuntimeLayer, index: float): void {
// Layers are not handled for 3D custom objects.
}
static getAnimationFrameTextureManager(
imageManager: gdjs.PixiImageManager
): ThreeAnimationFrameTextureManager {
if (!imageManager._threeAnimationFrameTextureManager) {
imageManager._threeAnimationFrameTextureManager = new ThreeAnimationFrameTextureManager(
imageManager
);
}
return imageManager._threeAnimationFrameTextureManager;
}
}
class ThreeAnimationFrameTextureManager
implements gdjs.AnimationFrameTextureManager<THREE.Material> {
private _imageManager: gdjs.PixiImageManager;
constructor(imageManager: gdjs.PixiImageManager) {
this._imageManager = imageManager;
}
getAnimationFrameTexture(imageName: string) {
return this._imageManager.getThreeMaterial(imageName, {
useTransparentTexture: true,
forceBasicMaterial: true,
});
}
getAnimationFrameWidth(material: THREE.Material) {
const map = (material as
| THREE.MeshBasicMaterial
| THREE.MeshStandardMaterial).map;
return map ? map.image.width : 0;
}
getAnimationFrameHeight(material: THREE.Material) {
const map = (material as
| THREE.MeshBasicMaterial
| THREE.MeshStandardMaterial).map;
return map ? map.image.height : 0;
}
}
}

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -811,7 +803,6 @@ module.exports = {
}
const Cube3DObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
Cube3DObject.updateProperty = function (
objectContent,
propertyName,
@@ -860,7 +851,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
Cube3DObject.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
@@ -1100,7 +1090,6 @@ module.exports = {
})
);
// $FlowExpectedError
Cube3DObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -1112,7 +1101,6 @@ module.exports = {
return false;
};
// $FlowExpectedError
Cube3DObject.getInitialInstanceProperties = function (
content,
instance,
@@ -1665,7 +1653,7 @@ module.exports = {
'Change the camera rotation to look at an object. The camera top always face the screen.'
),
_('Change the camera rotation of _PARAM2_ to look at _PARAM1_'),
_("Layers and cameras"),
_('Layers and cameras'),
'res/conditions/3d_box.svg',
'res/conditions/3d_box.svg'
)
@@ -1943,11 +1931,7 @@ module.exports = {
const effect = extension
.addEffect('HueAndSaturation')
.setFullName(_('Hue and saturation'))
.setDescription(
_(
'Adjust hue and saturation.'
)
)
.setDescription(_('Adjust hue and saturation.'))
.markAsNotWorkingForObjects()
.markAsOnlyWorkingFor3D()
.addIncludeFile('Extensions/3D/HueAndSaturationEffect.js');
@@ -1967,11 +1951,7 @@ module.exports = {
const effect = extension
.addEffect('Exposure')
.setFullName(_('Exposure'))
.setDescription(
_(
'Adjust exposure.'
)
)
.setDescription(_('Adjust exposure.'))
.markAsNotWorkingForObjects()
.markAsOnlyWorkingFor3D()
.addIncludeFile('Extensions/3D/ExposureEffect.js');
@@ -1986,11 +1966,7 @@ module.exports = {
const effect = extension
.addEffect('Bloom')
.setFullName(_('Bloom'))
.setDescription(
_(
'Apply a bloom effect.'
)
)
.setDescription(_('Apply a bloom effect.'))
.markAsNotWorkingForObjects()
.markAsOnlyWorkingFor3D()
.addIncludeFile('Extensions/3D/BloomEffect.js');
@@ -2014,12 +1990,8 @@ module.exports = {
{
const effect = extension
.addEffect('BrightnessAndContrast')
.setFullName(_('Brightness and contrast'))
.setDescription(
_(
'Adjust brightness and contrast.'
)
)
.setFullName(_('Brightness and contrast.'))
.setDescription(_('Adjust brightness and contrast.'))
.markAsNotWorkingForObjects()
.markAsOnlyWorkingFor3D()
.addIncludeFile('Extensions/3D/BrightnessAndContrastEffect.js');
@@ -2050,10 +2022,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -2061,17 +2030,13 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {},
registerEditorConfigurations: function (objectsEditorService) {},
/**
* Register renderers for instance of objects on the scene editor.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const Rendered3DInstance = objectsRenderingService.Rendered3DInstance;
const PIXI = objectsRenderingService.PIXI;
@@ -2079,39 +2044,25 @@ module.exports = {
const THREE_ADDONS = objectsRenderingService.THREE_ADDONS;
const materialIndexToFaceIndex = {
// $FlowFixMe
0: 3,
// $FlowFixMe
1: 2,
// $FlowFixMe
2: 5,
// $FlowFixMe
3: 4,
// $FlowFixMe
4: 0,
// $FlowFixMe
5: 1,
};
const noRepeatTextureVertexIndexToUvMapping = {
// $FlowFixMe
0: [0, 0],
// $FlowFixMe
1: [1, 0],
// $FlowFixMe
2: [0, 1],
// $FlowFixMe
3: [1, 1],
};
const noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ = {
// $FlowFixMe
0: [0, 1],
// $FlowFixMe
1: [0, 0],
// $FlowFixMe
2: [1, 1],
// $FlowFixMe
3: [1, 0],
};
@@ -2157,6 +2108,11 @@ module.exports = {
};
class RenderedCube3DObject2DInstance extends RenderedInstance {
/** @type {number} */
_centerX = 0;
/** @type {number} */
_centerY = 0;
constructor(
project,
layout,
@@ -2173,10 +2129,9 @@ module.exports = {
pixiContainer,
pixiResourcesLoader
);
/**
* Name of the resource that is rendered.
* If no face is visible, this will be null.
*/
// Name of the resource that is rendered.
// If no face is visible, this will be null.
this._renderedResourceName = undefined;
const properties = associatedObjectConfiguration.getProperties();
this._defaultWidth = parseFloat(properties.get('width').getValue());
@@ -2206,12 +2161,9 @@ module.exports = {
}
static getThumbnail(project, resourcesLoader, objectConfiguration) {
const instance = this._instance;
const textureResourceName =
RenderedCube3DObject2DInstance._getResourceNameToDisplay(
objectConfiguration
);
const textureResourceName = RenderedCube3DObject2DInstance._getResourceNameToDisplay(
objectConfiguration
);
if (textureResourceName) {
return resourcesLoader.getResourceFullUrl(
project,
@@ -2223,20 +2175,18 @@ module.exports = {
}
updateTextureIfNeeded() {
const textureName =
RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
const textureName = RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
if (textureName === this._renderedResourceName) return;
this.updateTexture();
}
updateTexture() {
const textureName =
RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
const textureName = RenderedCube3DObject2DInstance._getResourceNameToDisplay(
this._associatedObjectConfiguration
);
if (!textureName) {
this._renderFallbackObject = true;
@@ -2494,10 +2444,9 @@ module.exports = {
continue;
}
const shouldRepeatTexture =
this._shouldRepeatTextureOnFace[
materialIndexToFaceIndex[materialIndex]
];
const shouldRepeatTexture = this._shouldRepeatTextureOnFace[
materialIndexToFaceIndex[materialIndex]
];
const shouldOrientateFacesTowardsY = this._facesOrientation === 'Y';
@@ -2532,13 +2481,16 @@ module.exports = {
}
} else {
if (shouldOrientateFacesTowardsY) {
[x, y] =
noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
[x, y] = noRepeatTextureVertexIndexToUvMapping[
vertexIndex % 4
];
} else {
[x, y] =
noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
}
}
break;
@@ -2568,13 +2520,16 @@ module.exports = {
}
} else {
if (shouldOrientateFacesTowardsY) {
[x, y] =
noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
[x, y] = noRepeatTextureVertexIndexToUvMapping[
vertexIndex % 4
];
} else {
[x, y] =
noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
x = -x;
y = -y;
}
@@ -3111,7 +3066,7 @@ module.exports = {
);
threeObject.updateMatrixWorld(true);
const boundingBox = new THREE.Box3().setFromObject(threeObject);
const shouldKeepModelOrigin = !this._originPoint;
if (shouldKeepModelOrigin) {
// Keep the origin as part of the model.
@@ -3179,14 +3134,51 @@ module.exports = {
modelDepth < epsilon
? Number.POSITIVE_INFINITY
: originalDepth / modelDepth;
let scaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
if (!Number.isFinite(scaleRatio)) {
scaleRatio = 1;
}
const minScaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
if (!Number.isFinite(minScaleRatio)) {
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 = scaleRatio * modelHeight;
this._defaultDepth = scaleRatio * modelDepth;
this._defaultHeight = originalHeight;
this._defaultDepth = Rendered3DInstance.applyRatio({
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,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -41,6 +33,14 @@ module.exports = {
.addInstructionOrExpressionGroupMetadata(_('AdMob'))
.setIcon('JsPlatform/Extensions/admobicon24.png');
extension
.addDependency()
.setName('Consent Cordova plugin')
.setDependencyType('cordova')
.setExportName('cordova-plugin-consent')
.setVersion('2.4.0')
.onlyIfOtherDependencyIsExported('AdMob Cordova plugin');
extension
.registerProperty('AdMobAppIdAndroid')
.setLabel(_('AdMob Android App ID'))
@@ -71,13 +71,6 @@ module.exports = {
)
.onlyIfSomeExtraSettingsNonEmpty();
extension
.addDependency()
.setName('Consent Cordova plugin')
.setDependencyType('cordova')
.setExportName('cordova-plugin-consent')
.onlyIfOtherDependencyIsExported('AdMob Cordova plugin');
extension
.addAction(
'SetTestMode',
@@ -789,10 +782,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,11 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -710,10 +709,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,20 +13,11 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
const stringifyOptions = (options) => '["' + options.join('","') + '"]';
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -42,7 +34,6 @@ module.exports = {
.setIcon('JsPlatform/Extensions/bbcode32.png');
var objectBBText = new gd.ObjectJsImplementation();
// $FlowExpectedError
objectBBText.updateProperty = function (
objectContent,
propertyName,
@@ -59,7 +50,6 @@ module.exports = {
return false;
};
// $FlowExpectedError
objectBBText.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
@@ -126,7 +116,8 @@ module.exports = {
};
objectBBText.setRawJSONContent(
JSON.stringify({
text: '[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
text:
'[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
opacity: 255,
fontSize: 20,
visible: true,
@@ -137,7 +128,6 @@ module.exports = {
})
);
// $FlowExpectedError
objectBBText.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -148,7 +138,6 @@ module.exports = {
) {
return false;
};
// $FlowExpectedError
objectBBText.getInitialInstanceProperties = function (
content,
instance,
@@ -223,10 +212,9 @@ module.exports = {
parameterType === 'string' ||
parameterType === 'stringWithSelector'
) {
const parameterOptions =
gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
const parameterOptions = gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
if (property.options) {
parameterOptions.setTypeExtraInfo(
stringifyOptions(property.options)
@@ -276,10 +264,9 @@ module.exports = {
parameterType === 'number' ||
parameterType === 'stringWithSelector'
) {
const parameterOptions =
gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
const parameterOptions = gd.ParameterOptions.makeNewOptions().setDescription(
property.paramLabel
);
if (property.options) {
parameterOptions.setTypeExtraInfo(
stringifyOptions(property.options)
@@ -475,10 +462,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -486,9 +470,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'BBText::BBText',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -501,11 +483,8 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
const MultiStyleText = objectsRenderingService.requireModule(
__dirname,
'pixi-multistyle-text/dist/pixi-multistyle-text.umd'
@@ -514,150 +493,145 @@ module.exports = {
/**
* Renderer for instances of BBText inside the IDE.
*
* @extends RenderedBBTextInstance
* @extends RenderedInstance
* @class RenderedBBTextInstance
* @constructor
*/
function RenderedBBTextInstance(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
RenderedInstance.call(
this,
class RenderedBBTextInstance extends RenderedInstance {
constructor(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
) {
super(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
const bbTextStyles = {
default: {
// Use a default font family the time for the resource font to be loaded.
fontFamily: 'Arial',
fontSize: '24px',
fill: '#cccccc',
tagStyle: 'bbcode',
wordWrap: true,
wordWrapWidth: 250, // This value is the default wrapping width of the runtime object.
align: 'left',
},
};
const bbTextStyles = {
default: {
// Use a default font family the time for the resource font to be loaded.
fontFamily: 'Arial',
fontSize: '24px',
fill: '#cccccc',
tagStyle: 'bbcode',
wordWrap: true,
wordWrapWidth: 250, // This value is the default wrapping width of the runtime object.
align: 'left',
},
};
this._pixiObject = new MultiStyleText('', bbTextStyles);
this._pixiObject = new MultiStyleText('', bbTextStyles);
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
RenderedBBTextInstance.prototype = Object.create(
RenderedInstance.prototype
);
/**
* Return the path to the thumbnail of the specified object.
*/
RenderedBBTextInstance.getThumbnail = function (
project,
resourcesLoader,
objectConfiguration
) {
return 'JsPlatform/Extensions/bbcode24.png';
};
/**
* This is called to update the PIXI object on the scene editor
*/
RenderedBBTextInstance.prototype.update = function () {
const properties = this._associatedObjectConfiguration.getProperties();
const rawText = properties.get('text').getValue();
if (rawText !== this._pixiObject.text) {
this._pixiObject.text = rawText;
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
const opacity = properties.get('opacity').getValue();
this._pixiObject.alpha = opacity / 255;
const color = properties.get('color').getValue();
this._pixiObject.textStyles.default.fill =
objectsRenderingService.rgbOrHexToHexNumber(color);
const fontSize = properties.get('fontSize').getValue();
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
const fontResourceName = properties.get('fontFamily').getValue();
if (this._fontResourceName !== fontResourceName) {
this._fontResourceName = fontResourceName;
this._pixiResourcesLoader
.loadFontFamily(this._project, fontResourceName)
.then((fontFamily) => {
// Once the font is loaded, we can use the given fontFamily.
this._pixiObject.textStyles.default.fontFamily = fontFamily;
this._pixiObject.dirty = true;
})
.catch((err) => {
// Ignore errors
console.warn(
'Unable to load font family for RenderedBBTextInstance',
err
);
});
/**
* Return the path to the thumbnail of the specified object.
*/
static getThumbnail(project, resourcesLoader, objectConfiguration) {
return 'JsPlatform/Extensions/bbcode24.png';
}
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap !== this._pixiObject._style.wordWrap) {
this._pixiObject._style.wordWrap = wordWrap;
this._pixiObject.dirty = true;
}
/**
* This is called to update the PIXI object on the scene editor
*/
update() {
const properties = this._associatedObjectConfiguration.getProperties();
const align = properties.get('align').getValue();
if (align !== this._pixiObject._style.align) {
this._pixiObject._style.align = align;
this._pixiObject.dirty = true;
}
const rawText = properties.get('text').getValue();
if (rawText !== this._pixiObject.text) {
this._pixiObject.text = rawText;
}
this._pixiObject.position.x =
this._instance.getX() + this._pixiObject.width / 2;
this._pixiObject.position.y =
this._instance.getY() + this._pixiObject.height / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
const opacity = +properties.get('opacity').getValue();
this._pixiObject.alpha = opacity / 255;
if (this._instance.hasCustomSize() && this._pixiObject) {
const customWidth = this.getCustomWidth();
if (
this._pixiObject &&
this._pixiObject._style.wordWrapWidth !== customWidth
) {
this._pixiObject._style.wordWrapWidth = customWidth;
const color = properties.get('color').getValue();
this._pixiObject.textStyles.default.fill = objectsRenderingService.rgbOrHexToHexNumber(
color
);
const fontSize = properties.get('fontSize').getValue();
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
const fontResourceName = properties.get('fontFamily').getValue();
if (this._fontResourceName !== fontResourceName) {
this._fontResourceName = fontResourceName;
this._pixiResourcesLoader
.loadFontFamily(this._project, fontResourceName)
.then((fontFamily) => {
// Once the font is loaded, we can use the given fontFamily.
this._pixiObject.textStyles.default.fontFamily = fontFamily;
this._pixiObject.dirty = true;
})
.catch((err) => {
// Ignore errors
console.warn(
'Unable to load font family for RenderedBBTextInstance',
err
);
});
}
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap !== this._pixiObject._style.wordWrap) {
this._pixiObject._style.wordWrap = wordWrap;
this._pixiObject.dirty = true;
}
const align = properties.get('align').getValue();
if (align !== this._pixiObject._style.align) {
this._pixiObject._style.align = align;
this._pixiObject.dirty = true;
}
this._pixiObject.position.x =
this._instance.getX() + this._pixiObject.width / 2;
this._pixiObject.position.y =
this._instance.getY() + this._pixiObject.height / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
if (this._instance.hasCustomSize() && this._pixiObject) {
const customWidth = this.getCustomWidth();
if (
this._pixiObject &&
this._pixiObject._style.wordWrapWidth !== customWidth
) {
this._pixiObject._style.wordWrapWidth = customWidth;
this._pixiObject.dirty = true;
}
}
}
};
/**
* Return the width of the instance, when it's not resized.
*/
RenderedBBTextInstance.prototype.getDefaultWidth = function () {
return this._pixiObject.width;
};
/**
* Return the width of the instance, when it's not resized.
*/
getDefaultWidth() {
return this._pixiObject.width;
}
/**
* Return the height of the instance, when it's not resized.
*/
RenderedBBTextInstance.prototype.getDefaultHeight = function () {
return this._pixiObject.height;
};
/**
* Return the height of the instance, when it's not resized.
*/
getDefaultHeight() {
return this._pixiObject.height;
}
}
objectsRenderingService.registerInstanceRenderer(
'BBText::BBText',

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -42,7 +34,6 @@ module.exports = {
.setIcon('JsPlatform/Extensions/bitmapfont32.png');
const bitmapTextObject = new gd.ObjectJsImplementation();
// $FlowExpectedError
bitmapTextObject.updateProperty = function (
objectContent,
propertyName,
@@ -59,7 +50,6 @@ module.exports = {
return false;
};
// $FlowExpectedError
bitmapTextObject.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
@@ -127,7 +117,8 @@ module.exports = {
};
bitmapTextObject.setRawJSONContent(
JSON.stringify({
text: 'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
text:
'This text use the default bitmap font.\nUse a custom Bitmap Font to create your own texts.',
opacity: 255,
scale: 1,
fontSize: 20,
@@ -139,7 +130,6 @@ module.exports = {
})
);
// $FlowExpectedError
bitmapTextObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -150,7 +140,6 @@ module.exports = {
) {
return false;
};
// $FlowExpectedError
bitmapTextObject.getInitialInstanceProperties = function (
content,
instance,
@@ -176,7 +165,7 @@ module.exports = {
'Extensions/BitmapText/bitmaptextruntimeobject-pixi-renderer.js'
)
.setCategoryFullName(_('Text'))
.addDefaultBehavior("TextContainerCapability::TextContainerBehavior")
.addDefaultBehavior('TextContainerCapability::TextContainerBehavior')
.addDefaultBehavior('EffectCapability::EffectBehavior')
.addDefaultBehavior('OpacityCapability::OpacityBehavior')
.addDefaultBehavior('ScalableCapability::ScalableBehavior');
@@ -327,33 +316,33 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('setBitmapFontAndTextureAtlasResourceName');
object
.addAction(
'SetBitmapFontAndTextureAtlasResourceName2',
_('Bitmap files resources'),
_('Change the Bitmap Font and/or the atlas image used by the object.'),
_(
'Set the bitmap font of _PARAM0_ to _PARAM1_ and the atlas to _PARAM2_'
),
'',
'res/actions/font24.png',
'res/actions/font.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter(
'bitmapFontResource',
_('Bitmap font resource name'),
'',
false
)
.addParameter(
'imageResource',
_('Texture atlas resource name'),
'',
false
)
.getCodeExtraInformation()
.setFunctionName('setBitmapFontAndTextureAtlasResourceName');
object
.addAction(
'SetBitmapFontAndTextureAtlasResourceName2',
_('Bitmap files resources'),
_('Change the Bitmap Font and/or the atlas image used by the object.'),
_(
'Set the bitmap font of _PARAM0_ to _PARAM1_ and the atlas to _PARAM2_'
),
'',
'res/actions/font24.png',
'res/actions/font.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.addParameter(
'bitmapFontResource',
_('Bitmap font resource name'),
'',
false
)
.addParameter(
'imageResource',
_('Texture atlas resource name'),
'',
false
)
.getCodeExtraInformation()
.setFunctionName('setBitmapFontAndTextureAtlasResourceName');
object
.addExpressionAndCondition(
@@ -451,10 +440,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -462,9 +448,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'BitmapText::BitmapTextObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -477,9 +461,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
@@ -649,156 +631,144 @@ module.exports = {
};
/**
* Renderer for instances of BitmapText inside the IDE.
*
* @extends RenderedBitmapTextInstance
* @class RenderedBitmapTextInstance
* @constructor
* Return the path to the thumbnail of the specified object.
* This is called to update the PIXI object on the scene editor
*/
function RenderedBitmapTextInstance(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
RenderedInstance.call(
this,
class RenderedBitmapTextInstance extends RenderedInstance {
static getThumbnail(project, resourcesLoader, objectConfiguration) {
return 'JsPlatform/Extensions/bitmapfont24.png';
}
constructor(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
// We'll track changes of the font to trigger the loading of the new font.
this._currentBitmapFontResourceName = '';
this._currentTextureAtlasResourceName = '';
this._pixiObject = new PIXI.BitmapText('', {
// Use a default font. The proper font will be loaded in `update` method.
fontName: getDefaultBitmapFont().font,
});
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
RenderedBitmapTextInstance.prototype = Object.create(
RenderedInstance.prototype
);
/**
* Return the path to the thumbnail of the specified object.
*/
RenderedBitmapTextInstance.getThumbnail = function (
project,
resourcesLoader,
objectConfiguration
) {
return 'JsPlatform/Extensions/bitmapfont24.png';
};
// This is called to update the PIXI object on the scene editor
RenderedBitmapTextInstance.prototype.update = function () {
const properties = this._associatedObjectConfiguration.getProperties();
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).
const rawText = properties.get('text').getValue();
this._pixiObject.text = rawText;
const opacity = properties.get('opacity').getValue();
this._pixiObject.alpha = opacity / 255;
const align = properties.get('align').getValue();
this._pixiObject.align = align;
const color = properties.get('tint').getValue();
this._pixiObject.tint =
objectsRenderingService.rgbOrHexToHexNumber(color);
const scale = properties.get('scale').getValue() || 1;
this._pixiObject.scale.set(scale);
// Track the changes in font to load the new requested font.
const bitmapFontResourceName = properties
.get('bitmapFontResourceName')
.getValue();
const textureAtlasResourceName = properties
.get('textureAtlasResourceName')
.getValue();
if (
this._currentBitmapFontResourceName !== bitmapFontResourceName ||
this._currentTextureAtlasResourceName !== textureAtlasResourceName
) {
// Release the old font (if it was installed).
releaseBitmapFont(this._pixiObject.fontName);
super(
project,
layout,
instance,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
// Temporarily go back to the default font, as the PIXI.BitmapText
// object does not support being displayed with a font not installed at all.
// It will be replaced as soon as the proper font is loaded.
this._pixiObject.fontName = getDefaultBitmapFont().font;
// We'll track changes of the font to trigger the loading of the new font.
this._currentBitmapFontResourceName = '';
this._currentTextureAtlasResourceName = '';
this._currentBitmapFontResourceName = bitmapFontResourceName;
this._currentTextureAtlasResourceName = textureAtlasResourceName;
obtainBitmapFont(
this._pixiResourcesLoader,
this._project,
this._currentBitmapFontResourceName,
this._currentTextureAtlasResourceName
).then((bitmapFont) => {
this._pixiObject.fontName = bitmapFont.font;
this._pixiObject.fontSize = bitmapFont.size;
this._pixiObject.dirty = true;
this._pixiObject = new PIXI.BitmapText('', {
// Use a default font. The proper font will be loaded in `update` method.
fontName: getDefaultBitmapFont().font,
});
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
// Set up the wrapping width if enabled.
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap && this._instance.hasCustomSize()) {
this._pixiObject.maxWidth =
this.getCustomWidth() / this._pixiObject.scale.x;
this._pixiObject.dirty = true;
} else {
this._pixiObject.maxWidth = 0;
this._pixiObject.dirty = true;
update() {
const properties = this._associatedObjectConfiguration.getProperties();
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).
const rawText = properties.get('text').getValue();
this._pixiObject.text = rawText;
const opacity = +properties.get('opacity').getValue();
this._pixiObject.alpha = opacity / 255;
const align = properties.get('align').getValue();
this._pixiObject.align = align;
const color = properties.get('tint').getValue();
this._pixiObject.tint = objectsRenderingService.rgbOrHexToHexNumber(
color
);
const scale = +(properties.get('scale').getValue() || 1);
this._pixiObject.scale.set(scale);
// Track the changes in font to load the new requested font.
const bitmapFontResourceName = properties
.get('bitmapFontResourceName')
.getValue();
const textureAtlasResourceName = properties
.get('textureAtlasResourceName')
.getValue();
if (
this._currentBitmapFontResourceName !== bitmapFontResourceName ||
this._currentTextureAtlasResourceName !== textureAtlasResourceName
) {
// Release the old font (if it was installed).
releaseBitmapFont(this._pixiObject.fontName);
// Temporarily go back to the default font, as the PIXI.BitmapText
// object does not support being displayed with a font not installed at all.
// It will be replaced as soon as the proper font is loaded.
this._pixiObject.fontName = getDefaultBitmapFont().font;
this._currentBitmapFontResourceName = bitmapFontResourceName;
this._currentTextureAtlasResourceName = textureAtlasResourceName;
obtainBitmapFont(
this._pixiResourcesLoader,
this._project,
this._currentBitmapFontResourceName,
this._currentTextureAtlasResourceName
).then((bitmapFont) => {
this._pixiObject.fontName = bitmapFont.font;
this._pixiObject.fontSize = bitmapFont.size;
this._pixiObject.dirty = true;
});
}
// Set up the wrapping width if enabled.
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap && this._instance.hasCustomSize()) {
this._pixiObject.maxWidth =
this.getCustomWidth() / this._pixiObject.scale.x;
this._pixiObject.dirty = true;
} else {
this._pixiObject.maxWidth = 0;
this._pixiObject.dirty = true;
}
this._pixiObject.position.x =
this._instance.getX() + (this._pixiObject.textWidth * scale) / 2;
this._pixiObject.position.y =
this._instance.getY() + (this._pixiObject.textHeight * scale) / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
}
this._pixiObject.position.x =
this._instance.getX() + (this._pixiObject.textWidth * scale) / 2;
this._pixiObject.position.y =
this._instance.getY() + (this._pixiObject.textHeight * scale) / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
};
onRemovedFromScene() {
RenderedInstance.prototype.onRemovedFromScene.call(this);
RenderedBitmapTextInstance.prototype.onRemovedFromScene = function () {
RenderedInstance.prototype.onRemovedFromScene.call(this);
const fontName = this._pixiObject.fontName;
this._pixiObject.destroy();
releaseBitmapFont(fontName);
}
const fontName = this._pixiObject.fontName;
this._pixiObject.destroy();
releaseBitmapFont(fontName);
};
/**
* Return the width of the instance, when it's not resized.
*/
getDefaultWidth() {
return this._pixiObject.width;
}
/**
* Return the width of the instance, when it's not resized.
*/
RenderedBitmapTextInstance.prototype.getDefaultWidth = function () {
return this._pixiObject.width;
};
/**
* Return the height of the instance, when it's not resized.
*/
RenderedBitmapTextInstance.prototype.getDefaultHeight = function () {
return this._pixiObject.height;
};
/**
* Return the height of the instance, when it's not resized.
*/
getDefaultHeight() {
return this._pixiObject.height;
}
}
objectsRenderingService.registerInstanceRenderer(
'BitmapText::BitmapTextObject',

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -114,10 +106,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,450 +13,389 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
"DeviceSensors",
_("Device sensors"),
_(
"Allow the game to access the sensors of a mobile device."
),
"Matthias Meike",
"Open source (MIT License)"
).setExtensionHelpPath("/all-features/device-sensors")
.setCategory('Input');
extension.addInstructionOrExpressionGroupMetadata(_("Device sensors"))
.setIcon("JsPlatform/Extensions/orientation_active32.png");
extension
.setExtensionInformation(
'DeviceSensors',
_('Device sensors'),
_('Allow the game to access the sensors of a mobile device.'),
'Matthias Meike',
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/device-sensors')
.setCategory('Input');
extension
.addInstructionOrExpressionGroupMetadata(_('Device sensors'))
.setIcon('JsPlatform/Extensions/orientation_active32.png');
extension
.addCondition(
"OrientationSensorActive",
_("Sensor active"),
'OrientationSensorActive',
_('Sensor active'),
_(
"The condition is true if the device orientation sensor is currently active"
'The condition is true if the device orientation sensor is currently active'
),
_("Orientation sensor is active"),
_("Orientation"),
"JsPlatform/Extensions/orientation_active32.png",
"JsPlatform/Extensions/orientation_active32.png"
_('Orientation sensor is active'),
_('Orientation'),
'JsPlatform/Extensions/orientation_active32.png',
'JsPlatform/Extensions/orientation_active32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.isActive");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.isActive');
extension
extension
.addCondition(
"OrientationAlpha",
_("Compare the value of orientation alpha"),
_(
"Compare the value of orientation alpha. (Range: 0 to 360°)"
),
_("the orientation alpha"),
_("Orientation"),
"JsPlatform/Extensions/orientation_alpha32.png",
"JsPlatform/Extensions/orientation_alpha32.png"
'OrientationAlpha',
_('Compare the value of orientation alpha'),
_('Compare the value of orientation alpha. (Range: 0 to 360°)'),
_('the orientation alpha'),
_('Orientation'),
'JsPlatform/Extensions/orientation_alpha32.png',
'JsPlatform/Extensions/orientation_alpha32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationAlpha");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationAlpha');
extension
extension
.addCondition(
"OrientationBeta",
_("Compare the value of orientation beta"),
_(
"Compare the value of orientation beta. (Range: -180 to 180°)"
),
_("the orientation beta"),
_("Orientation"),
"JsPlatform/Extensions/orientation_beta32.png",
"JsPlatform/Extensions/orientation_beta32.png"
'OrientationBeta',
_('Compare the value of orientation beta'),
_('Compare the value of orientation beta. (Range: -180 to 180°)'),
_('the orientation beta'),
_('Orientation'),
'JsPlatform/Extensions/orientation_beta32.png',
'JsPlatform/Extensions/orientation_beta32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationBeta");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationBeta');
extension
extension
.addCondition(
"OrientationGamma",
_("Compare the value of orientation gamma"),
_(
"Compare the value of orientation gamma. (Range: -90 to 90°)"
),
_("the orientation gamma"),
_("Orientation"),
"JsPlatform/Extensions/orientation_gamma32.png",
"JsPlatform/Extensions/orientation_gamma32.png"
'OrientationGamma',
_('Compare the value of orientation gamma'),
_('Compare the value of orientation gamma. (Range: -90 to 90°)'),
_('the orientation gamma'),
_('Orientation'),
'JsPlatform/Extensions/orientation_gamma32.png',
'JsPlatform/Extensions/orientation_gamma32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationGamma");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationGamma');
extension
.addAction(
"ActivateOrientationListener",
_("Activate orientation sensor"),
_("Activate the orientation sensor. (remember to turn it off again)"),
_("Activate the orientation sensor."),
_("Orientation"),
"JsPlatform/Extensions/orientation_active32.png",
"JsPlatform/Extensions/orientation_active32.png"
'ActivateOrientationListener',
_('Activate orientation sensor'),
_('Activate the orientation sensor. (remember to turn it off again)'),
_('Activate the orientation sensor.'),
_('Orientation'),
'JsPlatform/Extensions/orientation_active32.png',
'JsPlatform/Extensions/orientation_active32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.activateOrientationSensor");
.getCodeExtraInformation()
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName(
'gdjs.deviceSensors.orientation.activateOrientationSensor'
);
extension
.addAction(
"DeactivateOrientationListener",
_("Deactivate orientation sensor"),
_("Deactivate the orientation sensor."),
_("Deactivate the orientation sensor."),
_("Orientation"),
"JsPlatform/Extensions/orientation_inactive32.png",
"JsPlatform/Extensions/orientation_inactive32.png"
'DeactivateOrientationListener',
_('Deactivate orientation sensor'),
_('Deactivate the orientation sensor.'),
_('Deactivate the orientation sensor.'),
_('Orientation'),
'JsPlatform/Extensions/orientation_inactive32.png',
'JsPlatform/Extensions/orientation_inactive32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.deactivateOrientationSensor");
.getCodeExtraInformation()
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName(
'gdjs.deviceSensors.orientation.deactivateOrientationSensor'
);
extension
.addExpression(
"OrientationAbsolute",
_("Is Absolute"),
_("Get if the devices orientation is absolute and not relative"),
_("Orientation"),
"JsPlatform/Extensions/orientation_absolute16.png"
'OrientationAbsolute',
_('Is Absolute'),
_('Get if the devices orientation is absolute and not relative'),
_('Orientation'),
'JsPlatform/Extensions/orientation_absolute16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationAbsolute");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationAbsolute');
extension
.addExpression(
"OrientationAlpha",
_("Alpha value"),
_("Get the devices orientation Alpha (compass)"),
_("Orientation"),
"JsPlatform/Extensions/orientation_alpha16.png"
'OrientationAlpha',
_('Alpha value'),
_('Get the devices orientation Alpha (compass)'),
_('Orientation'),
'JsPlatform/Extensions/orientation_alpha16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationAlpha");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationAlpha');
extension
.addExpression(
"OrientationBeta",
_("Beta value"),
_("Get the devices orientation Beta"),
_("Orientation"),
"JsPlatform/Extensions/orientation_beta16.png"
'OrientationBeta',
_('Beta value'),
_('Get the devices orientation Beta'),
_('Orientation'),
'JsPlatform/Extensions/orientation_beta16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationBeta");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationBeta');
extension
.addExpression(
"OrientationGamma",
_("Gamma value"),
_("Get the devices orientation Gamma value"),
_("Orientation"),
"JsPlatform/Extensions/orientation_gamma16.png"
'OrientationGamma',
_('Gamma value'),
_('Get the devices orientation Gamma value'),
_('Orientation'),
'JsPlatform/Extensions/orientation_gamma16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.orientation.getOrientationGamma");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.orientation.getOrientationGamma');
extension
extension
.addCondition(
"MotionSensorActive",
_("Sensor active"),
'MotionSensorActive',
_('Sensor active'),
_(
"The condition is true if the device motion sensor is currently active"
'The condition is true if the device motion sensor is currently active'
),
_("Motion sensor is active"),
_("Motion"),
"JsPlatform/Extensions/motion_active32.png",
"JsPlatform/Extensions/motion_active32.png"
_('Motion sensor is active'),
_('Motion'),
'JsPlatform/Extensions/motion_active32.png',
'JsPlatform/Extensions/motion_active32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.isActive");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.isActive');
extension
extension
.addCondition(
"RotationAlpha",
_("Compare the value of rotation alpha"),
'RotationAlpha',
_('Compare the value of rotation alpha'),
_(
"Compare the value of rotation alpha. (Note: few devices support this sensor)"
'Compare the value of rotation alpha. (Note: few devices support this sensor)'
),
_("the rotation alpha"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_alpha32.png",
"JsPlatform/Extensions/motion_rotation_alpha32.png"
_('the rotation alpha'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_alpha32.png',
'JsPlatform/Extensions/motion_rotation_alpha32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationAlpha");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationAlpha');
extension
extension
.addCondition(
"RotationBeta",
_("Compare the value of rotation beta"),
'RotationBeta',
_('Compare the value of rotation beta'),
_(
"Compare the value of rotation beta. (Note: few devices support this sensor)"
'Compare the value of rotation beta. (Note: few devices support this sensor)'
),
_("the rotation beta"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_beta32.png",
"JsPlatform/Extensions/motion_rotation_beta32.png"
_('the rotation beta'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_beta32.png',
'JsPlatform/Extensions/motion_rotation_beta32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationBeta");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationBeta');
extension
extension
.addCondition(
"RotationGamma",
_("Compare the value of rotation gamma"),
'RotationGamma',
_('Compare the value of rotation gamma'),
_(
"Compare the value of rotation gamma. (Note: few devices support this sensor)"
'Compare the value of rotation gamma. (Note: few devices support this sensor)'
),
_("the rotation gamma"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_gamma32.png",
"JsPlatform/Extensions/motion_rotation_gamma32.png"
_('the rotation gamma'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_gamma32.png',
'JsPlatform/Extensions/motion_rotation_gamma32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationGamma");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationGamma');
extension
extension
.addCondition(
"AccelerationX",
_("Compare the value of acceleration on X-axis"),
_(
"Compare the value of acceleration on the X-axis (m/s²)."
),
_("the acceleration X"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_x32.png",
"JsPlatform/Extensions/motion_acceleration_x32.png"
'AccelerationX',
_('Compare the value of acceleration on X-axis'),
_('Compare the value of acceleration on the X-axis (m/s²).'),
_('the acceleration X'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_x32.png',
'JsPlatform/Extensions/motion_acceleration_x32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationX");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationX');
extension
extension
.addCondition(
"AccelerationY",
_("Compare the value of acceleration on Y-axis"),
_(
"Compare the value of acceleration on the Y-axis (m/s²)."
),
_("the acceleration Y"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_y32.png",
"JsPlatform/Extensions/motion_acceleration_y32.png"
'AccelerationY',
_('Compare the value of acceleration on Y-axis'),
_('Compare the value of acceleration on the Y-axis (m/s²).'),
_('the acceleration Y'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_y32.png',
'JsPlatform/Extensions/motion_acceleration_y32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationY");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationY');
extension
extension
.addCondition(
"AccelerationZ",
_("Compare the value of acceleration on Z-axis"),
_(
"Compare the value of acceleration on the Z-axis (m/s²)."
),
_("the acceleration Z"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_z32.png",
"JsPlatform/Extensions/motion_acceleration_z32.png"
'AccelerationZ',
_('Compare the value of acceleration on Z-axis'),
_('Compare the value of acceleration on the Z-axis (m/s²).'),
_('the acceleration Z'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_z32.png',
'JsPlatform/Extensions/motion_acceleration_z32.png'
)
.addParameter("relationalOperator", _("Sign of the test"), "number")
.addParameter("expression", _("Value (m/s²)"))
.addParameter('relationalOperator', _('Sign of the test'), 'number')
.addParameter('expression', _('Value (m/s²)'))
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationZ");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationZ');
extension
.addAction(
"ActivateMotionListener",
_("Activate motion sensor"),
_("Activate the motion sensor. (remember to turn it off again)"),
_("Activate the motion sensor."),
_("Motion"),
"JsPlatform/Extensions/motion_active32.png",
"JsPlatform/Extensions/motion_active32.png"
'ActivateMotionListener',
_('Activate motion sensor'),
_('Activate the motion sensor. (remember to turn it off again)'),
_('Activate the motion sensor.'),
_('Motion'),
'JsPlatform/Extensions/motion_active32.png',
'JsPlatform/Extensions/motion_active32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.activateMotionSensor");
.getCodeExtraInformation()
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.activateMotionSensor');
extension
.addAction(
"DeactivateMotionListener",
_("Deactivate motion sensor"),
_("Deactivate the motion sensor."),
_("Deactivate the motion sensor."),
_("Motion"),
"JsPlatform/Extensions/motion_inactive32.png",
"JsPlatform/Extensions/motion_inactive32.png"
'DeactivateMotionListener',
_('Deactivate motion sensor'),
_('Deactivate the motion sensor.'),
_('Deactivate the motion sensor.'),
_('Motion'),
'JsPlatform/Extensions/motion_inactive32.png',
'JsPlatform/Extensions/motion_inactive32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.deactivateMotionSensor");
.getCodeExtraInformation()
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.deactivateMotionSensor');
extension
.addExpression(
"RotationAlpha",
_("Alpha value"),
_("Get the devices rotation Alpha"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_alpha16.png"
'RotationAlpha',
_('Alpha value'),
_('Get the devices rotation Alpha'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_alpha16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationAlpha");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationAlpha');
extension
.addExpression(
"RotationBeta",
_("Beta value"),
_("Get the devices rotation Beta"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_beta16.png"
'RotationBeta',
_('Beta value'),
_('Get the devices rotation Beta'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_beta16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationBeta");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationBeta');
extension
.addExpression(
"RotationGamma",
_("Gamma value"),
_("Get the devices rotation Gamma"),
_("Motion"),
"JsPlatform/Extensions/motion_rotation_gamma16.png"
'RotationGamma',
_('Gamma value'),
_('Get the devices rotation Gamma'),
_('Motion'),
'JsPlatform/Extensions/motion_rotation_gamma16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getRotationGamma");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getRotationGamma');
extension
extension
.addExpression(
"AccelerationX",
_("Acceleration X value"),
_("Get the devices acceleration on the X-axis (m/s²)"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_x16.png"
'AccelerationX',
_('Acceleration X value'),
_('Get the devices acceleration on the X-axis (m/s²)'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_x16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationX");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationX');
extension
extension
.addExpression(
"AccelerationY",
_("Acceleration Y value"),
_("Get the devices acceleration on the Y-axis (m/s²)"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_y16.png"
'AccelerationY',
_('Acceleration Y value'),
_('Get the devices acceleration on the Y-axis (m/s²)'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_y16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationY");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationY');
extension
extension
.addExpression(
"AccelerationZ",
_("Acceleration Z value"),
_("Get the devices acceleration on the Z-axis (m/s²)"),
_("Motion"),
"JsPlatform/Extensions/motion_acceleration_z16.png"
'AccelerationZ',
_('Acceleration Z value'),
_('Get the devices acceleration on the Z-axis (m/s²)'),
_('Motion'),
'JsPlatform/Extensions/motion_acceleration_z16.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/DeviceSensors/devicesensortools.js"
)
.setFunctionName("gdjs.deviceSensors.motion.getAccelerationZ");
.setIncludeFile('Extensions/DeviceSensors/devicesensortools.js')
.setFunctionName('gdjs.deviceSensors.motion.getAccelerationZ');
return extension;
},
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) { return []; },
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -36,8 +28,9 @@ module.exports = {
)
.setExtensionHelpPath('/all-features/device-vibration')
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("Device vibration"))
.setIcon("JsPlatform/Extensions/vibration_start32.png");
extension
.addInstructionOrExpressionGroupMetadata(_('Device vibration'))
.setIcon('JsPlatform/Extensions/vibration_start32.png');
extension
.addDependency()
@@ -99,10 +92,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -721,10 +713,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,28 +13,20 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'Effects',
'Effects',
'Lots of different effects to be used in your game.',
'Various contributors from PixiJS, PixiJS filters and GDevelop',
'MIT'
)
.setCategory('Visual effect')
.setExtensionHelpPath('/interface/scene-editor/layer-effects');
extension
.setExtensionInformation(
'Effects',
'Effects',
'Lots of different effects to be used in your game.',
'Various contributors from PixiJS, PixiJS filters and GDevelop',
'MIT'
)
.setCategory('Visual effect')
.setExtensionHelpPath('/interface/scene-editor/layer-effects');
// You can declare an effect here. Please order the effects by alphabetical order.
// This file is for common effects that are well-known/"battle-tested". If you have an
@@ -230,7 +223,11 @@ module.exports = {
const blurEffect = extension
.addEffect('Blur')
.setFullName(_('Blur (Gaussian, slow - prefer to use Kawase blur)'))
.setDescription(_('Blur the rendered image. This is slow, so prefer to use Kawase blur in most cases.'))
.setDescription(
_(
'Blur the rendered image. This is slow, so prefer to use Kawase blur in most cases.'
)
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/blur-pixi-filter.js');
const blurProperties = blurEffect.getProperties();
@@ -728,13 +725,11 @@ module.exports = {
const hslAdjustmentEffect = extension
.addEffect('HslAdjustment')
.setFullName(_('HSL Adjustment'))
.setDescription(
_(
'Adjust hue, saturation and lightness.'
)
)
.setDescription(_('Adjust hue, saturation and lightness.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-hsl-adjustment.js')
.addIncludeFile(
'Extensions/Effects/pixi-filters/filter-hsl-adjustment.js'
)
.addIncludeFile('Extensions/Effects/hsl-adjustment-pixi-filter.js');
const hslAdjustmentProperties = hslAdjustmentEffect.getProperties();
hslAdjustmentProperties
@@ -767,7 +762,9 @@ module.exports = {
.addEffect('KawaseBlur')
.setFullName(_('Blur (Kawase, fast)'))
.setDescription(
_('Blur the rendered image, with much better performance than Gaussian blur.')
_(
'Blur the rendered image, with much better performance than Gaussian blur.'
)
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-kawase-blur.js')
@@ -816,9 +813,7 @@ module.exports = {
const motionBlurEffect = extension
.addEffect('MotionBlur')
.setFullName(_('Motion Blur'))
.setDescription(
_('Blur the rendered image to give a feeling of speed.')
)
.setDescription(_('Blur the rendered image to give a feeling of speed.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-motion-blur.js')
.addIncludeFile('Extensions/Effects/motion-blur-pixi-filter.js');
@@ -1174,7 +1169,9 @@ module.exports = {
.setValue('0')
.setLabel(_('Elapsed time'))
.setType('number')
.setDescription('It can be set back to 0 to play the shockwave animation again.');
.setDescription(
'It can be set back to 0 to play the shockwave animation again.'
);
shockwaveEffectProperties
.getOrCreate('speed')
.setValue('500')
@@ -1311,10 +1308,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'MyDummyExtension',
@@ -153,7 +145,6 @@ module.exports = {
// Everything that is stored inside the behavior is in "behaviorContent" and is automatically
// saved/loaded to JSON.
var dummyBehavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehavior.updateProperty = function (
behaviorContent,
propertyName,
@@ -170,7 +161,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehavior.getProperties = function (behaviorContent) {
var behaviorProperties = new gd.MapStringPropertyDescriptor();
@@ -187,7 +177,6 @@ module.exports = {
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehavior.initializeContent = function (behaviorContent) {
behaviorContent.setStringAttribute('property1', 'Initial value 1');
behaviorContent.setBoolAttribute('property2', true);
@@ -201,6 +190,7 @@ module.exports = {
'',
'CppPlatform/Extensions/topdownmovementicon.png',
'DummyBehavior',
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
dummyBehavior,
new gd.BehaviorsSharedData()
)
@@ -215,7 +205,6 @@ module.exports = {
// Create a new gd.BehaviorSharedDataJsImplementation object and implement the methods
// that are called to get and set the properties of the shared data.
var dummyBehaviorWithSharedData = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehaviorWithSharedData.updateProperty = function (
behaviorContent,
propertyName,
@@ -228,7 +217,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehaviorWithSharedData.getProperties = function (behaviorContent) {
var behaviorProperties = new gd.MapStringPropertyDescriptor();
@@ -238,13 +226,11 @@ module.exports = {
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
dummyBehaviorWithSharedData.initializeContent = function (behaviorContent) {
behaviorContent.setStringAttribute('property1', 'Initial value 1');
};
var sharedData = new gd.BehaviorSharedDataJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.updateProperty = function (
sharedContent,
propertyName,
@@ -257,7 +243,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.getProperties = function (sharedContent) {
var sharedProperties = new gd.MapStringPropertyDescriptor();
@@ -267,7 +252,6 @@ module.exports = {
return sharedProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.initializeContent = function (behaviorContent) {
behaviorContent.setStringAttribute(
'sharedProperty1',
@@ -284,6 +268,7 @@ module.exports = {
'',
'CppPlatform/Extensions/topdownmovementicon.png',
'DummyBehaviorWithSharedData',
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
dummyBehaviorWithSharedData,
sharedData
)
@@ -302,7 +287,6 @@ module.exports = {
// Everything that is stored inside the object is in "content" and is automatically
// saved/loaded to JSON.
var dummyObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
dummyObject.updateProperty = function (
objectContent,
propertyName,
@@ -327,7 +311,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
dummyObject.getProperties = function (objectContent) {
var objectProperties = new gd.MapStringPropertyDescriptor();
@@ -362,7 +345,6 @@ module.exports = {
})
);
// $FlowExpectedError - ignore Flow warning as we're creating an object
dummyObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -382,7 +364,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
dummyObject.getInitialInstanceProperties = function (
content,
instance,
@@ -446,10 +427,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
const dummyBehavior = extension
.getBehaviorMetadata('MyDummyExtension::DummyBehavior')
.get();
@@ -474,9 +452,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'MyDummyExtension::DummyObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -489,9 +465,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,368 +13,414 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
"FacebookInstantGames",
_("Facebook Instant Games"),
'FacebookInstantGames',
_('Facebook Instant Games'),
_(
"Allow your game to send scores and interact with the Facebook Instant Games platform."
'Allow your game to send scores and interact with the Facebook Instant Games platform.'
),
"Florian Rival",
"Open source (MIT License)"
'Florian Rival',
'Open source (MIT License)'
)
.setExtensionHelpPath("/publishing/publishing-to-facebook-instant-games")
.setExtensionHelpPath('/publishing/publishing-to-facebook-instant-games')
.setCategory('Third-party');
extension.addInstructionOrExpressionGroupMetadata(_("Facebook Instant Games"))
.setIcon("JsPlatform/Extensions/facebookicon32.png");
extension
.addInstructionOrExpressionGroupMetadata(_('Facebook Instant Games'))
.setIcon('JsPlatform/Extensions/facebookicon32.png');
extension
.addAction(
"SavePlayerData",
_("Save player data"),
'SavePlayerData',
_('Save player data'),
_(
"Save the content of the given scene variable in the player data, stored on Facebook Instant Games servers"
'Save the content of the given scene variable in the player data, stored on Facebook Instant Games servers'
),
_(
"Save the content of _PARAM1_ in key _PARAM0_ of player data (store success message in _PARAM2_ or error in _PARAM3_)"
'Save the content of _PARAM1_ in key _PARAM0_ of player data (store success message in _PARAM2_ or error in _PARAM3_)'
),
_("Player data"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
_('Player data'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter("string", 'Data key name (e.g: "Lives")', "", false)
.addParameter("scenevar", "Scene variable with the content to save", "", false)
.addParameter('string', 'Data key name (e.g: "Lives")', '', false)
.addParameter(
"scenevar",
_("Variable where to store the success message (optional)"),
"",
true
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.setPlayerData");
extension
.addAction(
"LoadPlayerData",
_("Load player data"),
_("Load the player data with the given key in a variable"),
_(
"Load player data with key _PARAM0_ in _PARAM1_ (or error in _PARAM2_)"
),
_("Player data"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
)
.addParameter("string", _('Data key name (e.g: "Lives")'), "", false)
.addParameter(
"scenevar",
_("Variable where to store loaded data"),
"",
'scenevar',
'Scene variable with the content to save',
'',
false
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_('Variable where to store the success message (optional)'),
'',
true
)
.addParameter(
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.loadPlayerData");
.setFunctionName('gdjs.evtTools.facebookInstantGames.setPlayerData');
extension
.addAction(
"SavePlayerScore",
_("Save player score"),
'LoadPlayerData',
_('Load player data'),
_('Load the player data with the given key in a variable'),
_(
"Save the score, and optionally the content of the given variable in the player score, for the given metadata."
'Load player data with key _PARAM0_ in _PARAM1_ (or error in _PARAM2_)'
),
_(
"In leaderboard _PARAM0_, save score _PARAM1_ for the player and extra data from _PARAM2_ (store success message in _PARAM3_ or error in _PARAM4_)"
),
_("Leaderboards"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
_('Player data'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter('string', _('Data key name (e.g: "Lives")'), '', false)
.addParameter(
'scenevar',
_('Variable where to store loaded data'),
'',
false
)
.addParameter(
"string",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName('gdjs.evtTools.facebookInstantGames.loadPlayerData');
extension
.addAction(
'SavePlayerScore',
_('Save player score'),
_(
'Save the score, and optionally the content of the given variable in the player score, for the given metadata.'
),
_(
'In leaderboard _PARAM0_, save score _PARAM1_ for the player and extra data from _PARAM2_ (store success message in _PARAM3_ or error in _PARAM4_)'
),
_('Leaderboards'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
'string',
'Leaderboard name (e.g: "PlayersBestTimes")',
"",
'',
false
)
.addParameter("expression", "Score to register for the player", "", false)
.addParameter('expression', 'Score to register for the player', '', false)
.addParameter(
"scenevar",
_("Optional variable with metadata to save"),
"",
'scenevar',
_('Optional variable with metadata to save'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store the success message (optional)"),
"",
'scenevar',
_('Variable where to store the success message (optional)'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.setPlayerScore");
extension
.addAction(
"LoadPlayerEntry",
_("Load player entry"),
_("Load the player entry in the given leaderboard"),
'scenevar',
_(
"Load player entry from leaderboard _PARAM0_. Set rank in _PARAM1_, score in _PARAM2_ (extra data if any in _PARAM3_ and error in _PARAM4_)"
'Variable where to store the error message (optional, if an error occurs)'
),
_("Leaderboards"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName('gdjs.evtTools.facebookInstantGames.setPlayerScore');
extension
.addAction(
'LoadPlayerEntry',
_('Load player entry'),
_('Load the player entry in the given leaderboard'),
_(
'Load player entry from leaderboard _PARAM0_. Set rank in _PARAM1_, score in _PARAM2_ (extra data if any in _PARAM3_ and error in _PARAM4_)'
),
_('Leaderboards'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"string",
'string',
_('Leaderboard name (e.g: "PlayersBestTimes")'),
"",
'',
false
)
.addParameter(
"scenevar",
_("Variable where to store the player rank (of -1 if not ranked)"),
"",
'scenevar',
_('Variable where to store the player rank (of -1 if not ranked)'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store the player score (of -1 if no score)"),
"",
'scenevar',
_('Variable where to store the player score (of -1 if no score)'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store extra data (if any)"),
"",
'scenevar',
_('Variable where to store extra data (if any)'),
'',
true
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.getPlayerEntry");
.setFunctionName('gdjs.evtTools.facebookInstantGames.getPlayerEntry');
extension
.addCondition(
"AreAdsSupported",
_("Check if ads are supported"),
_("Check if showing ads is supported on this device (only mobile phones can show ads)"),
_("Ads can be shown on this device"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'AreAdsSupported',
_('Check if ads are supported'),
_(
'Check if showing ads is supported on this device (only mobile phones can show ads)'
),
_('Ads can be shown on this device'),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.areAdsSupported");
.setFunctionName('gdjs.evtTools.facebookInstantGames.areAdsSupported');
extension
.addCondition(
"IsInterstitialAdReady",
_("Is the interstitial ad ready"),
_("Check if the interstitial ad requested from Facebook is loaded and ready to be shown."),
_("The interstitial ad is loaded and ready to be shown"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'IsInterstitialAdReady',
_('Is the interstitial ad ready'),
_(
'Check if the interstitial ad requested from Facebook is loaded and ready to be shown.'
),
_('The interstitial ad is loaded and ready to be shown'),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.isInterstitialAdReady");
.setFunctionName(
'gdjs.evtTools.facebookInstantGames.isInterstitialAdReady'
);
extension
.addAction(
"LoadInterstitialAd",
_("Load and prepare an interstitial ad"),
_("Request and load an interstitial ad from Facebook, so that it is ready to be shown."),
_("Request and load an interstitial ad from Facebook (ad placement id: _PARAM0_, error in _PARAM1_)"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'LoadInterstitialAd',
_('Load and prepare an interstitial ad'),
_(
'Request and load an interstitial ad from Facebook, so that it is ready to be shown.'
),
_(
'Request and load an interstitial ad from Facebook (ad placement id: _PARAM0_, error in _PARAM1_)'
),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"string",
_("The Ad Placement id (can be found while setting up the ad on Facebook)"),
"",
'string',
_(
'The Ad Placement id (can be found while setting up the ad on Facebook)'
),
'',
false
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.loadInterstitialAd");
.setFunctionName('gdjs.evtTools.facebookInstantGames.loadInterstitialAd');
extension
.addAction(
"ShowInterstitialAd",
_("Show the loaded interstitial ad"),
_("Show the interstitial ad previously loaded in memory. This won't work if you did not load the interstitial before."),
_("Show the interstitial ad previously loaded in memory (if any error, store it in _PARAM0_)"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'ShowInterstitialAd',
_('Show the loaded interstitial ad'),
_(
"Show the interstitial ad previously loaded in memory. This won't work if you did not load the interstitial before."
),
_(
'Show the interstitial ad previously loaded in memory (if any error, store it in _PARAM0_)'
),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.showInterstitialAd");
.setFunctionName('gdjs.evtTools.facebookInstantGames.showInterstitialAd');
extension
.addCondition(
"IsRewardedVideoReady",
_("Is the rewarded video ready"),
_("Check if the rewarded video requested from Facebook is loaded and ready to be shown."),
_("The rewarded video is loaded and ready to be shown"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'IsRewardedVideoReady',
_('Is the rewarded video ready'),
_(
'Check if the rewarded video requested from Facebook is loaded and ready to be shown.'
),
_('The rewarded video is loaded and ready to be shown'),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.isRewardedVideoReady");
.setFunctionName(
'gdjs.evtTools.facebookInstantGames.isRewardedVideoReady'
);
extension
.addAction(
"LoadRewardedVideo",
_("Load and prepare a rewarded video"),
_("Request and load a rewarded video from Facebook, so that it is ready to be shown."),
_("Request and load a rewarded video from Facebook (ad placement id: _PARAM0_, error in _PARAM1_)"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'LoadRewardedVideo',
_('Load and prepare a rewarded video'),
_(
'Request and load a rewarded video from Facebook, so that it is ready to be shown.'
),
_(
'Request and load a rewarded video from Facebook (ad placement id: _PARAM0_, error in _PARAM1_)'
),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"string",
_("The Ad Placement id (can be found while setting up the ad on Facebook)"),
"",
'string',
_(
'The Ad Placement id (can be found while setting up the ad on Facebook)'
),
'',
false
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.loadRewardedVideo");
.setFunctionName('gdjs.evtTools.facebookInstantGames.loadRewardedVideo');
extension
.addAction(
"ShowRewardedVideo",
_("Show the loaded rewarded video"),
_("Show the rewarded video previously loaded in memory. This won't work if you did not load the video before."),
_("Show the rewarded video previously loaded in memory (if any error, store it in _PARAM0_)"),
_("Ads"),
"JsPlatform/Extensions/facebookicon32.png",
"JsPlatform/Extensions/facebookicon32.png"
'ShowRewardedVideo',
_('Show the loaded rewarded video'),
_(
"Show the rewarded video previously loaded in memory. This won't work if you did not load the video before."
),
_(
'Show the rewarded video previously loaded in memory (if any error, store it in _PARAM0_)'
),
_('Ads'),
'JsPlatform/Extensions/facebookicon32.png',
'JsPlatform/Extensions/facebookicon32.png'
)
.addParameter(
"scenevar",
_("Variable where to store the error message (optional, if an error occurs)"),
"",
'scenevar',
_(
'Variable where to store the error message (optional, if an error occurs)'
),
'',
true
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.showRewardedVideo");
.setFunctionName('gdjs.evtTools.facebookInstantGames.showRewardedVideo');
extension
.addStrExpression(
"PlayerId",
_("Player identifier"),
_("Get the player unique identifier"),
'PlayerId',
_('Player identifier'),
_('Get the player unique identifier'),
'',
"JsPlatform/Extensions/facebookicon32.png"
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.getPlayerId");
.setFunctionName('gdjs.evtTools.facebookInstantGames.getPlayerId');
extension
.addStrExpression(
"PlayerName",
_("Player name"),
_("Get the player name"),
'PlayerName',
_('Player name'),
_('Get the player name'),
'',
"JsPlatform/Extensions/facebookicon32.png"
'JsPlatform/Extensions/facebookicon32.png'
)
.getCodeExtraInformation()
.setIncludeFile(
"Extensions/FacebookInstantGames/facebookinstantgamestools.js"
'Extensions/FacebookInstantGames/facebookinstantgamestools.js'
)
.setFunctionName("gdjs.evtTools.facebookInstantGames.getPlayerName");
.setFunctionName('gdjs.evtTools.facebookInstantGames.getPlayerName');
return extension;
},
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
runExtensionSanityTests: function (gd, extension) {
return [];
}
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -11,11 +12,10 @@
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
@@ -2314,10 +2314,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension */
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

238
Extensions/JsExtensionTypes.d.ts vendored Normal file
View File

@@ -0,0 +1,238 @@
type GDNamespace = typeof import('../GDevelop.js/types');
// This is necessary for typescript to interpret the identifier PIXI as a namespace
// in this file and merge it with the other namespace declarations.
declare namespace PIXI {}
/**
* RenderedInstance is the base class used for creating 2D renderers of instances,
* which display on the scene editor, using Pixi.js, the instance of an object (see InstancesEditor).
*/
class RenderedInstance {
_project: gd.Project;
_layout: gd.Layout;
_instance: gd.InitialInstance;
_associatedObjectConfiguration: gd.ObjectConfiguration;
_pixiContainer: PIXI.Container;
_pixiResourcesLoader: Class<PixiResourcesLoader>;
_pixiObject: PIXI.DisplayObject;
wasUsed: boolean;
constructor(
project: gdProject,
layout: gdLayout,
instance: gdInitialInstance,
associatedObjectConfiguration: gdObjectConfiguration,
pixiContainer: PIXI.Container,
pixiResourcesLoader: Class<PixiResourcesLoader>
);
/**
* Convert an angle from degrees to radians.
*/
static toRad(angleInDegrees: number): number;
/**
* Called when the scene editor is rendered.
*/
update(): void;
getPixiObject(): PIXI.DisplayObject | null;
getInstance(): gd.InitialInstance;
/**
* Called to notify the instance renderer that its associated instance was removed from
* the scene. The PIXI object should probably be removed from the container: This is what
* the default implementation of the method does.
*/
onRemovedFromScene(): void;
getOriginX(): number;
getOriginY(): number;
getCenterX(): number;
getCenterY(): number;
getCustomWidth(): number;
getCustomHeight(): number;
getWidth(): number;
getHeight(): number;
getDepth(): number;
/**
* Return the width of the instance when the instance doesn't have a custom size.
*/
getDefaultWidth(): number;
/**
* Return the height of the instance when the instance doesn't have a custom size.
*/
getDefaultHeight(): number;
getDefaultDepth(): number;
}
/**
* Rendered3DInstance is the base class used for creating 3D renderers of instances,
* which display on the scene editor, using Three.js, the instance of an object (see InstancesEditor).
* It can also display 2D artifacts on Pixi 2D plane (3D object shadow projected on the plane for instance).
*/
class Rendered3DInstance {
_project: gdProject;
_layout: gdLayout;
_instance: gdInitialInstance;
_associatedObjectConfiguration: gdObjectConfiguration;
_pixiContainer: PIXI.Container;
_threeGroup: THREE.Group;
_pixiResourcesLoader: Class<PixiResourcesLoader>;
_pixiObject: PIXI.DisplayObject;
_threeObject: THREE.Object3D | null;
wasUsed: boolean;
constructor(
project: gdProject,
layout: gdLayout,
instance: gdInitialInstance,
associatedObjectConfiguration: gdObjectConfiguration,
pixiContainer: PIXI.Container,
threeGroup: THREE.Group,
pixiResourcesLoader: Class<PixiResourcesLoader>
);
/**
* Convert an angle from degrees to radians.
*/
static toRad(angleInDegrees: number): number;
/**
* Applies ratio to value without intermediary value to avoid precision issues.
*/
static applyRatio({
oldReferenceValue,
newReferenceValue,
valueToApplyTo,
}: {
oldReferenceValue: number;
newReferenceValue: number;
valueToApplyTo: number;
}): number;
/**
* Called when the scene editor is rendered.
*/
update(): void;
getPixiObject(): PIXI.DisplayObject;
getThreeObject(): THREE.Object3D;
getInstance(): gd.InitialInstance;
/**
* Called to notify the instance renderer that its associated instance was removed from
* the scene. The PIXI object should probably be removed from the container: This is what
* the default implementation of the method does.
*/
onRemovedFromScene(): void;
getOriginX(): number;
getOriginY(): number;
getCenterX(): number;
getCenterY(): number;
getWidth(): number;
getHeight(): number;
getDepth(): number;
/**
* Return the width of the instance when the instance doesn't have a custom size.
*/
getDefaultWidth(): number;
/**
* Return the height of the instance when the instance doesn't have a custom size.
*/
getDefaultHeight(): number;
/**
* Return the depth of the instance when the instance doesn't have a custom size.
*/
getDefaultDepth(): number;
}
declare type ObjectsRenderingService = {
gd: GDNamespace;
PIXI: PIXI;
THREE: typeof import('../newIDE/app/node_modules/three');
THREE_ADDONS: { SkeletonUtils: any };
RenderedInstance: typeof RenderedInstance;
Rendered3DInstance: typeof Rendered3DInstance;
registerInstanceRenderer: (objectType: string, renderer: any) => void;
registerInstance3DRenderer: (objectType: string, renderer: any) => void;
requireModule: (dirname: string, moduleName: string) => any;
getThumbnail: (
project: gd.Project,
objectConfiguration: gd.ObjectConfiguration
) => string;
rgbOrHexToHexNumber: (value: string) => number;
registerClearCache: (clearCache: (_: any) => void) => void;
};
declare type ObjectsEditorService = {
registerEditorConfiguration: (
objectType: string,
editorConfiguration: any
) => void;
getDefaultObjectJsImplementationPropertiesEditor: ({
helpPagePath: string,
}) => any;
};
declare type ExtensionModule = {
createExtension: (
_: (string) => string,
gd: GDNamespace
) => gd.PlatformExtension;
/**
* You can optionally add sanity tests that will check the basic working
* of your extension behaviors/objects by instantiating behaviors/objects
* and setting the property to a given value.
*
* If you don't have any tests, you can simply return an empty array.
*
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: (
gd: GDNamespace,
extension: gd.PlatformExtension
) => string[];
/**
* Register editors for objects.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations?: (
objectsEditorService: ObjectsEditorService
) => void;
/**
* Register renderers for instance of objects on the scene editor.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers?: (
objectsRenderingService: ObjectsRenderingService
) => void;
};

View File

@@ -1,35 +0,0 @@
// @flow
/**
* @file This file contains the (Flow) types that are used in the JavaScript
* extensions declaration (i.e: JsExtension.js files).
* Extension runtime files are in TypeScript (ts files) and not using Flow.
*
* If you do changes here, run `node import-GDJS-Runtime.js` (in newIDE/app/scripts),
* and be sure that the types declared here are reflecting the types exposed by the editor.
*
* Note that Flow comments are used to avoid having to preprocess this file and the
* JsExtension.js files through Babel. This allows to keep plain JS files, while allowing
* Flow static type checking to be run on them when integrated in the editor.
*/
/*::
export type ObjectsRenderingService = {
gd: libGDevelop,
PIXI: any,
THREE: any,
THREE_ADDONS: {SkeletonUtils: any},
RenderedInstance: any,
Rendered3DInstance: any,
registerInstanceRenderer: (objectType: string, renderer: any) => void,
registerInstance3DRenderer: (objectType: string, renderer: any) => void,
requireModule: (dirname: string, moduleName: string) => any,
getThumbnail: (project: gdProject, objectConfiguration: gdObjectConfiguration) => string,
rgbOrHexToHexNumber: (value: string) => number,
registerClearCache: (clearCache: any => void) => void,
};
export type ObjectsEditorService = {
registerEditorConfiguration: (objectType: string, editorConfiguration: any) => void,
getDefaultObjectJsImplementationPropertiesEditor: ({| helpPagePath: string |}) => any,
};
*/

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -96,7 +88,9 @@ module.exports = {
.setIncludeFile('Extensions/Leaderboards/sha256.js')
.addIncludeFile('Extensions/Leaderboards/leaderboardstools.js')
.setFunctionName('gdjs.evtTools.leaderboards.saveConnectedPlayerScore')
.setAsyncFunctionName('gdjs.evtTools.leaderboards.saveConnectedPlayerScore');
.setAsyncFunctionName(
'gdjs.evtTools.leaderboards.saveConnectedPlayerScore'
);
extension
.addCondition(
@@ -299,10 +293,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,32 +13,23 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'Lighting',
_('Lights'),
extension
.setExtensionInformation(
'Lighting',
_('Lights'),
'This provides a light object, and a behavior to mark other objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
'Harsimran Virk',
'MIT'
)
.setCategory('Visual effect')
.setTags("light");
'This provides a light object, and a behavior to mark other objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
'Harsimran Virk',
'MIT'
)
.setCategory('Visual effect')
.setTags('light');
const lightObstacleBehavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
lightObstacleBehavior.updateProperty = function (
behaviorContent,
propertyName,
@@ -46,14 +38,12 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
lightObstacleBehavior.getProperties = function (behaviorContent) {
const behaviorProperties = new gd.MapStringPropertyDescriptor();
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
lightObstacleBehavior.initializeContent = function (behaviorContent) {};
extension
.addBehavior(
@@ -66,6 +56,7 @@ module.exports = {
'',
'CppPlatform/Extensions/lightObstacleIcon32.png',
'LightObstacleBehavior',
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
lightObstacleBehavior,
new gd.BehaviorsSharedData()
)
@@ -77,7 +68,6 @@ module.exports = {
const lightObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.updateProperty = function (
objectContent,
propertyName,
@@ -106,7 +96,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
@@ -160,7 +149,6 @@ module.exports = {
})
);
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -172,7 +160,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object.
lightObject.getInitialInstanceProperties = function (
content,
instance,
@@ -233,16 +220,11 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'Lighting::LightObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -255,9 +237,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
@@ -283,32 +263,34 @@ module.exports = {
);
this._radius = parseFloat(
this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('radius')
.getValue()
);
if (this._radius <= 0) this._radius = 1;
const color = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('color')
.getValue()
);
// The icon in the middle.
const lightIconSprite = new PIXI.Sprite(PIXI.Texture.from('CppPlatform/Extensions/lightIcon32.png'));
const lightIconSprite = new PIXI.Sprite(
PIXI.Texture.from('CppPlatform/Extensions/lightIcon32.png')
);
lightIconSprite.anchor.x = 0.5;
lightIconSprite.anchor.y = 0.5;
// The circle to show the radius of the light.
const radiusBorderWidth = 2;
const radiusGraphics = new PIXI.Graphics();
radiusGraphics.lineStyle(
radiusBorderWidth,
color,
0.8
radiusGraphics.lineStyle(radiusBorderWidth, color, 0.8);
radiusGraphics.drawCircle(
0,
0,
Math.max(1, this._radius - radiusBorderWidth)
);
radiusGraphics.drawCircle(0, 0, Math.max(1, this._radius - radiusBorderWidth));
this._pixiObject = new PIXI.Container();
this._pixiObject.addChild(lightIconSprite);
@@ -326,11 +308,7 @@ module.exports = {
/**
* Return the path to the thumbnail of the specified object.
*/
static getThumbnail(
project,
resourcesLoader,
objectConfiguration
) {
static getThumbnail(project, resourcesLoader, objectConfiguration) {
return 'CppPlatform/Extensions/lightIcon32.png';
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -422,6 +422,17 @@ namespace gdjs {
});
};
/**
* Forces the usage of a relay (TURN) server, to avoid sharing IP addresses with the other peers.
* @param shouldUseRelayServer Whether relay-only should be enabled or disabled.
*/
export const forceUseRelayServer = (shouldUseRelayServer: boolean) => {
peerConfig.config = peerConfig.config || {};
peerConfig.config.iceTransportPolicy = shouldUseRelayServer
? 'relay'
: 'all';
};
/**
* Overrides the default peer ID. Must be called before connecting to a broker.
* Overriding the ID may have unwanted consequences. Do not use this feature

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension /*: gdPlatformExtension */ = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -182,6 +174,33 @@ module.exports = {
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.useCustomICECandidate');
extension
.addAction(
'ForceRelayServer',
_('Disable IP address sharing'),
_(
'Disables the sharing of IP addresses with the other peers. ' +
'This action needs to be called BEFORE connecting to the broker server.'
),
_('Disable IP sharing: _PARAM0_'),
'',
'JsPlatform/Extensions/p2picon.svg',
'JsPlatform/Extensions/p2picon.svg'
)
.addParameter(
'yesorno',
_('Disable sharing of IP addresses'),
'Generally, it is recommended to keep sharing of IP addressed enabled ' +
'to make connections faster and more often possible. ' +
'Disabling IP address sharing will force all connections to pass messages through a ' +
'TURN relay server, you can make P2P use one by adding one as an ICE candidate.',
false
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/P2P/A_peer.js')
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
.setFunctionName('gdjs.evtTools.p2p.forceUseRelayServer');
extension
.addAction(
'UseDefaultBroker',
@@ -474,10 +493,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -68,7 +68,10 @@ namespace gdjs {
max: objectData.particleLifeTimeMax,
},
// A negative flow is "infinite flow" (all particles burst)
frequency: objectData.flow < 0 ? 0.0001 : 1.0 / objectData.flow,
frequency:
objectData.flow < 0
? ParticleEmitterObjectPixiRenderer.frequencyMinimumValue
: 1.0 / objectData.flow,
spawnChance: 1,
particlesPerWave: objectData.flow < 0 ? objectData.maxParticleNb : 1,
maxParticles: objectData.maxParticleNb,
@@ -347,7 +350,10 @@ namespace gdjs {
}
setFlow(flow: number, tank: number): void {
this.emitter.frequency = flow < 0 ? 0.0001 : 1.0 / flow;
this.emitter.frequency =
flow < 0
? ParticleEmitterObjectPixiRenderer.frequencyMinimumValue
: 1.0 / flow;
this.emitter.emitterLifetime = ParticleEmitterObjectPixiRenderer.computeLifetime(
flow,
tank
@@ -420,11 +426,33 @@ namespace gdjs {
return this.started;
}
/**
* @returns `true` at the end of emission or at the start if it's paused.
* Returns false if there is no limit.
*/
_mayHaveEndedEmission(): boolean {
return (
// No end can be reached if there is no flow.
this.emitter.frequency >
ParticleEmitterObjectPixiRenderer.frequencyMinimumValue &&
// No end can be reached when there is no limit.
this.emitter.emitterLifetime >= 0 &&
// Pixi stops the emission at the end.
!this.emitter.emit &&
// Pixi reset `_emitterLife` to `emitterLifetime` at the end of emission
// so there is no way to know if it is the end or the start.
// @ts-ignore Use a private attribute.
this.emitter._emitterLife === this.emitter.emitterLifetime
);
}
static computeLifetime(flow: number, tank: number): float {
if (tank < 0) return -1;
else if (flow < 0) return 0.001;
else return (tank + 0.1) / flow;
}
private static readonly frequencyMinimumValue = 0.0001;
}
// @ts-ignore - Register the class to let the engine use it.

View File

@@ -113,6 +113,11 @@ namespace gdjs {
_additiveRenderingDirty: boolean = true;
// Don't mark texture as dirty if not using one.
_textureDirty: boolean;
/**
* `true` only when the emission is paused by events.
* It allows to tell the end of emission apart from it.
*/
_isEmissionPaused: boolean = false;
// @ts-ignore
_renderer: gdjs.ParticleEmitterObjectRenderer;
@@ -390,9 +395,11 @@ namespace gdjs {
this._additiveRenderingDirty = this._maxParticlesCountDirty = this._particleRotationSpeedDirty = false;
this._renderer.update(this.getElapsedTime() / 1000.0);
if (
this._renderer.hasStarted() &&
this.destroyWhenNoParticles &&
this.getParticleCount() === 0 &&
this.destroyWhenNoParticles
this._renderer.hasStarted() &&
!this._isEmissionPaused &&
this._renderer._mayHaveEndedEmission()
) {
this.deleteFromScene(instanceContainer);
}
@@ -803,10 +810,12 @@ namespace gdjs {
}
startEmission(): void {
this._isEmissionPaused = false;
this._renderer.start();
}
stopEmission(): void {
this._isEmissionPaused = true;
this._renderer.stop();
}

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -35,13 +27,12 @@ module.exports = {
)
.setExtensionHelpPath('/behaviors/physics2')
.setCategory('Movement')
.setTags("physics, gravity, obstacle, collision");
.setTags('physics, gravity, obstacle, collision');
extension
.addInstructionOrExpressionGroupMetadata(_('Physics Engine 2.0'))
.setIcon('res/physics32.png');
var physics2Behavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
physics2Behavior.updateProperty = function (
behaviorContent,
propertyName,
@@ -51,104 +42,138 @@ module.exports = {
behaviorContent.getChild('bodyType').setStringValue(newValue);
return true;
}
if (propertyName === 'bullet') {
behaviorContent.getChild('bullet').setBoolValue(newValue === '1');
return true;
}
if (propertyName === 'fixedRotation') {
behaviorContent
.getChild('fixedRotation')
.setBoolValue(newValue === '1');
return true;
}
if (propertyName === 'canSleep') {
behaviorContent.getChild('canSleep').setBoolValue(newValue === '1');
return true;
}
if (propertyName === 'shape') {
behaviorContent.getChild('shape').setStringValue(newValue);
return true;
}
if (propertyName === 'shapeDimensionA') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('shapeDimensionA').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('shapeDimensionA')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'shapeDimensionB') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('shapeDimensionB').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('shapeDimensionB')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'shapeOffsetX') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('shapeOffsetX').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('shapeOffsetX')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'shapeOffsetY') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('shapeOffsetY').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('shapeOffsetY')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'polygonOrigin') {
behaviorContent.addChild('polygonOrigin').setStringValue(newValue);
return true;
}
if (propertyName === 'vertices') {
behaviorContent.addChild('vertices');
// $FlowFixMe
behaviorContent.setChild('vertices', gd.Serializer.fromJSON(newValue));
return true;
}
if (propertyName === 'density') {
behaviorContent
.getChild('density')
.setDoubleValue(parseFloat(newValue));
return true;
}
if (propertyName === 'friction') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('friction').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent.getChild('friction').setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'restitution') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('restitution').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('restitution')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'linearDamping') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('linearDamping').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('linearDamping')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'angularDamping') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('angularDamping').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('angularDamping')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'gravityScale') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
behaviorContent.getChild('gravityScale').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
behaviorContent
.getChild('gravityScale')
.setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'layers') {
behaviorContent.getChild('layers').setIntValue(parseInt(newValue, 10));
return true;
}
if (propertyName === 'masks') {
behaviorContent.getChild('masks').setIntValue(parseInt(newValue, 10));
return true;
}
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
physics2Behavior.getProperties = function (behaviorContent) {
var behaviorProperties = new gd.MapStringPropertyDescriptor();
@@ -312,7 +337,6 @@ module.exports = {
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
physics2Behavior.initializeContent = function (behaviorContent) {
behaviorContent.addChild('bodyType').setStringValue('Dynamic');
behaviorContent.addChild('bullet').setBoolValue(false);
@@ -336,40 +360,41 @@ module.exports = {
};
var sharedData = new gd.BehaviorSharedDataJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.updateProperty = function (
sharedContent,
propertyName,
newValue
) {
if (propertyName === 'gravityX') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
sharedContent.getChild('gravityX').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
sharedContent.getChild('gravityX').setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'gravityY') {
newValue = parseFloat(newValue);
if (newValue !== newValue) return false;
sharedContent.getChild('gravityY').setDoubleValue(newValue);
const newValueAsNumber = parseFloat(newValue);
if (newValueAsNumber !== newValueAsNumber) return false;
sharedContent.getChild('gravityY').setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'scaleX') {
newValue = parseInt(newValue, 10);
if (newValue !== newValue) return false;
sharedContent.getChild('scaleX').setDoubleValue(newValue);
const newValueAsNumber = parseInt(newValue, 10);
if (newValueAsNumber !== newValueAsNumber) return false;
sharedContent.getChild('scaleX').setDoubleValue(newValueAsNumber);
return true;
}
if (propertyName === 'scaleY') {
newValue = parseInt(newValue, 10);
if (newValue !== newValue) return false;
sharedContent.getChild('scaleY').setDoubleValue(newValue);
const newValueAsNumber = parseInt(newValue, 10);
if (newValueAsNumber !== newValueAsNumber) return false;
sharedContent.getChild('scaleY').setDoubleValue(newValueAsNumber);
return true;
}
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.getProperties = function (sharedContent) {
var sharedProperties = new gd.MapStringPropertyDescriptor();
@@ -402,7 +427,6 @@ module.exports = {
return sharedProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
sharedData.initializeContent = function (behaviorContent) {
behaviorContent.addChild('gravityX').setDoubleValue(0);
behaviorContent.addChild('gravityY').setDoubleValue(9.8);
@@ -422,6 +446,7 @@ module.exports = {
'',
'res/physics32.png',
'Physics2Behavior',
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
physics2Behavior,
sharedData
)
@@ -561,7 +586,8 @@ module.exports = {
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Time scale (1 by default)'))
.getCodeExtraInformation()
.setIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.addIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.addIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
.setFunctionName('gdjs.physics2.setTimeScale');
aut
@@ -775,10 +801,10 @@ module.exports = {
.setDefaultValue('true')
.getCodeExtraInformation()
.setFunctionName('setSleepingAllowed');
// Deprecated action (fixed typo):
aut
.addDuplicatedAction("SetSleepingaAllowed", "SetSleepingAllowed")
.addDuplicatedAction('SetSleepingaAllowed', 'SetSleepingAllowed')
.setHidden();
aut
@@ -1390,6 +1416,26 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('getLinearVelocityAngle');
aut
.addAction(
'LinearVelocityAngle',
_('Linear velocity towards an angle'),
_('Set the linear velocity towards an angle.'),
_(
'Set the linear velocity of _PARAM0_ towards angle: _PARAM2_ degrees, speed: _PARAM3_ pixels per second'
),
_('Velocity'),
'res/physics32.png',
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Angle'))
.addParameter('expression', _('Speed (in pixels per second)'))
.getCodeExtraInformation()
.setFunctionName('setLinearVelocityAngle')
.setGetter('getLinearVelocityAngle');
aut
.addExpression(
'LinearVelocityAngle',
@@ -1476,10 +1522,16 @@ module.exports = {
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('X component (N)'))
.addParameter('expression', _('Y component (N)'))
.setParameterLongDescription(_('A force is like an acceleration but depends on the mass.'))
.setParameterLongDescription(
_('A force is like an acceleration but depends on the mass.')
)
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyForce');
@@ -1499,10 +1551,16 @@ module.exports = {
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Angle'))
.addParameter('expression', _('Length (N)'))
.setParameterLongDescription(_('A force is like an acceleration but depends on the mass.'))
.setParameterLongDescription(
_('A force is like an acceleration but depends on the mass.')
)
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyPolarForce');
@@ -1523,12 +1581,18 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Length (N)'))
.setParameterLongDescription(_('A force is like an acceleration but depends on the mass.'))
.setParameterLongDescription(
_('A force is like an acceleration but depends on the mass.')
)
.addParameter('expression', _('X position'))
.addParameter('expression', _('Y position'))
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyForceTowardPosition');
@@ -1546,18 +1610,18 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter(
'expression',
_('X component (N·s or kg·m·s⁻¹)')
.addParameter('expression', _('X component (N·s or kg·m·s⁻¹)'))
.addParameter('expression', _('Y component (N·s or kg·m·s⁻¹)'))
.setParameterLongDescription(
_('An impulse is like a speed addition but depends on the mass.')
)
.addParameter(
'expression',
_('Y component (N·s or kg·m·s⁻¹)')
)
.setParameterLongDescription(_('An impulse is like a speed addition but depends on the mass.'))
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyImpulse');
@@ -1578,14 +1642,17 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Angle'))
.addParameter(
'expression',
_('Length (N·s or kg·m·s⁻¹)')
.addParameter('expression', _('Length (N·s or kg·m·s⁻¹)'))
.setParameterLongDescription(
_('An impulse is like a speed addition but depends on the mass.')
)
.setParameterLongDescription(_('An impulse is like a speed addition but depends on the mass.'))
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyPolarImpulse');
@@ -1605,16 +1672,19 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter(
'expression',
_('Length (N·s or kg·m·s⁻¹)')
.addParameter('expression', _('Length (N·s or kg·m·s⁻¹)'))
.setParameterLongDescription(
_('An impulse is like a speed addition but depends on the mass.')
)
.setParameterLongDescription(_('An impulse is like a speed addition but depends on the mass.'))
.addParameter('expression', _('X position'))
.addParameter('expression', _('Y position'))
.addParameter('expression', _('Application point on X axis'))
.addParameter('expression', _('Application point on Y axis'))
.setParameterLongDescription(_('Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'))
.setParameterLongDescription(
_(
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyImpulseTowardPosition');
@@ -1633,7 +1703,9 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Torque (N·m)'))
.setParameterLongDescription(_('A torque is like a rotation acceleration but depends on the mass.'))
.setParameterLongDescription(
_('A torque is like a rotation acceleration but depends on the mass.')
)
.getCodeExtraInformation()
.setFunctionName('applyTorque');
@@ -1652,7 +1724,11 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Angular impulse (N·m·s'))
.setParameterLongDescription(_('An impulse is like a rotation speed addition but depends on the mass.'))
.setParameterLongDescription(
_(
'An impulse is like a rotation speed addition but depends on the mass.'
)
)
.getCodeExtraInformation()
.setFunctionName('applyAngularImpulse');
@@ -4019,7 +4095,8 @@ module.exports = {
.addParameter('objectList', _('Object'), '', false)
.addCodeOnlyParameter('conditionInverted', '')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.addIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.addIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
.setFunctionName('gdjs.physics2.objectsCollide');
extension
@@ -4037,7 +4114,8 @@ module.exports = {
.addParameter('objectList', _('Object'), '', false)
.addCodeOnlyParameter('conditionInverted', '')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.addIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.addIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
.setFunctionName('gdjs.physics2.haveObjectsStartedColliding');
extension
@@ -4055,16 +4133,14 @@ module.exports = {
.addParameter('objectList', _('Object'), '', false)
.addCodeOnlyParameter('conditionInverted', '')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.addIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.addIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
.setFunctionName('gdjs.physics2.haveObjectsStoppedColliding');
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
const dummyBehavior = extension
.getBehaviorMetadata('Physics2::Physics2Behavior')
.get();

View File

@@ -1429,6 +1429,23 @@ namespace gdjs {
);
}
setLinearVelocityAngle(angle: float, linearVelocity: float): void {
// If there is no body, set a new one
if (this._body === null) {
if (!this.createBody()) return;
}
const body = this._body!;
// Set the linear velocity toward an angle
angle = gdjs.toRad(angle);
body.SetLinearVelocity(
this.b2Vec2(
linearVelocity * Math.cos(angle) * this._sharedData.invScaleX,
linearVelocity * Math.sin(angle) * this._sharedData.invScaleY
)
);
}
getAngularVelocity(): float {
// If there is no body, set a new one
if (this._body === null) {

View File

@@ -872,6 +872,5 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("objectList", _("Object"), "", false)
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
.AddParameter("objectList", _("Platforms"), "", false)
.AddCodeOnlyParameter("conditionInverted", "")
.SetFunctionName("gdjs.evtTools.platform.isOnPlatform");
.AddCodeOnlyParameter("conditionInverted", "");
}

View File

@@ -41,6 +41,17 @@ class PlatformBehaviorJsExtension : public gd::PlatformExtension {
.AddIncludeFile(
"Extensions/PlatformBehavior/platformtools.js");
std::map<gd::String, gd::InstructionMetadata>& extConditions =
GetAllConditions();
extConditions["PlatformBehavior::IsObjectOnGivenFloor"].SetFunctionName(
"gdjs.evtTools.platform.isOnPlatform")
.AddIncludeFile(
"Extensions/PlatformBehavior/platformruntimebehavior.js")
.AddIncludeFile(
"Extensions/PlatformBehavior/platformerobjectruntimebehavior.js")
.AddIncludeFile(
"Extensions/PlatformBehavior/platformtools.js");
{
std::map<gd::String, gd::InstructionMetadata>& autActions =
GetAllActionsForBehavior(

View File

@@ -262,6 +262,9 @@ namespace gdjs {
const behavior1 = object1.getBehavior(
behaviorName
) as PlatformerObjectRuntimeBehavior;
if (!behavior1) {
return false;
}
return behavior1.isOnFloorObject(object2);
}
}

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -72,9 +64,7 @@ module.exports = {
.addAction(
'HideAuthenticationBanner',
_('Hide authentication banner'),
_(
'Hide the authentication banner from the top of the game screen.'
),
_('Hide the authentication banner from the top of the game screen.'),
_('Hide the authentication banner'),
'',
'JsPlatform/Extensions/authentication.svg',
@@ -226,10 +216,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -124,6 +124,25 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("The height of the ellipse"))
.SetFunctionName("DrawEllipse");
obj.AddAction("FilletRectangle",
_("Fillet Rectangle"),
_("Draw a fillet rectangle on screen"),
_("Draw from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ a fillet "
"rectangle (fillet: _PARAM5_)"
"with _PARAM0_"),
_("Drawing"),
"res/actions/filletRectangle24.png",
"res/actions/filletRectangle.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("Left X position"))
.AddParameter("expression", _("Top Y position"))
.AddParameter("expression", _("Right X position"))
.AddParameter("expression", _("Bottom Y position"))
.AddParameter("expression", _("Fillet (in pixels)"))
.SetFunctionName("DrawFilletRectangle");
obj.AddAction("RoundedRectangle",
_("Rounded rectangle"),
_("Draw a rounded rectangle on screen"),

View File

@@ -47,6 +47,11 @@ class PrimitiveDrawingJsExtension : public gd::PlatformExtension {
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::Ellipse"]
.SetFunctionName("drawEllipse");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::FilletRectangle"]
.SetFunctionName("drawFilletRectangle");
GetAllActionsForObject(
"PrimitiveDrawing::Drawer")["PrimitiveDrawing::RoundedRectangle"]
.SetFunctionName("drawRoundedRectangle");

View File

@@ -136,6 +136,25 @@ namespace gdjs {
this.invalidateBounds();
}
drawFilletRectangle(
x1: float,
y1: float,
x2: float,
y2: float,
fillet: float
) {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
this._object._fillOpacity / 255
);
//@ts-ignore from @pixi/graphics-extras
this._graphics.drawFilletRect(x1, y1, x2 - x1, y2 - y1, fillet);
this._graphics.closePath();
this._graphics.endFill();
this.invalidateBounds();
}
drawChamferRectangle(
x1: float,
y1: float,

View File

@@ -210,6 +210,22 @@ namespace gdjs {
this._renderer.drawEllipse(centerX, centerY, width, height);
}
drawFilletRectangle(
startX1: float,
startY1: float,
endX2: float,
endY2: float,
fillet: float
) {
this._renderer.drawFilletRectangle(
startX1,
startY1,
endX2,
endY2,
fillet
);
}
drawRoundedRectangle(
startX1: float,
startY1: float,

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -59,10 +51,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -83,10 +75,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
// @ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
@@ -94,10 +86,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -105,17 +94,13 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {},
registerEditorConfigurations: function (objectsEditorService) {},
/**
* Register renderers for instance of objects on the scene editor.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const { PIXI, RenderedInstance, gd } = objectsRenderingService;
class RenderedSpineInstance extends RenderedInstance {
@@ -163,6 +148,9 @@ module.exports = {
this._instance.getX(),
this._instance.getY()
);
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
this.setAnimation(this._instance.getRawDoubleProperty('animation'));
@@ -203,6 +191,14 @@ module.exports = {
return -this._spineOriginOffsetY;
}
getCenterX() {
return this.getOriginX();
}
getCenterY() {
return this.getOriginY();
}
/**
* @param {number} index - animation index
*/
@@ -228,7 +224,6 @@ module.exports = {
const animation = configuration.getAnimation(index);
const source = animation.getSource();
const shouldLoop = animation.shouldLoop();
const scale = this.getScale();
// reset scale to track new animation range
// if custom size is set it will be reinitialized in update method

View File

@@ -124,6 +124,16 @@ namespace gdjs {
return this.getY() + originOffset.y;
}
getCenterX(): float {
const originOffset = this._renderer.getOriginOffset();
return -originOffset.x;
}
getCenterY(): float {
const originOffset = this._renderer.getOriginOffset();
return -originOffset.y;
}
onDestroyed(): void {
super.onDestroyed();
this._renderer.onDestroy();

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -1243,10 +1235,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,18 +13,9 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -39,7 +31,6 @@ module.exports = {
.setIcon('JsPlatform/Extensions/text_input.svg');
const textInputObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
textInputObject.updateProperty = function (
objectContent,
propertyName,
@@ -94,7 +85,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
textInputObject.getProperties = function (objectContent) {
const objectProperties = new gd.MapStringPropertyDescriptor();
@@ -232,7 +222,6 @@ module.exports = {
})
);
// $FlowExpectedError - ignore Flow warning as we're creating an object
textInputObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -251,7 +240,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
textInputObject.getInitialInstanceProperties = function (
content,
instance,
@@ -288,7 +276,7 @@ module.exports = {
.addIncludeFile(
'Extensions/TextInput/textinputruntimeobject-pixi-renderer.js'
)
.addDefaultBehavior("TextContainerCapability::TextContainerBehavior")
.addDefaultBehavior('TextContainerCapability::TextContainerBehavior')
.addDefaultBehavior('ResizableCapability::ResizableBehavior')
.addDefaultBehavior('OpacityCapability::OpacityBehavior');
@@ -599,7 +587,9 @@ module.exports = {
.addScopedAction(
'Focus',
_('Focus'),
_('Focus the input so that text can be entered (like if it was touched/clicked).'),
_(
'Focus the input so that text can be entered (like if it was touched/clicked).'
),
_('Focus _PARAM0_'),
_(''),
'res/conditions/surObjet24.png',
@@ -621,10 +611,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -632,9 +619,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'TextInput::TextInputObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -647,9 +632,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;

View File

@@ -40,38 +40,6 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddDefaultBehavior("ScalableCapability::ScalableBehavior")
.AddDefaultBehavior("OpacityCapability::OpacityBehavior");
// Deprecated
obj.AddAction("String",
_("Modify the text"),
_("Modify the text of a Text object."),
_("the text"),
"",
"res/actions/text24_black.png",
"res/actions/text_black.png")
.SetHidden()
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"string",
gd::ParameterOptions::MakeNewOptions().SetDescription(_("Text")))
.SetFunctionName("SetString")
.SetGetter("GetString");
// Deprecated
obj.AddCondition("String",
_("Compare the text"),
_("Compare the text of a Text object."),
_("the text"),
"",
"res/conditions/text24_black.png",
"res/conditions/text_black.png")
.SetHidden()
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"string",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Text to compare to")))
.SetFunctionName("GetString");
obj.AddAction("Font",
_("Font"),
_("Change the font of the text."),
@@ -84,94 +52,6 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("police", _("Font"))
.SetFunctionName("ChangeFont");
// Deprecated
obj.AddCondition("ScaleX",
_("Scale on X axis"),
_("Compare the scale of the text on the X axis"),
_("the scale on the X axis"),
"Scale",
"res/conditions/scaleWidth24_black.png",
"res/conditions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale to compare to (1 by default)")))
.SetHidden()
.SetFunctionName("GetScaleX");
// Deprecated
obj.AddAction(
"ScaleX",
_("Scale on X axis"),
_("Modify the scale of the text on the X axis (default scale is 1)"),
_("the scale on the X axis"),
_("Scale"),
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))
.SetHidden()
.SetFunctionName("SetScaleX");
// Deprecated
obj.AddCondition("ScaleY",
_("Scale on Y axis"),
_("Compare the scale of the text on the Y axis"),
_("the scale on the Y axis"),
"Scale",
"res/conditions/scaleHeight24_black.png",
"res/conditions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale to compare to (1 by default)")))
.SetHidden()
.SetFunctionName("GetScaleY");
// Deprecated
obj.AddAction(
"ScaleY",
_("Scale on Y axis"),
_("Modify the scale of the text on the Y axis (default scale is 1)"),
_("the scale on the Y axis"),
_("Scale"),
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))
.SetHidden()
.SetFunctionName("SetScaleY");
// Deprecated
obj.AddAction(
"Scale",
_("Scale"),
_("Modify the scale of the specified object (default scale is 1)"),
_("the scale"),
_("Scale"),
"res/actions/scale24_black.png",
"res/actions/scale_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))
.SetHidden()
.SetFunctionName("SetScale");
obj.AddAction(
"ChangeColor",
_("Color"),
@@ -355,43 +235,6 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Blur radius")));
// Deprecated
obj.AddAction("Opacity",
_("Text opacity"),
_("Change the opacity of a Text. 0 is fully transparent, 255 "
"is opaque (default)."),
_("the opacity"),
"",
"res/actions/opacity24.png",
"res/actions/opacity.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Opacity (0-255)")))
.SetFunctionName("SetOpacity")
.SetGetter("GetOpacity")
.SetHidden();
// Deprecated
obj.AddCondition("Opacity",
_("Opacity"),
_("Compare the opacity of a Text object, between 0 (fully "
"transparent) to 255 (opaque)."),
_("the opacity"),
"",
"res/conditions/opacity24.png",
"res/conditions/opacity.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Opacity to compare to (0-255)")))
.SetFunctionName("GetOpacity")
.SetHidden();
obj.AddAction("SetSmooth",
_("Smoothing"),
_("Activate or deactivate text smoothing."),
@@ -484,37 +327,6 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("IsUnderlined");
obj.AddAction("Angle",
_("Angle"),
_("Modify the angle of a Text object."),
_("the angle"),
_("Rotation"),
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Angle (in degrees)")))
.SetFunctionName("SetAngle")
.SetGetter("GetAngle");
obj.AddCondition("Angle",
_("Angle"),
_("Compare the value of the angle of a Text object."),
_("the angle"),
_("Rotation"),
"res/conditions/rotate24_black.png",
"res/conditions/rotate_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Angle to compare to (in degrees)")))
.SetFunctionName("GetAngle");
obj.AddCondition("Padding",
_("Padding"),
_("Compare the number of pixels around a text object. If "
@@ -628,6 +440,143 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"res/actions/textPadding_black.png")
.AddParameter("object", _("Object"), "Text");
obj.AddExpressionAndConditionAndAction("number",
"FontSize",
_("Font size"),
_("the font size of a text object"),
_("the font size"),
"",
"res/conditions/characterSize24.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions());
// Support for deprecated "Size" actions/conditions:
obj.AddDuplicatedAction("Size", "Text::SetFontSize").SetHidden();
obj.AddDuplicatedCondition("Size", "Text::FontSize").SetHidden();
// Deprecated
obj.AddAction("Angle",
_("Angle"),
_("Modify the angle of a Text object."),
_("the angle"),
_("Rotation"),
"res/actions/rotate24_black.png",
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Angle (in degrees)")))
.SetHidden()
.SetFunctionName("SetAngle")
.SetGetter("GetAngle");
// Deprecated
obj.AddCondition("Angle",
_("Angle"),
_("Compare the value of the angle of a Text object."),
_("the angle"),
_("Rotation"),
"res/conditions/rotate24_black.png",
"res/conditions/rotate_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Angle to compare to (in degrees)")))
.SetHidden()
.SetFunctionName("GetAngle");
// Deprecated
obj.AddCondition("ScaleX",
_("Scale on X axis"),
_("Compare the scale of the text on the X axis"),
_("the scale on the X axis"),
"Scale",
"res/conditions/scaleWidth24_black.png",
"res/conditions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale to compare to (1 by default)")))
.SetHidden()
.SetFunctionName("GetScaleX");
// Deprecated
obj.AddAction(
"ScaleX",
_("Scale on X axis"),
_("Modify the scale of the text on the X axis (default scale is 1)"),
_("the scale on the X axis"),
_("Scale"),
"res/actions/scaleWidth24_black.png",
"res/actions/scaleWidth_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))
.SetHidden()
.SetFunctionName("SetScaleX");
// Deprecated
obj.AddCondition("ScaleY",
_("Scale on Y axis"),
_("Compare the scale of the text on the Y axis"),
_("the scale on the Y axis"),
"Scale",
"res/conditions/scaleHeight24_black.png",
"res/conditions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale to compare to (1 by default)")))
.SetHidden()
.SetFunctionName("GetScaleY");
// Deprecated
obj.AddAction(
"ScaleY",
_("Scale on Y axis"),
_("Modify the scale of the text on the Y axis (default scale is 1)"),
_("the scale on the Y axis"),
_("Scale"),
"res/actions/scaleHeight24_black.png",
"res/actions/scaleHeight_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))
.SetHidden()
.SetFunctionName("SetScaleY");
// Deprecated
obj.AddAction(
"Scale",
_("Scale"),
_("Modify the scale of the specified object (default scale is 1)"),
_("the scale"),
_("Scale"),
"res/actions/scale24_black.png",
"res/actions/scale_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Scale (1 by default)")))
.SetHidden()
.SetFunctionName("SetScale");
// Deprecated
obj.AddExpression("ScaleX",
_("X Scale of a Text object"),
@@ -648,6 +597,43 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.SetHidden()
.SetFunctionName("GetScaleY");
// Deprecated
obj.AddAction("Opacity",
_("Text opacity"),
_("Change the opacity of a Text. 0 is fully transparent, 255 "
"is opaque (default)."),
_("the opacity"),
"",
"res/actions/opacity24.png",
"res/actions/opacity.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Opacity (0-255)")))
.SetFunctionName("SetOpacity")
.SetGetter("GetOpacity")
.SetHidden();
// Deprecated
obj.AddCondition("Opacity",
_("Opacity"),
_("Compare the opacity of a Text object, between 0 (fully "
"transparent) to 255 (opaque)."),
_("the opacity"),
"",
"res/conditions/opacity24.png",
"res/conditions/opacity.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Opacity to compare to (0-255)")))
.SetFunctionName("GetOpacity")
.SetHidden();
// Deprecated
obj.AddExpression("Opacity",
_("Opacity of a Text object"),
@@ -658,30 +644,52 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.SetFunctionName("GetOpacity")
.SetHidden();
// Deprecated
obj.AddExpression("Angle",
_("Angle"),
_("Angle"),
_("Rotation"),
"res/actions/rotate_black.png")
.AddParameter("object", _("Object"), "Text")
.SetHidden()
.SetFunctionName("GetAngle");
obj.AddExpressionAndConditionAndAction("number",
"FontSize",
_("Font size"),
_("the font size of a text object"),
_("the font size"),
"",
"res/conditions/characterSize24.png")
// Deprecated
obj.AddAction("String",
_("Modify the text"),
_("Modify the text of a Text object."),
_("the text"),
"",
"res/actions/text24_black.png",
"res/actions/text_black.png")
.SetHidden()
.AddParameter("object", _("Object"), "Text")
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions());
.UseStandardOperatorParameters(
"string",
gd::ParameterOptions::MakeNewOptions().SetDescription(_("Text")))
.SetFunctionName("SetString")
.SetGetter("GetString");
// Support for deprecated "Size" actions/conditions:
obj.AddDuplicatedAction("Size", "Text::SetFontSize").SetHidden();
obj.AddDuplicatedCondition("Size", "Text::FontSize").SetHidden();
// Deprecated
obj.AddCondition("String",
_("Compare the text"),
_("Compare the text of a Text object."),
_("the text"),
"",
"res/conditions/text24_black.png",
"res/conditions/text_black.png")
.SetHidden()
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters(
"string",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Text to compare to")))
.SetFunctionName("GetString");
// Deprecated
obj.AddStrExpression(
"String", _("Text"), _("Text"), _("Text"), "res/texteicon.png")
.AddParameter("object", _("Object"), "Text")
.SetHidden()
.SetFunctionName("GetString");
}

View File

@@ -29,24 +29,6 @@ class TextObjectJsExtension : public gd::PlatformExtension {
.SetIncludeFile("Extensions/TextObject/textruntimeobject.js")
.AddIncludeFile(
"Extensions/TextObject/textruntimeobject-pixi-renderer.js");
GetAllActionsForObject("TextObject::Text")["TextObject::Scale"]
.SetFunctionName("setScale")
.SetGetter("getScaleMean");
GetAllActionsForObject("TextObject::Text")["TextObject::ScaleX"]
.SetFunctionName("setScaleX")
.SetGetter("getScaleX");
GetAllConditionsForObject("TextObject::Text")["TextObject::ScaleX"]
.SetFunctionName("getScaleX");
GetAllActionsForObject("TextObject::Text")["TextObject::ScaleY"]
.SetFunctionName("setScaleY")
.SetGetter("getScaleY");
GetAllConditionsForObject("TextObject::Text")["TextObject::ScaleY"]
.SetFunctionName("getScaleY");
GetAllActionsForObject("TextObject::Text")["TextObject::String"]
.SetFunctionName("setString")
.SetGetter("getString");
GetAllConditionsForObject("TextObject::Text")["TextObject::String"]
.SetFunctionName("getString");
GetAllActionsForObject("TextObject::Text")["TextObject::Text::SetFontSize"]
.SetFunctionName("setCharacterSize")
@@ -56,24 +38,6 @@ class TextObjectJsExtension : public gd::PlatformExtension {
GetAllExpressionsForObject("TextObject::Text")["FontSize"]
.SetFunctionName("getCharacterSize");
// Deprecated actions/conditions (use "FontSize"/"SetFontSize" instead):
GetAllActionsForObject("TextObject::Text")["TextObject::Size"]
.SetFunctionName("setCharacterSize")
.SetGetter("getCharacterSize");
GetAllConditionsForObject("TextObject::Text")["TextObject::Size"]
.SetFunctionName("getCharacterSize");
GetAllActionsForObject("TextObject::Text")["TextObject::Angle"]
.SetFunctionName("setAngle")
.SetGetter("getAngle");
GetAllConditionsForObject("TextObject::Text")["TextObject::Angle"]
.SetFunctionName("getAngle");
GetAllActionsForObject("TextObject::Text")["TextObject::Opacity"]
.SetFunctionName("setOpacity")
.SetGetter("getOpacity");
GetAllConditionsForObject("TextObject::Text")["TextObject::Opacity"]
.SetFunctionName("getOpacity");
GetAllActionsForObject("TextObject::Text")["TextObject::SetBold"]
.SetFunctionName("setBold");
GetAllConditionsForObject("TextObject::Text")["TextObject::IsBold"]
@@ -108,16 +72,6 @@ class TextObjectJsExtension : public gd::PlatformExtension {
GetAllExpressionsForObject("TextObject::Text")["Padding"]
.SetFunctionName("getPadding");
GetAllExpressionsForObject("TextObject::Text")["ScaleX"]
.SetFunctionName("getScaleX");
GetAllExpressionsForObject("TextObject::Text")["ScaleY"]
.SetFunctionName("getScaleY");
GetAllExpressionsForObject("TextObject::Text")["Opacity"]
.SetFunctionName("getOpacity");
GetAllExpressionsForObject("TextObject::Text")["Angle"]
.SetFunctionName("getAngle");
GetAllStrExpressionsForObject("TextObject::Text")["String"]
.SetFunctionName("getString");
GetAllActionsForObject("TextObject::Text")["TextObject::ChangeColor"]
.SetFunctionName("setColor");
@@ -125,15 +79,13 @@ class TextObjectJsExtension : public gd::PlatformExtension {
GetAllActionsForObject("TextObject::Text")["TextObject::SetGradient"]
.SetFunctionName("setGradient");
GetAllActionsForObject("TextObject::Text")["TextObject::SetOutline"]
.SetFunctionName("setOutline");
GetAllActionsForObject("TextObject::Text")["TextObject::Text::SetOutlineEnabled"]
.SetFunctionName("setOutlineEnabled");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::IsOutlineEnabled"]
.SetFunctionName("isOutlineEnabled");
GetAllActionsForObject("TextObject::Text")["TextObject::Text::SetOutlineColor"]
.SetFunctionName("setOutlineColor");
GetAllExpressionsForObject("TextObject::Text")["TextObject::Text::OutlineThickness"]
GetAllExpressionsForObject("TextObject::Text")["OutlineThickness"]
.SetFunctionName("getOutlineThickness");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::OutlineThickness"]
.SetFunctionName("getOutlineThickness");
@@ -141,8 +93,6 @@ class TextObjectJsExtension : public gd::PlatformExtension {
.SetFunctionName("setOutlineThickness")
.SetGetter("getOutlineThickness");
GetAllActionsForObject("TextObject::Text")["TextObject::SetShadow"]
.SetFunctionName("setShadow");
GetAllActionsForObject("TextObject::Text")["TextObject::ShowShadow"]
.SetFunctionName("showShadow");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::IsShadowEnabled"]
@@ -150,7 +100,7 @@ class TextObjectJsExtension : public gd::PlatformExtension {
GetAllActionsForObject("TextObject::Text")["TextObject::Text::SetShadowColor"]
.SetFunctionName("setShadowColor");
GetAllExpressionsForObject("TextObject::Text")["TextObject::Text::ShadowOpacity"]
GetAllExpressionsForObject("TextObject::Text")["ShadowOpacity"]
.SetFunctionName("getShadowOpacity");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::ShadowOpacity"]
.SetFunctionName("getShadowOpacity");
@@ -158,7 +108,7 @@ class TextObjectJsExtension : public gd::PlatformExtension {
.SetFunctionName("setShadowOpacity")
.SetGetter("getShadowOpacity");
GetAllExpressionsForObject("TextObject::Text")["TextObject::Text::ShadowDistance"]
GetAllExpressionsForObject("TextObject::Text")["ShadowDistance"]
.SetFunctionName("getShadowDistance");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::ShadowDistance"]
.SetFunctionName("getShadowDistance");
@@ -166,7 +116,7 @@ class TextObjectJsExtension : public gd::PlatformExtension {
.SetFunctionName("setShadowDistance")
.SetGetter("getShadowDistance");
GetAllExpressionsForObject("TextObject::Text")["TextObject::Text::ShadowAngle"]
GetAllExpressionsForObject("TextObject::Text")["ShadowAngle"]
.SetFunctionName("getShadowAngle");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::ShadowAngle"]
.SetFunctionName("getShadowAngle");
@@ -174,7 +124,7 @@ class TextObjectJsExtension : public gd::PlatformExtension {
.SetFunctionName("setShadowAngle")
.SetGetter("getShadowAngle");
GetAllExpressionsForObject("TextObject::Text")["TextObject::Text::ShadowBlurRadius"]
GetAllExpressionsForObject("TextObject::Text")["ShadowBlurRadius"]
.SetFunctionName("getShadowBlurRadius");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::ShadowBlurRadius"]
.SetFunctionName("getShadowBlurRadius");
@@ -182,6 +132,61 @@ class TextObjectJsExtension : public gd::PlatformExtension {
.SetFunctionName("setShadowBlurRadius")
.SetGetter("getShadowBlurRadius");
// Deprecated actions/conditions (use "FontSize"/"SetFontSize" instead):
GetAllActionsForObject("TextObject::Text")["TextObject::Size"]
.SetFunctionName("setCharacterSize")
.SetGetter("getCharacterSize");
GetAllConditionsForObject("TextObject::Text")["TextObject::Size"]
.SetFunctionName("getCharacterSize");
// Deprecated: now available for all objects.
GetAllActionsForObject("TextObject::Text")["TextObject::Angle"]
.SetFunctionName("setAngle")
.SetGetter("getAngle");
GetAllConditionsForObject("TextObject::Text")["TextObject::Angle"]
.SetFunctionName("getAngle");
GetAllExpressionsForObject("TextObject::Text")["Angle"]
.SetFunctionName("getAngle");
// Deprecated: available through capabilities.
GetAllActionsForObject("TextObject::Text")["TextObject::Scale"]
.SetFunctionName("setScale")
.SetGetter("getScaleMean");
GetAllActionsForObject("TextObject::Text")["TextObject::ScaleX"]
.SetFunctionName("setScaleX")
.SetGetter("getScaleX");
GetAllConditionsForObject("TextObject::Text")["TextObject::ScaleX"]
.SetFunctionName("getScaleX");
GetAllActionsForObject("TextObject::Text")["TextObject::ScaleY"]
.SetFunctionName("setScaleY")
.SetGetter("getScaleY");
GetAllConditionsForObject("TextObject::Text")["TextObject::ScaleY"]
.SetFunctionName("getScaleY");
GetAllExpressionsForObject("TextObject::Text")["ScaleX"]
.SetFunctionName("getScaleX");
GetAllExpressionsForObject("TextObject::Text")["ScaleY"]
.SetFunctionName("getScaleY");
GetAllActionsForObject("TextObject::Text")["TextObject::String"]
.SetFunctionName("setString")
.SetGetter("getString");
GetAllStrExpressionsForObject("TextObject::Text")["String"]
.SetFunctionName("getString");
GetAllConditionsForObject("TextObject::Text")["TextObject::String"]
.SetFunctionName("getString");
GetAllExpressionsForObject("TextObject::Text")["Opacity"]
.SetFunctionName("getOpacity");
GetAllActionsForObject("TextObject::Text")["TextObject::Opacity"]
.SetFunctionName("setOpacity")
.SetGetter("getOpacity");
GetAllConditionsForObject("TextObject::Text")["TextObject::Opacity"]
.SetFunctionName("getOpacity");
// Deprecated: split into several instructions.
GetAllActionsForObject("TextObject::Text")["TextObject::SetOutline"]
.SetFunctionName("setOutline");
GetAllActionsForObject("TextObject::Text")["TextObject::SetShadow"]
.SetFunctionName("setShadow");
// Unimplemented actions and conditions:
GetAllActionsForObject("TextObject::Text")["TextObject::Font"]
.SetFunctionName("");

View File

@@ -56,7 +56,8 @@ namespace gdjs {
opacity: float = 255;
_textAlign: string = 'left';
_wrapping: boolean = false;
_wrappingWidth: float = 1;
// A wrapping of 1 makes games crash on Firefox
_wrappingWidth: float = 100;
_isOutlineEnabled: boolean;
_outlineThickness: float;
@@ -194,8 +195,8 @@ namespace gdjs {
*/
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
if (initialInstanceData.customSize) {
this.setWrapping(true);
this.setWrappingWidth(initialInstanceData.width);
this.setWrapping(true);
} else {
this.setWrapping(false);
}
@@ -513,11 +514,15 @@ namespace gdjs {
if (width <= 1) {
width = 1;
}
if (this._wrappingWidth === width) return;
if (this._wrappingWidth === width) {
return;
}
this._wrappingWidth = width;
this._renderer.updateStyle();
this.invalidateHitboxes();
if (this._wrapping) {
this._renderer.updateStyle();
this.invalidateHitboxes();
}
}
/**

View File

@@ -1,5 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/// <reference path="helper/TileMapHelper.d.ts" />
/**
@@ -15,20 +15,13 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
const defineTileMap = function (
extension,
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
/**
* @param {gd.PlatformExtension} extension
* @param {(translationSource: string) => string} _
* @param {GDNamespace} gd
*/
const defineTileMap = function (extension, _, gd) {
var objectTileMap = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
objectTileMap.updateProperty = function (
objectContent,
propertyName,
@@ -69,7 +62,6 @@ const defineTileMap = function (
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
objectTileMap.getProperties = function (objectContent) {
var objectProperties = new gd.MapStringPropertyDescriptor();
@@ -168,7 +160,6 @@ const defineTileMap = function (
})
);
// $FlowExpectedError - ignore Flow warning as we're creating an object
objectTileMap.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -179,7 +170,6 @@ const defineTileMap = function (
) {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
objectTileMap.getInitialInstanceProperties = function (
content,
instance,
@@ -608,13 +598,13 @@ const defineTileMap = function (
.setFunctionName('setHeight');
};
const defineCollisionMask = function (
extension,
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
/**
* @param {gd.PlatformExtension} extension
* @param {(translationSource: string) => string} _
* @param {GDNamespace} gd
*/
const defineCollisionMask = function (extension, _, gd) {
var collisionMaskObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
collisionMaskObject.updateProperty = function (
objectContent,
propertyName,
@@ -659,7 +649,6 @@ const defineCollisionMask = function (
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
collisionMaskObject.getProperties = function (objectContent) {
var objectProperties = new gd.MapStringPropertyDescriptor();
@@ -768,7 +757,6 @@ const defineCollisionMask = function (
})
);
// $FlowExpectedError - ignore Flow warning as we're creating an object
collisionMaskObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -779,7 +767,6 @@ const defineCollisionMask = function (
) {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
collisionMaskObject.getInitialInstanceProperties = function (
content,
instance,
@@ -1033,6 +1020,7 @@ const defineCollisionMask = function (
.setFunctionName('setHeight');
};
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
@@ -1093,10 +1081,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -1104,9 +1089,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'TileMap::TileMap',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -1125,9 +1108,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
@@ -1238,33 +1219,33 @@ module.exports = {
updateTileMap() {
// Get the tileset resource to use
const tilemapAtlasImage = this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('tilemapAtlasImage')
.getValue();
const tilemapJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('tilemapJsonFile')
.getValue();
const tilesetJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('tilesetJsonFile')
.getValue();
const layerIndex = parseInt(
this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('layerIndex')
.getValue(),
10
);
const levelIndex = parseInt(
this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('levelIndex')
.getValue(),
10
);
const displayMode = this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('displayMode')
.getValue();
@@ -1306,7 +1287,7 @@ module.exports = {
}
/** @type {TileMapHelper.TileTextureCache} */
const textureCache = manager.getOrLoadTextureCache(
manager.getOrLoadTextureCache(
this._loadTileMapWithCallback.bind(this),
(textureName) =>
this._pixiResourcesLoader.getPIXITexture(
@@ -1357,14 +1338,14 @@ module.exports = {
async _loadTileMap(tilemapJsonFile, tilesetJsonFile) {
try {
const tileMapJsonData =
await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilemapJsonFile
);
const tileMapJsonData = await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilemapJsonFile
);
const tileMap =
TilemapHelper.TileMapManager.identify(tileMapJsonData);
const tileMap = TilemapHelper.TileMapManager.identify(
tileMapJsonData
);
if (tileMap.kind === 'tiled') {
const tilesetJsonData = tilesetJsonFile
@@ -1521,43 +1502,45 @@ module.exports = {
* This is used to reload the Tilemap
*/
updateTileMap() {
// Get the tileset resource to use
// This might become useful in the future
/*
const tilemapAtlasImage = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapAtlasImage')
.getValue();
*/
const tilemapJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('tilemapJsonFile')
.getValue();
const tilesetJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('tilesetJsonFile')
.getValue();
const collisionMaskTag = this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('collisionMaskTag')
.getValue();
const outlineColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('outlineColor')
.getValue()
);
const fillColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.getProperties(this.project)
.getProperties()
.get('fillColor')
.getValue()
);
const outlineOpacity =
this._associatedObjectConfiguration
.getProperties(this.project)
+this._associatedObjectConfiguration
.getProperties()
.get('outlineOpacity')
.getValue() / 255;
const fillOpacity =
this._associatedObjectConfiguration
.getProperties(this.project)
+this._associatedObjectConfiguration
.getProperties()
.get('fillOpacity')
.getValue() / 255;
const outlineSize = 1;
@@ -1601,14 +1584,14 @@ module.exports = {
async _loadTileMap(tilemapJsonFile, tilesetJsonFile) {
try {
const tileMapJsonData =
await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilemapJsonFile
);
const tileMapJsonData = await this._pixiResourcesLoader.getResourceJsonData(
this._project,
tilemapJsonFile
);
const tileMap =
TilemapHelper.TileMapManager.identify(tileMapJsonData);
const tileMap = TilemapHelper.TileMapManager.identify(
tileMapJsonData
);
if (tileMap.kind === 'tiled') {
const tilesetJsonData = tilesetJsonFile

View File

@@ -1,5 +1,5 @@
declare namespace PIXI {
namespace tilemap {
export namespace tilemap {
/**
* The renderer plugin for canvas. It isn't registered by default.
*

View File

@@ -1,4 +1,5 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,13 +13,6 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
const easingChoices = JSON.stringify([
'linear',
'easeInQuad',
@@ -57,11 +51,9 @@ const easingChoices = JSON.stringify([
'easeTo',
]);
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
@@ -387,57 +379,61 @@ module.exports = {
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.tweenCameraRotation2');
extension
.addAction(
'TweenNumberEffectPropertyTween',
_('Tween number effect property'),
_('Tweens a number effect property from its current value to a new one.'),
_(
'Tween the property _PARAM5_ for effect _PARAM4_ of _PARAM3_ to _PARAM2_ with easing _PARAM6_ over _PARAM7_ seconds as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('expression', _('To value'), '', false)
.addParameter('layer', _('Layer'), '', true)
.addParameter("layerEffectName", _("Effect name"))
.addParameter("layerEffectParameterName", _("Property name"))
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/TweenManager.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.tweenNumberEffectPropertyTween');
extension
.addAction(
'TweenColorEffectPropertyTween',
_('Tween color effect property'),
_('Tweens a color effect property from its current value to a new one.'),
_(
'Tween the color property _PARAM5_ for effect _PARAM4_ of _PARAM3_ to _PARAM2_ with easing _PARAM6_ over _PARAM7_ seconds as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('color', _('To color'), '', false)
.addParameter('layer', _('Layer'), '', true)
.addParameter("layerEffectName", _("Effect name"))
.addParameter("layerEffectParameterName", _("Property name"))
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/TweenManager.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.tweenColorEffectPropertyTween');
extension
.addAction(
'TweenNumberEffectPropertyTween',
_('Tween number effect property'),
_(
'Tweens a number effect property from its current value to a new one.'
),
_(
'Tween the property _PARAM5_ for effect _PARAM4_ of _PARAM3_ to _PARAM2_ with easing _PARAM6_ over _PARAM7_ seconds as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('expression', _('To value'), '', false)
.addParameter('layer', _('Layer'), '', true)
.addParameter('layerEffectName', _('Effect name'))
.addParameter('layerEffectParameterName', _('Property name'))
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/TweenManager.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.tweenNumberEffectPropertyTween');
extension
.addAction(
'TweenColorEffectPropertyTween',
_('Tween color effect property'),
_(
'Tweens a color effect property from its current value to a new one.'
),
_(
'Tween the color property _PARAM5_ for effect _PARAM4_ of _PARAM3_ to _PARAM2_ with easing _PARAM6_ over _PARAM7_ seconds as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('color', _('To color'), '', false)
.addParameter('layer', _('Layer'), '', true)
.addParameter('layerEffectName', _('Effect name'))
.addParameter('layerEffectParameterName', _('Property name'))
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/TweenManager.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.tweenColorEffectPropertyTween');
extension
.addCondition(
@@ -597,7 +593,6 @@ module.exports = {
const tweenBehavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
tweenBehavior.updateProperty = function (
behaviorContent,
propertyName,
@@ -606,13 +601,11 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
tweenBehavior.getProperties = function (behaviorContent) {
var behaviorProperties = new gd.MapStringPropertyDescriptor();
return behaviorProperties;
};
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
tweenBehavior.initializeContent = function (behaviorContent) {};
const behavior = extension
@@ -626,6 +619,7 @@ module.exports = {
'',
'JsPlatform/Extensions/tween_behavior32.png',
'TweenBehavior',
// @ts-ignore - TODO: Fix tweenBehavior being an BehaviorJsImplementation instead of an Behavior
tweenBehavior,
new gd.BehaviorsSharedData()
)
@@ -926,37 +920,37 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('addObjectPositionZTween');
behavior
.addAction(
'AddObjectPositionZTween2',
_('Tween object Z position'),
_(
'Tweens an object Z position (3D objects only) from its current Z position to a new one.'
),
_(
'Tween the Z position of _PARAM0_ to _PARAM4_ with easing _PARAM5_ over _PARAM6_ seconds as _PARAM3_'
),
_('Position'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter("behavior", _("3D capability"), "Scene3D::Base3DBehavior")
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To Z'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.addParameter(
'yesorno',
_('Destroy this object when tween finishes'),
'',
false
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addObjectPositionZTween2');
behavior
.addAction(
'AddObjectPositionZTween2',
_('Tween object Z position'),
_(
'Tweens an object Z position (3D objects only) from its current Z position to a new one.'
),
_(
'Tween the Z position of _PARAM0_ to _PARAM4_ with easing _PARAM5_ over _PARAM6_ seconds as _PARAM3_'
),
_('Position'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('behavior', _('3D capability'), 'Scene3D::Base3DBehavior')
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To Z'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.addParameter(
'yesorno',
_('Destroy this object when tween finishes'),
'',
false
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addObjectPositionZTween2');
// deprecated
behavior
@@ -1111,37 +1105,37 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('addObjectDepthTween');
behavior
.addAction(
'AddObjectDepthTween2',
_('Tween object depth'),
_(
'Tweens an object depth (suitable 3D objects only) from its current depth to a new one.'
),
_(
'Tween the depth of _PARAM0_ to _PARAM4_ with easing _PARAM5_ over _PARAM6_ seconds as _PARAM3_'
),
_('Size'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter("behavior", _("3D capability"), "Scene3D::Base3DBehavior")
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To depth'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.addParameter(
'yesorno',
_('Destroy this object when tween finishes'),
'',
false
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addObjectDepthTween2');
behavior
.addAction(
'AddObjectDepthTween2',
_('Tween object depth'),
_(
'Tweens an object depth (suitable 3D objects only) from its current depth to a new one.'
),
_(
'Tween the depth of _PARAM0_ to _PARAM4_ with easing _PARAM5_ over _PARAM6_ seconds as _PARAM3_'
),
_('Size'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('behavior', _('3D capability'), 'Scene3D::Base3DBehavior')
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To depth'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.addParameter(
'yesorno',
_('Destroy this object when tween finishes'),
'',
false
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addObjectDepthTween2');
// deprecated
behavior
@@ -1267,65 +1261,69 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('addObjectAngleTween2');
behavior
.addScopedAction(
'AddObjectRotationXTween',
_('Tween object rotation on X axis'),
_('Tweens an object rotation on X axis from its current angle to a new one.'),
_(
'Tween the rotation on X axis of _PARAM0_ to _PARAM4_° with easing _PARAM5_ over _PARAM6_ seconds as _PARAM3_'
),
_('Angle'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter("behavior", _("3D capability"), "Scene3D::Base3DBehavior")
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To angle (in degrees)'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.addParameter(
'yesorno',
_('Destroy this object when tween finishes'),
'',
false
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addObjectRotationXTween');
behavior
.addScopedAction(
'AddObjectRotationXTween',
_('Tween object rotation on X axis'),
_(
'Tweens an object rotation on X axis from its current angle to a new one.'
),
_(
'Tween the rotation on X axis of _PARAM0_ to _PARAM4_° with easing _PARAM5_ over _PARAM6_ seconds as _PARAM3_'
),
_('Angle'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('behavior', _('3D capability'), 'Scene3D::Base3DBehavior')
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To angle (in degrees)'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.addParameter(
'yesorno',
_('Destroy this object when tween finishes'),
'',
false
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addObjectRotationXTween');
behavior
.addScopedAction(
'AddObjectRotationYTween',
_('Tween object rotation on Y axis'),
_('Tweens an object rotation on Y axis from its current angle to a new one.'),
_(
'Tween the rotation on Y axis of _PARAM0_ to _PARAM4_° with easing _PARAM5_ over _PARAM6_ seconds as _PARAM3_'
),
_('Angle'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter("behavior", _("3D capability"), "Scene3D::Base3DBehavior")
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To angle (in degrees)'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.addParameter(
'yesorno',
_('Destroy this object when tween finishes'),
'',
false
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addObjectRotationYTween');
behavior
.addScopedAction(
'AddObjectRotationYTween',
_('Tween object rotation on Y axis'),
_(
'Tweens an object rotation on Y axis from its current angle to a new one.'
),
_(
'Tween the rotation on Y axis of _PARAM0_ to _PARAM4_° with easing _PARAM5_ over _PARAM6_ seconds as _PARAM3_'
),
_('Angle'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('behavior', _('3D capability'), 'Scene3D::Base3DBehavior')
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To angle (in degrees)'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
.addParameter(
'yesorno',
_('Destroy this object when tween finishes'),
'',
false
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addObjectRotationYTween');
// deprecated
behavior
@@ -1700,7 +1698,9 @@ module.exports = {
.addScopedAction(
'AddNumberEffectPropertyTween',
_('Tween number effect property'),
_('Tweens a number effect property from its current value to a new one.'),
_(
'Tweens a number effect property from its current value to a new one.'
),
_(
'Tween the property _PARAM6_ for effect _PARAM5_ of _PARAM0_ to _PARAM4_ with easing _PARAM7_ over _PARAM8_ seconds as _PARAM3_'
),
@@ -1710,11 +1710,15 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter("behavior", _("Effect capability"), "EffectCapability::EffectBehavior")
.addParameter(
'behavior',
_('Effect capability'),
'EffectCapability::EffectBehavior'
)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To value'), '', false)
.addParameter("objectEffectName", _("Effect name"))
.addParameter("objectEffectParameterName", _("Property name"))
.addParameter('objectEffectName', _('Effect name'))
.addParameter('objectEffectParameterName', _('Property name'))
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
@@ -1732,7 +1736,9 @@ module.exports = {
.addScopedAction(
'AddColorEffectPropertyTween',
_('Tween color effect property'),
_('Tweens a color effect property from its current value to a new one.'),
_(
'Tweens a color effect property from its current value to a new one.'
),
_(
'Tween the color property _PARAM6_ for effect _PARAM5_ of _PARAM0_ to _PARAM4_ with easing _PARAM7_ over _PARAM8_ seconds as _PARAM3_'
),
@@ -1742,11 +1748,15 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter("behavior", _("Effect capability"), "EffectCapability::EffectBehavior")
.addParameter(
'behavior',
_('Effect capability'),
'EffectCapability::EffectBehavior'
)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('color', _('To color'), '', false)
.addParameter("objectEffectName", _("Effect name"))
.addParameter("objectEffectParameterName", _("Property name"))
.addParameter('objectEffectName', _('Effect name'))
.addParameter('objectEffectParameterName', _('Property name'))
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration (in seconds)'), '', false)
@@ -1758,7 +1768,7 @@ module.exports = {
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addNumberEffectPropertyTween');
.setFunctionName('addColorEffectPropertyTween');
// deprecated
behavior
@@ -2088,10 +2098,7 @@ module.exports = {
return extension;
},
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
};

View File

@@ -1,4 +1,6 @@
// @flow
//@ts-check
/// <reference path="../JsExtensionTypes.d.ts" />
/**
* This is a declaration of an extension for GDevelop 5.
*
@@ -12,34 +14,27 @@
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
/*::
// Import types to allow Flow to do static type checking on this file.
// Extensions declaration are typed using Flow (like the editor), but the files
// for the game engine are checked with TypeScript annotations.
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
/** @type {ExtensionModule} */
module.exports = {
createExtension: function (
_ /*: (string) => string */,
gd /*: libGDevelop */
) {
createExtension: function (_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
'Video',
_('Video'),
_('Provides an object to display a video on the scene. The recommended file format is MPEG4, with H264 video codec and AAC audio codec, to maximize the support of the video on different platform and browsers.'),
_(
'Provides an object to display a video on the scene. The recommended file format is MPEG4, with H264 video codec and AAC audio codec, to maximize the support of the video on different platform and browsers.'
),
'Aurélien Vivet',
'Open source (MIT License)'
)
.setCategory('User interface')
.setExtensionHelpPath('/objects/video');
extension.addInstructionOrExpressionGroupMetadata(_("Video"))
.setIcon("JsPlatform/Extensions/videoicon16.png");
extension
.addInstructionOrExpressionGroupMetadata(_('Video'))
.setIcon('JsPlatform/Extensions/videoicon16.png');
var videoObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
videoObject.updateProperty = function (
objectContent,
propertyName,
@@ -64,7 +59,6 @@ module.exports = {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
videoObject.getProperties = function (objectContent) {
var objectProperties = new gd.MapStringPropertyDescriptor();
@@ -104,7 +98,6 @@ module.exports = {
})
);
// $FlowExpectedError - ignore Flow warning as we're creating an object
videoObject.updateInitialInstanceProperty = function (
objectContent,
instance,
@@ -115,7 +108,6 @@ module.exports = {
) {
return false;
};
// $FlowExpectedError - ignore Flow warning as we're creating an object
videoObject.getInitialInstanceProperties = function (
content,
instance,
@@ -132,13 +124,14 @@ module.exports = {
_('Video'),
_('Displays a video.'),
'JsPlatform/Extensions/videoicon32.png',
// @ts-ignore - TODO: Fix videoObject being an ObjectJsImplementation instead of an ObjectConfiguration
videoObject
)
.setIncludeFile('Extensions/Video/videoruntimeobject.js')
.addIncludeFile('Extensions/Video/videoruntimeobject-pixi-renderer.js')
.setCategoryFullName(_('User interface'))
.addDefaultBehavior('EffectCapability::EffectBehavior')
.addDefaultBehavior("OpacityCapability::OpacityBehavior");
.addDefaultBehavior('OpacityCapability::OpacityBehavior');
object
.addAction(
@@ -533,10 +526,7 @@ module.exports = {
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function (
gd /*: libGDevelop */,
extension /*: gdPlatformExtension*/
) {
runExtensionSanityTests: function (gd, extension) {
return [];
},
/**
@@ -545,9 +535,7 @@ module.exports = {
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function (
objectsEditorService /*: ObjectsEditorService */
) {
registerEditorConfigurations: function (objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'Video::VideoObject',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
@@ -560,9 +548,7 @@ module.exports = {
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
registerInstanceRenderers: function (objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
@@ -570,7 +556,7 @@ module.exports = {
* Renderer for instances of VideoObject inside the IDE.
*/
class RenderedVideoObjectInstance extends RenderedInstance {
constructor (
constructor(
project,
layout,
instance,
@@ -606,11 +592,7 @@ module.exports = {
/**
* Return the path to the thumbnail of the specified object.
*/
static getThumbnail(
project,
resourcesLoader,
objectConfiguration
) {
static getThumbnail(project, resourcesLoader, objectConfiguration) {
return 'JsPlatform/Extensions/videoicon24.png';
}
@@ -647,14 +629,13 @@ module.exports = {
that._pixiObject.texture.on('error', function () {
that._pixiObject.texture.off('error', this);
that._pixiObject.texture =
that._pixiResourcesLoader.getInvalidPIXITexture();
that._pixiObject.texture = that._pixiResourcesLoader.getInvalidPIXITexture();
});
}
}
// Update opacity
const opacity = this._associatedObjectConfiguration
const opacity = +this._associatedObjectConfiguration
.getProperties()
.get('Opacity')
.getValue();

View File

@@ -7,7 +7,11 @@
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/WholeProjectBrowser.h"
#include "GDCore/Project/CustomBehavior.h"
#include "GDCore/Project/CustomBehaviorsSharedData.h"
#include "GDCore/Project/EventsBasedObject.h"
@@ -125,8 +129,6 @@ gd::ObjectMetadata &MetadataDeclarationHelper::DeclareObjectMetadata(
// Note: EventsFunctionsExtension should be used instead of
// PlatformExtension but this line will be removed soon.
.SetCategoryFullName(extension.GetCategory())
// Update Project::CreateObject when default behaviors are added.
.AddDefaultBehavior("EffectCapability::EffectBehavior")
.AddDefaultBehavior("ResizableCapability::ResizableBehavior")
.AddDefaultBehavior("ScalableCapability::ScalableBehavior")
.AddDefaultBehavior("FlippableCapability::FlippableBehavior");
@@ -136,8 +138,17 @@ gd::ObjectMetadata &MetadataDeclarationHelper::DeclareObjectMetadata(
.AddDefaultBehavior("Scene3D::Base3DBehavior");
}
else {
objectMetadata.AddDefaultBehavior("EffectCapability::EffectBehavior");
objectMetadata.AddDefaultBehavior("OpacityCapability::OpacityBehavior");
}
if (eventsBasedObject.IsAnimatable()) {
objectMetadata
.AddDefaultBehavior("AnimatableCapability::AnimatableBehavior");
}
if (eventsBasedObject.IsTextContainer()) {
objectMetadata
.AddDefaultBehavior("TextContainerCapability::TextContainerBehavior");
}
// TODO EBO Use full type to identify object to avoid collision.
// Objects are identified by their name alone.
@@ -1510,7 +1521,7 @@ gd::BehaviorMetadata &MetadataDeclarationHelper::GenerateBehaviorMetadata(
}
gd::ObjectMetadata &MetadataDeclarationHelper::GenerateObjectMetadata(
const gd::Project &project, gd::PlatformExtension &extension,
gd::Project &project, gd::PlatformExtension &extension,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
std::map<gd::String, gd::String> &objectMethodMangledNames) {
@@ -1545,7 +1556,58 @@ gd::ObjectMetadata &MetadataDeclarationHelper::GenerateObjectMetadata(
instructionOrExpression.SetPrivate();
}
UpdateCustomObjectDefaultBehaviors(project, objectMetadata);
return objectMetadata;
}
class DefaultBehaviorUpdater : public gd::ArbitraryObjectsWorker {
public:
DefaultBehaviorUpdater(const gd::Project &project_,
const gd::ObjectMetadata &objectMetadata_)
: project(project_), objectMetadata(objectMetadata_){};
virtual ~DefaultBehaviorUpdater(){};
private:
void DoVisitObject(gd::Object &object) override {
if (object.GetType() != objectMetadata.GetName()) {
return;
}
auto &defaultBehaviorTypes = objectMetadata.GetDefaultBehaviors();
for (const gd::String &behaviorName : object.GetAllBehaviorNames()) {
const auto &behavior = object.GetBehavior(behaviorName);
if (behavior.IsDefaultBehavior()) {
object.RemoveBehavior(behaviorName);
}
}
auto &platform = project.GetCurrentPlatform();
for (const gd::String &behaviorType : defaultBehaviorTypes) {
auto &behaviorMetadata =
gd::MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
gd::LogWarning("Object: " + object.GetType() +
" has an unknown default behavior: " + behaviorType);
continue;
}
const gd::String &behaviorName = behaviorMetadata.GetDefaultName();
auto *behavior =
object.AddNewBehavior(project, behaviorType, behaviorName);
behavior->SetDefaultBehavior(true);
}
}
const gd::Project &project;
const gd::ObjectMetadata &objectMetadata;
};
void MetadataDeclarationHelper::UpdateCustomObjectDefaultBehaviors(
gd::Project &project, const gd::ObjectMetadata &objectMetadata) {
gd::WholeProjectBrowser projectBrowser;
auto defaultBehaviorUpdater = DefaultBehaviorUpdater(project, objectMetadata);
projectBrowser.ExposeObjects(project, defaultBehaviorUpdater);
}
} // namespace gdjs

View File

@@ -61,7 +61,7 @@ public:
std::map<gd::String, gd::String> &behaviorMethodMangledNames);
static gd::ObjectMetadata &GenerateObjectMetadata(
const gd::Project &project, gd::PlatformExtension &extension,
gd::Project &project, gd::PlatformExtension &extension,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
std::map<gd::String, gd::String> &objectMethodMangledNames);
@@ -313,6 +313,10 @@ private:
gd::MultipleInstructionMetadata &multipleInstructionMetadata,
const int userDefinedFirstParameterIndex);
static void
UpdateCustomObjectDefaultBehaviors(gd::Project &project,
const gd::ObjectMetadata &objectMetadata);
static gd::String RemoveTrailingDot(const gd::String &description);
static gd::String

View File

@@ -78,7 +78,7 @@ gd::String ObjectCodeGenerator::GenerateRuntimeObjectCompleteCode(
methodFullyQualifiedName,
"that._onceTriggers",
functionName == doStepPreEventsFunctionName
? GenerateDoStepPreEventsPreludeCode()
? GenerateDoStepPreEventsPreludeCode(eventsBasedObject)
: "",
functionName == onCreatedFunctionName
? "gdjs.CustomRuntimeObject.prototype.onCreated.call(this);\n"
@@ -109,6 +109,77 @@ gd::String ObjectCodeGenerator::GenerateRuntimeObjectCompleteCode(
}
return updateFromObjectCode;
},
// generateInitializeAnimatableCode
[&]() {
return gd::String(R"jscode_template(
this._animator = new gdjs.SpriteAnimator(
objectData.animatable.animations,
gdjs.RENDERER_CLASS_NAME.getAnimationFrameTextureManager(
parentInstanceContainer.getGame().getImageManager()));
)jscode_template")
.FindAndReplace("RENDERER_CLASS_NAME", eventsBasedObject.IsRenderedIn3D() ? "CustomRuntimeObject3DRenderer" : "CustomRuntimeObject2DRenderer");
},
// generateAnimatableCode
[&]() {
return gd::String(R"jscode_template(
// gdjs.Animatable interface implementation
getAnimator() {
return this._animator;
}
getAnimationIndex() {
return this._animator.getAnimationIndex();
}
setAnimationIndex(animationIndex) {
this._animator.setAnimationIndex(animationIndex);
}
getAnimationName() {
return this._animator.getAnimationName();
}
setAnimationName(animationName) {
this._animator.setAnimationName(animationName);
}
hasAnimationEnded() {
return this._animator.hasAnimationEnded();
}
isAnimationPaused() {
return this._animator.isAnimationPaused();
}
pauseAnimation() {
this._animator.pauseAnimation();
}
resumeAnimation() {
this._animator.resumeAnimation();
}
getAnimationSpeedScale() {
return this._animator.getAnimationSpeedScale();
}
setAnimationSpeedScale(ratio) {
this._animator.setAnimationSpeedScale(ratio);
}
getAnimationElapsedTime() {
return this._animator.getAnimationElapsedTime();
}
setAnimationElapsedTime(time) {
this._animator.setAnimationElapsedTime(time);
}
getAnimationDuration() {
return this._animator.getAnimationDuration();
}
)jscode_template");
},
// generateTextContainerCode
[&]() {
return gd::String(R"jscode_template(
// gdjs.TextContainer interface implementation
_text = '';
getText() {
return this._text;
}
setText(text) {
this._text = text;
}
)jscode_template");
});
}
@@ -119,7 +190,10 @@ gd::String ObjectCodeGenerator::GenerateRuntimeObjectTemplateCode(
std::function<gd::String()> generateInitializePropertiesCode,
std::function<gd::String()> generatePropertiesCode,
std::function<gd::String()> generateMethodsCode,
std::function<gd::String()> generateUpdateFromObjectDataCode) {
std::function<gd::String()> generateUpdateFromObjectDataCode,
std::function<gd::String()> generateInitializeAnimatableCode,
std::function<gd::String()> generateAnimatableCode,
std::function<gd::String()> generateTextContainerCode) {
return gd::String(R"jscode_template(
CODE_NAMESPACE = CODE_NAMESPACE || {};
@@ -134,6 +208,7 @@ CODE_NAMESPACE.RUNTIME_OBJECT_CLASSNAME = class RUNTIME_OBJECT_CLASSNAME extends
this._onceTriggers = new gdjs.OnceTriggers();
this._objectData = {};
INITIALIZE_PROPERTIES_CODE
INITIALIZE_ANIMATABLE_CODE
// It calls the onCreated super implementation at the end.
this.onCreated();
@@ -149,6 +224,10 @@ CODE_NAMESPACE.RUNTIME_OBJECT_CLASSNAME = class RUNTIME_OBJECT_CLASSNAME extends
// Properties:
PROPERTIES_CODE
ANIMATABLE_CODE
TEXT_CONTAINER_CODE
}
// Methods:
@@ -166,8 +245,14 @@ gdjs.registerObject("EXTENSION_NAME::OBJECT_NAME", CODE_NAMESPACE.RUNTIME_OBJECT
.FindAndReplace("CODE_NAMESPACE", codeNamespace)
.FindAndReplace("INITIALIZE_PROPERTIES_CODE",
generateInitializePropertiesCode())
.FindAndReplace("INITIALIZE_ANIMATABLE_CODE",
eventsBasedObject.IsAnimatable()
? generateInitializeAnimatableCode()
: "")
.FindAndReplace("UPDATE_FROM_OBJECT_DATA_CODE", generateUpdateFromObjectDataCode())
.FindAndReplace("PROPERTIES_CODE", generatePropertiesCode())
.FindAndReplace("ANIMATABLE_CODE", eventsBasedObject.IsAnimatable() ? generateAnimatableCode() : "")
.FindAndReplace("TEXT_CONTAINER_CODE", eventsBasedObject.IsTextContainer() ? generateTextContainerCode() : "")
.FindAndReplace("METHODS_CODE", generateMethodsCode());
;
}
@@ -265,11 +350,18 @@ CODE_NAMESPACE.RUNTIME_OBJECT_CLASSNAME.prototype.doStepPreEvents = function() {
.FindAndReplace("RUNTIME_OBJECT_CLASSNAME",
eventsBasedObject.GetName())
.FindAndReplace("CODE_NAMESPACE", codeNamespace)
.FindAndReplace("PRELUDE_CODE", GenerateDoStepPreEventsPreludeCode());
}
.FindAndReplace("PRELUDE_CODE",
GenerateDoStepPreEventsPreludeCode(eventsBasedObject));}
gd::String ObjectCodeGenerator::GenerateDoStepPreEventsPreludeCode() {
return "this._onceTriggers.startNewFrame();";
gd::String ObjectCodeGenerator::GenerateDoStepPreEventsPreludeCode(
const gd::EventsBasedObject& eventsBasedObject) {
gd::String doStepPreEventsPreludeCode;
doStepPreEventsPreludeCode += "this._onceTriggers.startNewFrame();";
if (eventsBasedObject.IsAnimatable()) {
doStepPreEventsPreludeCode +=
"\nthis._animator.step(this.getElapsedTime() / 1000);";
}
return doStepPreEventsPreludeCode;
}
} // namespace gdjs

View File

@@ -74,7 +74,10 @@ class ObjectCodeGenerator {
std::function<gd::String()> generateInitializePropertiesCode,
std::function<gd::String()> generateMethodsCode,
std::function<gd::String()> generatePropertiesCode,
std::function<gd::String()> generateUpdateFromObjectDataCode);
std::function<gd::String()> generateUpdateFromObjectDataCode,
std::function<gd::String()> generateInitializeAnimatableCode,
std::function<gd::String()> generateAnimatableCode,
std::function<gd::String()> generateTextContainerCode);
gd::String GenerateRuntimeObjectPropertyTemplateCode(
const gd::EventsBasedObject& eventsBasedObject,
@@ -104,7 +107,8 @@ class ObjectCodeGenerator {
const gd::EventsBasedObject& eventsBasedObject,
const gd::String& codeNamespace);
gd::String GenerateDoStepPreEventsPreludeCode();
gd::String GenerateDoStepPreEventsPreludeCode(
const gd::EventsBasedObject& eventsBasedObject);
gd::Project& project;

View File

@@ -745,6 +745,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
InsertUnique(includesFiles, "variablescontainer.js");
InsertUnique(includesFiles, "oncetriggers.js");
InsertUnique(includesFiles, "runtimebehavior.js");
InsertUnique(includesFiles, "SpriteAnimator.js");
InsertUnique(includesFiles, "spriteruntimeobject.js");
InsertUnique(includesFiles, "affinetransformation.js");
InsertUnique(includesFiles, "CustomRuntimeObjectInstanceContainer.js");

View File

@@ -9,6 +9,7 @@ namespace gdjs {
};
export type CustomObjectConfiguration = ObjectConfiguration & {
animatable?: SpriteAnimationData[];
childrenContent: { [objectName: string]: ObjectConfiguration & any };
};
@@ -86,10 +87,30 @@ namespace gdjs {
oldObjectData: ObjectData & CustomObjectConfiguration,
newObjectData: ObjectData & CustomObjectConfiguration
): boolean {
const animator = this.getAnimator();
if (animator) {
animator.updateFromObjectData(
oldObjectData.animatable || [],
newObjectData.animatable || []
);
}
return this._instanceContainer.updateFrom(oldObjectData, newObjectData);
}
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
const animator = this.getAnimator();
if (initialInstanceData.numberProperties) {
for (
let i = 0, len = initialInstanceData.numberProperties.length;
i < len;
++i
) {
const extraData = initialInstanceData.numberProperties[i];
if (animator && extraData.name === 'animation') {
animator.setAnimationIndex(extraData.value);
}
}
}
if (initialInstanceData.customSize) {
this.setWidth(initialInstanceData.width);
this.setHeight(initialInstanceData.height);
@@ -658,6 +679,15 @@ namespace gdjs {
isFlippedY(): boolean {
return this._flippedY;
}
/**
* Return the sprite animator.
*
* It returns `null` when custom objects don't have the Animatable capability.
*/
getAnimator(): gdjs.SpriteAnimator<any> | null {
return null;
}
}
// Others initialization and internal state management :

View File

@@ -0,0 +1,717 @@
namespace gdjs {
/** Represents a point in a coordinate system. */
export type SpritePoint = {
/** X position of the point. */
x: float;
/** Y position of the point. */
y: float;
};
/** Represents a custom point in a frame. */
export type SpriteCustomPointData = {
/** Name of the point. */
name: string;
/** X position of the point. */
x: float;
/** Y position of the point. */
y: float;
};
/** Represents the center point in a frame. */
export type SpriteCenterPointData = {
/** Name of the point. */
name: string;
/** Is the center automatically computed? */
automatic: boolean;
/** X position of the point. */
x: float;
/** Y position of the point. */
y: float;
};
/** Represents a {@link gdjs.SpriteAnimationFrame}. */
export type SpriteFrameData = {
/** The resource name of the image used in this frame. */
image: string;
/** The points of the frame. */
points: Array<SpriteCustomPointData>;
/** The origin point. */
originPoint: SpriteCustomPointData;
/** The center of the frame. */
centerPoint: SpriteCenterPointData;
/** Is The collision mask custom? */
hasCustomCollisionMask: boolean;
/** The collision mask if it is custom. */
customCollisionMask: Array<Array<SpritePoint>>;
};
/** Represents the data of a {@link gdjs.SpriteAnimationDirection}. */
export type SpriteDirectionData = {
/** Time between each frame, in seconds. */
timeBetweenFrames: float;
/** Is the animation looping? */
looping: boolean;
/** The list of frames. */
sprites: Array<SpriteFrameData>;
};
/** Represents the data of a {@link gdjs.SpriteAnimation}. */
export type SpriteAnimationData = {
/** The name of the animation. */
name: string;
/** Does the animation use multiple {@link gdjs.SpriteAnimationDirection}? */
useMultipleDirections: boolean;
/** The list of {@link SpriteDirectionData} representing {@link gdjs.SpriteAnimationDirection} instances. */
directions: Array<SpriteDirectionData>;
};
/**
* Abstraction from graphic libraries texture classes.
*/
export interface AnimationFrameTextureManager<T> {
getAnimationFrameTexture(imageName: string): T;
getAnimationFrameWidth(pixiTexture: T);
getAnimationFrameHeight(pixiTexture: T);
}
/**
* A frame used by a SpriteAnimation in a {@link gdjs.SpriteRuntimeObject}.
*
* It contains the texture displayed as well as information like the points position
* or the collision mask.
*/
export class SpriteAnimationFrame<T> {
image: string;
//TODO: Rename in imageName, and do not store it in the object?
texture: T;
center: SpritePoint = { x: 0, y: 0 };
origin: SpritePoint = { x: 0, y: 0 };
hasCustomHitBoxes: boolean = false;
customHitBoxes: gdjs.Polygon[] = [];
points: Hashtable<SpritePoint>;
/**
* @param imageManager The game image manager
* @param frameData The frame data used to initialize the frame
*/
constructor(
frameData: SpriteFrameData,
textureManager: gdjs.AnimationFrameTextureManager<T>
) {
this.image = frameData ? frameData.image : '';
this.texture = textureManager.getAnimationFrameTexture(this.image);
this.points = new Hashtable();
this.reinitialize(frameData, textureManager);
}
/**
* @param frameData The frame data used to initialize the frame
* @param textureManager The game image manager
*/
reinitialize(
frameData: SpriteFrameData,
textureManager: gdjs.AnimationFrameTextureManager<T>
) {
this.points.clear();
for (let i = 0, len = frameData.points.length; i < len; ++i) {
const ptData = frameData.points[i];
const point = { x: ptData.x, y: ptData.y };
this.points.put(ptData.name, point);
}
const origin = frameData.originPoint;
this.origin.x = origin.x;
this.origin.y = origin.y;
const center = frameData.centerPoint;
if (center.automatic !== true) {
this.center.x = center.x;
this.center.y = center.y;
} else {
this.center.x = textureManager.getAnimationFrameWidth(this.texture) / 2;
this.center.y =
textureManager.getAnimationFrameHeight(this.texture) / 2;
}
//Load the custom collision mask, if any:
if (frameData.hasCustomCollisionMask) {
this.hasCustomHitBoxes = true;
let i = 0;
for (let len = frameData.customCollisionMask.length; i < len; ++i) {
const polygonData: SpritePoint[] = frameData.customCollisionMask[i];
//Add a polygon, if necessary (Avoid recreating a polygon if it already exists).
if (i >= this.customHitBoxes.length) {
this.customHitBoxes.push(new gdjs.Polygon());
}
let j = 0;
for (const len2 = polygonData.length; j < len2; ++j) {
const pointData: SpritePoint = polygonData[j];
//Add a point, if necessary (Avoid recreating a point if it already exists).
if (j >= this.customHitBoxes[i].vertices.length) {
this.customHitBoxes[i].vertices.push([0, 0]);
}
this.customHitBoxes[i].vertices[j][0] = pointData.x;
this.customHitBoxes[i].vertices[j][1] = pointData.y;
}
this.customHitBoxes[i].vertices.length = j;
}
this.customHitBoxes.length = i;
} else {
this.customHitBoxes.length = 0;
}
}
/**
* Get a point of the frame.<br>
* If the point does not exist, the origin is returned.
* @param name The point's name
* @return The requested point. If it doesn't exists returns the origin point.
*/
getPoint(name: string): SpritePoint {
if (name === 'Centre' || name === 'Center') {
return this.center;
} else {
if (name === 'Origin') {
return this.origin;
}
}
return this.points.containsKey(name)
? this.points.get(name)
: this.origin;
}
}
/**
* Represents a direction of an animation of a {@link gdjs.SpriteRuntimeObject}.
*/
export class SpriteAnimationDirection<T> {
timeBetweenFrames: float;
loop: boolean;
frames: SpriteAnimationFrame<T>[] = [];
/**
* @param imageManager The game image manager
* @param directionData The direction data used to initialize the direction
*/
constructor(
directionData: SpriteDirectionData,
textureManager: gdjs.AnimationFrameTextureManager<T>
) {
this.timeBetweenFrames = directionData
? directionData.timeBetweenFrames
: 1.0;
this.loop = !!directionData.looping;
this.reinitialize(directionData, textureManager);
}
/**
* @param directionData The direction data used to initialize the direction
* @param textureManager The game image manager
*/
reinitialize(
directionData: SpriteDirectionData,
textureManager: gdjs.AnimationFrameTextureManager<T>
) {
this.timeBetweenFrames = directionData
? directionData.timeBetweenFrames
: 1.0;
this.loop = !!directionData.looping;
let i = 0;
for (const len = directionData.sprites.length; i < len; ++i) {
const frameData = directionData.sprites[i];
if (i < this.frames.length) {
this.frames[i].reinitialize(frameData, textureManager);
} else {
this.frames.push(
new gdjs.SpriteAnimationFrame<T>(frameData, textureManager)
);
}
}
this.frames.length = i;
}
}
/**
* Represents an animation of a {@link SpriteRuntimeObject}.
*/
export class SpriteAnimation<T> {
hasMultipleDirections: boolean;
name: string;
directions: gdjs.SpriteAnimationDirection<T>[] = [];
/**
* @param animData The animation data used to initialize the animation
* @param textureManager The game image manager
*/
constructor(
animData: SpriteAnimationData,
textureManager: gdjs.AnimationFrameTextureManager<T>
) {
this.hasMultipleDirections = !!animData.useMultipleDirections;
this.name = animData.name || '';
this.reinitialize(animData, textureManager);
}
/**
* @param animData The animation data used to initialize the animation
* @param textureManager The game image manager
*/
reinitialize(
animData: SpriteAnimationData,
textureManager: gdjs.AnimationFrameTextureManager<T>
) {
this.hasMultipleDirections = !!animData.useMultipleDirections;
this.name = animData.name || '';
let i = 0;
for (const len = animData.directions.length; i < len; ++i) {
const directionData = animData.directions[i];
if (i < this.directions.length) {
this.directions[i].reinitialize(directionData, textureManager);
} else {
this.directions.push(
new gdjs.SpriteAnimationDirection(directionData, textureManager)
);
}
}
// Make sure to delete already existing directions which are not used anymore.
this.directions.length = i;
}
}
/**
* Image-base animation model.
*/
export class SpriteAnimator<T> implements gdjs.Animatable {
_animations: gdjs.SpriteAnimation<T>[] = [];
_textureManager: gdjs.AnimationFrameTextureManager<T>;
/**
* Reference to the current SpriteAnimationFrame that is displayed.
* Can be null, so ensure that this case is handled properly.
*/
private _animationFrame: gdjs.SpriteAnimationFrame<T> | null = null;
private _animationFrameDirty: boolean = true;
private _currentAnimation: integer = 0;
private _currentDirection: integer = 0;
private _currentFrameIndex: integer = 0;
/** In seconds */
private _animationElapsedTime: float = 0;
private _animationSpeedScale: float = 1;
private _animationPaused: boolean = false;
private _onFrameChange: (() => void) | null = null;
/**
* @param frameData The frame data used to initialize the frame
* @param textureManager The game image manager
*/
constructor(
animations: Array<SpriteAnimationData>,
textureManager: gdjs.AnimationFrameTextureManager<T>
) {
this._textureManager = textureManager;
for (let i = 0, len = animations.length; i < len; ++i) {
this._animations.push(
new gdjs.SpriteAnimation(animations[i], textureManager)
);
}
}
invalidateFrame() {
this._animationFrameDirty = true;
if (this._onFrameChange) {
this._onFrameChange();
}
}
reinitialize(animations: Array<SpriteAnimationData>) {
this._currentAnimation = 0;
this._currentDirection = 0;
this._currentFrameIndex = 0;
this._animationElapsedTime = 0;
this._animationSpeedScale = 1;
this._animationPaused = false;
let i = 0;
for (const len = animations.length; i < len; ++i) {
const animData = animations[i];
if (i < this._animations.length) {
this._animations[i].reinitialize(animData, this._textureManager);
} else {
this._animations.push(
new gdjs.SpriteAnimation(animData, this._textureManager)
);
}
}
this._animations.length = i;
// Make sure to delete already existing animations which are not used anymore.
this._animationFrame = null;
this.invalidateFrame();
}
updateFromObjectData(
oldAnimations: Array<SpriteAnimationData>,
newAnimations: Array<SpriteAnimationData>
): boolean {
let i = 0;
for (const len = newAnimations.length; i < len; ++i) {
const animData = newAnimations[i];
if (i < this._animations.length) {
this._animations[i].reinitialize(animData, this._textureManager);
} else {
this._animations.push(
new gdjs.SpriteAnimation(animData, this._textureManager)
);
}
}
this._animations.length = i;
// Make sure to delete already existing animations which are not used anymore.
this.invalidateFrame();
const animationFrame = this.getCurrentFrame();
if (!animationFrame) {
this.setAnimationIndex(0);
}
return true;
}
/**
* @returns Returns the current frame or null if the current animation doesn't have any frame.
*/
getCurrentFrame(): gdjs.SpriteAnimationFrame<T> | null {
if (!this._animationFrameDirty) {
return this._animationFrame;
}
this._animationFrameDirty = false;
if (
this._currentAnimation < this._animations.length &&
this._currentDirection <
this._animations[this._currentAnimation].directions.length
) {
const direction = this._animations[this._currentAnimation].directions[
this._currentDirection
];
if (this._currentFrameIndex < direction.frames.length) {
this._animationFrame = direction.frames[this._currentFrameIndex];
return this._animationFrame;
}
}
//Invalid animation/direction/frame:
this._animationFrame = null;
return this._animationFrame;
}
/**
* Update the current frame of the object according to the elapsed time on the scene.
* @param timeDelta in seconds
*/
step(timeDelta: float): boolean {
if (
this._currentAnimation >= this._animations.length ||
this._currentDirection >=
this._animations[this._currentAnimation].directions.length
) {
return false;
}
const direction = this._animations[this._currentAnimation].directions[
this._currentDirection
];
const animationDuration = this.getAnimationDuration();
if (
!this._animationPaused &&
(direction.loop || this._animationElapsedTime !== animationDuration) &&
direction.timeBetweenFrames
) {
const animationElapsedTime =
this._animationElapsedTime + timeDelta * this._animationSpeedScale;
return this.setAnimationElapsedTime(
direction.loop
? gdjs.evtTools.common.mod(animationElapsedTime, animationDuration)
: gdjs.evtTools.common.clamp(
animationElapsedTime,
0,
animationDuration
)
);
}
return false;
}
/**
* Register a listener to frame changes.
*
* It's useful for custom objects as they don't drive this class themselves.
*
* @param callback Called each time {@link getCurrentFrame} changes.
*/
setOnFrameChangeCallback(callback: () => void): void {
this._onFrameChange = callback;
}
getAnimationIndex(): integer {
return this._currentAnimation;
}
setAnimationIndex(newAnimation: integer): boolean {
// Truncate the index.
newAnimation = newAnimation | 0;
if (
newAnimation < this._animations.length &&
this._currentAnimation !== newAnimation &&
newAnimation >= 0
) {
this._currentAnimation = newAnimation;
this._currentFrameIndex = 0;
this._animationElapsedTime = 0;
this.invalidateFrame();
return true;
}
return false;
}
getAnimationName(): string {
if (this._currentAnimation >= this._animations.length) {
return '';
}
return this._animations[this._currentAnimation].name;
}
setAnimationName(newAnimationName: string): boolean {
if (!newAnimationName) {
return false;
}
for (let i = 0; i < this._animations.length; ++i) {
if (this._animations[i].name === newAnimationName) {
this.setAnimationIndex(i);
return true;
}
}
return false;
}
hasAnimationEnded(): boolean {
if (
this._currentAnimation >= this._animations.length ||
this._currentDirection >=
this._animations[this._currentAnimation].directions.length
) {
return true;
}
const direction = this._animations[this._currentAnimation].directions[
this._currentDirection
];
if (direction.loop) {
return false;
}
return (
this._currentFrameIndex === direction.frames.length - 1 &&
this._animationElapsedTime ===
direction.frames.length * direction.timeBetweenFrames
);
}
isAnimationPaused() {
return this._animationPaused;
}
pauseAnimation() {
this._animationPaused = true;
}
resumeAnimation() {
this._animationPaused = false;
}
getAnimationSpeedScale() {
return this._animationSpeedScale;
}
setAnimationSpeedScale(ratio: float): void {
this._animationSpeedScale = ratio;
}
/**
* Change the current frame displayed by the animation
* @param newFrameIndex The index of the frame to be displayed
*/
setAnimationFrameIndex(newFrameIndex: integer): boolean {
if (
this._currentAnimation >= this._animations.length ||
this._currentDirection >=
this._animations[this._currentAnimation].directions.length
) {
return false;
}
const direction = this._animations[this._currentAnimation].directions[
this._currentDirection
];
if (
newFrameIndex >= 0 &&
newFrameIndex < direction.frames.length &&
newFrameIndex !== this._currentFrameIndex
) {
this._currentFrameIndex = newFrameIndex;
this._animationElapsedTime =
newFrameIndex * direction.timeBetweenFrames;
this.invalidateFrame();
return true;
}
return false;
}
/**
* Get the index of the current frame displayed by the animation
* @return newFrame The index of the frame being displayed
*/
getAnimationFrameIndex(): integer {
return this._currentFrameIndex;
}
getAnimationElapsedTime(): float {
return this._animationElapsedTime;
}
setAnimationElapsedTime(time: float): boolean {
const direction = this._animations[this._currentAnimation].directions[
this._currentDirection
];
this._animationElapsedTime = gdjs.evtTools.common.clamp(
time,
0,
this.getAnimationDuration()
);
const oldFrame = this._currentFrameIndex;
this._currentFrameIndex = Math.min(
Math.floor(this._animationElapsedTime / direction.timeBetweenFrames),
direction.frames.length - 1
);
if (oldFrame !== this._currentFrameIndex) {
this.invalidateFrame();
return true;
}
return false;
}
getAnimationDuration(): float {
const direction = this._animations[this._currentAnimation].directions[
this._currentDirection
];
return direction.frames.length * direction.timeBetweenFrames;
}
getAnimationFrameCount(): integer {
if (this._currentAnimation >= this._animations.length) {
return 0;
}
const currentAnimation = this._animations[this._currentAnimation];
if (this._currentDirection >= currentAnimation.directions.length) {
return 0;
}
return currentAnimation.directions[this._currentDirection].frames.length;
}
/**
* Change the angle (or direction index) of the object
* @param The new angle (or direction index) to be applied
* @deprecated
*/
setDirectionOrAngle(oldValue: float, newValue: float): float | null {
if (this._currentAnimation >= this._animations.length) {
return null;
}
const anim = this._animations[this._currentAnimation];
if (!anim.hasMultipleDirections) {
return oldValue === newValue ? null : newValue;
} else {
newValue = newValue | 0;
if (
newValue === this._currentDirection ||
newValue >= anim.directions.length ||
anim.directions[newValue].frames.length === 0
) {
return null;
}
this._currentDirection = newValue;
this._currentFrameIndex = 0;
this._animationElapsedTime = 0;
this.invalidateFrame();
return 0;
}
}
/**
* @deprecated
*/
getDirectionOrAngle(angle: float): float {
if (this._currentAnimation >= this._animations.length) {
return 0;
}
if (!this._animations[this._currentAnimation].hasMultipleDirections) {
return angle;
} else {
return this._currentDirection;
}
}
/**
* @deprecated
*/
getAngle(angle: float): float {
if (this._currentAnimation >= this._animations.length) {
return 0;
}
if (!this._animations[this._currentAnimation].hasMultipleDirections) {
return angle;
} else {
return this._currentDirection * 45;
}
}
/**
* @deprecated
*/
setAngle(oldAngle: float, angle: float): float | null {
if (this._currentAnimation >= this._animations.length) {
return null;
}
if (!this._animations[this._currentAnimation].hasMultipleDirections) {
if (oldAngle === angle) {
return null;
}
return angle;
} else {
angle = angle % 360;
if (angle < 0) {
angle += 360;
}
return this.setDirectionOrAngle(oldAngle, Math.round(angle / 45) % 8);
}
}
/**
* @deprecated
* Return true if animation has ended.
* Prefer using {@link hasAnimationEnded}. This method returns true as soon as
* the animation enters the last frame, not at the end of the last frame.
*/
hasAnimationEndedLegacy(): boolean {
if (
this._currentAnimation >= this._animations.length ||
this._currentDirection >=
this._animations[this._currentAnimation].directions.length
) {
return true;
}
const direction = this._animations[this._currentAnimation].directions[
this._currentDirection
];
if (direction.loop) {
return false;
}
return this._currentFrameIndex === direction.frames.length - 1;
}
}
}

View File

@@ -18,7 +18,7 @@ namespace gdjs {
}
/**
* A behavior that forwards the Animatable interface to its object.
* A behavior that forwards the TextContainer interface to its object.
*/
export class TextContainerBehavior
extends gdjs.RuntimeBehavior

View File

@@ -152,6 +152,14 @@ namespace gdjs {
this._pixiContainer.removeChild(layerPixiObject);
this._pixiContainer.addChildAt(layerPixiObject, index);
}
static getAnimationFrameTextureManager(
imageManager: gdjs.PixiImageManager
) {
return gdjs.SpriteRuntimeObjectPixiRenderer.getAnimationFrameTextureManager(
imageManager
);
}
}
// Register the class to let the engine use it.

View File

@@ -264,9 +264,10 @@ namespace gdjs {
// Draw custom point
if (showCustomPoints && object instanceof gdjs.SpriteRuntimeObject) {
if (!object._animationFrame) continue;
const animationFrame = object._animator.getCurrentFrame();
if (!animationFrame) continue;
for (const customPointName in object._animationFrame.points.items) {
for (const customPointName in animationFrame.points.items) {
let customPoint = object.getPointPosition(customPointName);
customPoint = layer.applyLayerTransformation(

View File

@@ -228,7 +228,7 @@ namespace gdjs {
useTransparentTexture,
forceBasicMaterial,
}: { useTransparentTexture: boolean; forceBasicMaterial: boolean }
) {
): THREE.Material {
const cacheKey = `${resourceName}|${useTransparentTexture ? 1 : 0}|${
forceBasicMaterial ? 1 : 0
}`;

View File

@@ -1,4 +1,7 @@
namespace gdjs {
export interface PixiImageManager {
_pixiAnimationFrameTextureManager: PixiAnimationFrameTextureManager;
}
/**
* The renderer for a gdjs.SpriteRuntimeObject using Pixi.js.
*/
@@ -19,9 +22,8 @@ namespace gdjs {
instanceContainer: gdjs.RuntimeInstanceContainer
) {
this._object = runtimeObject;
this._sprite = new PIXI.Sprite(
instanceContainer.getGame().getImageManager().getInvalidPIXITexture()
);
const imageManager = instanceContainer.getGame().getImageManager();
this._sprite = new PIXI.Sprite(imageManager.getInvalidPIXITexture());
const layer = instanceContainer.getLayer('');
if (layer) {
layer
@@ -54,22 +56,19 @@ namespace gdjs {
* Update the internal PIXI.Sprite position, angle...
*/
_updatePIXISprite() {
if (this._object._animationFrame !== null) {
const animationFrame = this._object._animator.getCurrentFrame();
if (animationFrame !== null) {
this._sprite.anchor.x =
this._object._animationFrame.center.x /
this._sprite.texture.frame.width;
animationFrame.center.x / this._sprite.texture.frame.width;
this._sprite.anchor.y =
this._object._animationFrame.center.y /
this._sprite.texture.frame.height;
animationFrame.center.y / this._sprite.texture.frame.height;
this._sprite.position.x =
this._object.x +
(this._object._animationFrame.center.x -
this._object._animationFrame.origin.x) *
(animationFrame.center.x - animationFrame.origin.x) *
Math.abs(this._object._scaleX);
this._sprite.position.y =
this._object.y +
(this._object._animationFrame.center.y -
this._object._animationFrame.origin.y) *
(animationFrame.center.y - animationFrame.origin.y) *
Math.abs(this._object._scaleY);
this._sprite.rotation = gdjs.toRad(this._object.angle);
this._sprite.visible = !this._object.hidden;
@@ -100,7 +99,7 @@ namespace gdjs {
/**
* Update the internal texture of the PIXI sprite.
*/
updateFrame(animationFrame): void {
updateFrame(animationFrame: gdjs.SpriteAnimationFrame<PIXI.Texture>): void {
this._spriteDirty = true;
this._sprite.texture = animationFrame.texture;
}
@@ -110,8 +109,9 @@ namespace gdjs {
}
updateX(): void {
const animationFrame = this._object
._animationFrame as SpriteAnimationFrame;
const animationFrame = this._object._animator.getCurrentFrame() as SpriteAnimationFrame<
PIXI.Texture
>;
this._sprite.position.x =
this._object.x +
(animationFrame.center.x - animationFrame.origin.x) *
@@ -119,8 +119,9 @@ namespace gdjs {
}
updateY(): void {
const animationFrame = this._object
._animationFrame as SpriteAnimationFrame;
const animationFrame = this._object._animator.getCurrentFrame() as SpriteAnimationFrame<
PIXI.Texture
>;
this._sprite.position.y =
this._object.y +
(animationFrame.center.y - animationFrame.origin.y) *
@@ -184,18 +185,35 @@ namespace gdjs {
return this._sprite.texture.frame.height;
}
static getAnimationFrame(
imageManager: gdjs.PixiImageManager,
imageName: string
) {
return imageManager.getPIXITexture(imageName);
static getAnimationFrameTextureManager(
imageManager: gdjs.PixiImageManager
): PixiAnimationFrameTextureManager {
if (!imageManager._pixiAnimationFrameTextureManager) {
imageManager._pixiAnimationFrameTextureManager = new PixiAnimationFrameTextureManager(
imageManager
);
}
return imageManager._pixiAnimationFrameTextureManager;
}
}
class PixiAnimationFrameTextureManager
implements gdjs.AnimationFrameTextureManager<PIXI.Texture> {
private _imageManager: gdjs.PixiImageManager;
constructor(imageManager: gdjs.PixiImageManager) {
this._imageManager = imageManager;
}
static getAnimationFrameWidth(pixiTexture: PIXI.Texture) {
getAnimationFrameTexture(imageName: string) {
return this._imageManager.getPIXITexture(imageName);
}
getAnimationFrameWidth(pixiTexture: PIXI.Texture) {
return pixiTexture.width;
}
static getAnimationFrameHeight(pixiTexture: PIXI.Texture) {
getAnimationFrameHeight(pixiTexture: PIXI.Texture) {
return pixiTexture.height;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -29,8 +29,8 @@
"check-types": "tsc",
"build": "node scripts/build.js",
"test": "cd tests && npm run test-benchmark",
"format": "prettier --write \"Runtime/**/*.ts\" \"../Extensions/**/*.ts\" \"../Extensions/**/*.spec.js\"",
"check-format": "prettier --list-different \"Runtime/**/*.ts\" \"../Extensions/**/*.ts\" \"../Extensions/**/*.spec.js\"",
"format": "prettier --write \"Runtime/**/*.ts\" \"../Extensions/**/*.ts\" \"../Extensions/**/JsExtension.js\" \"../Extensions/**/*.spec.js\"",
"check-format": "prettier --list-different \"Runtime/**/*.ts\" \"../Extensions/**/*.ts\" \"../Extensions/**/JsExtension.js\" \"../Extensions/**/*.spec.js\"",
"generate-doc": "typedoc --options docs/typedoc.json"
}
}

View File

@@ -70,6 +70,7 @@ module.exports = function (config) {
'./newIDE/app/resources/GDJS/Runtime/variablescontainer.js',
'./newIDE/app/resources/GDJS/Runtime/oncetriggers.js',
'./newIDE/app/resources/GDJS/Runtime/runtimebehavior.js',
'./newIDE/app/resources/GDJS/Runtime/SpriteAnimator.js',
'./newIDE/app/resources/GDJS/Runtime/spriteruntimeobject.js',
'./newIDE/app/resources/GDJS/Runtime/CustomRuntimeObject.js',
'./newIDE/app/resources/GDJS/Runtime/CustomRuntimeObject2D.js',

View File

@@ -202,7 +202,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
runtimeScene.renderAndStep(stepDurationInMilliseconds);
expect(object._animationElapsedTime).to.be(
expect(object.getAnimationElapsedTime()).to.be(
stepDurationInMilliseconds / 1000
);
@@ -232,7 +232,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
}
// Almost at the animation end.
expect(object._animationElapsedTime).to.be.within(
expect(object.getAnimationElapsedTime()).to.be.within(
3 * firstAnimationTimeBetweenFrames - stepDurationInMilliseconds / 1000,
3 * firstAnimationTimeBetweenFrames - 0.001
);
@@ -241,7 +241,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
runtimeScene.renderAndStep(stepDurationInMilliseconds);
// The animation ended.
expect(object._animationElapsedTime).to.be(
expect(object.getAnimationElapsedTime()).to.be(
3 * firstAnimationTimeBetweenFrames
);
expect(object.getAnimationFrame()).to.be(2);
@@ -249,7 +249,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
runtimeScene.renderAndStep(stepDurationInMilliseconds);
// No change.
expect(object._animationElapsedTime).to.be(
expect(object.getAnimationElapsedTime()).to.be(
3 * firstAnimationTimeBetweenFrames
);
expect(object.getAnimationFrame()).to.be(2);
@@ -272,7 +272,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
}
// Almost at the animation end.
expect(object._animationElapsedTime).to.be.within(
expect(object.getAnimationElapsedTime()).to.be.within(
3 * firstAnimationTimeBetweenFrames - stepDurationInMilliseconds / 1000,
3 * firstAnimationTimeBetweenFrames - 0.001
);
@@ -281,7 +281,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
runtimeScene.renderAndStep(stepDurationInMilliseconds);
// The animation looped to the beginning.
expect(object._animationElapsedTime).to.within(0.01, 0.02);
expect(object.getAnimationElapsedTime()).to.within(0.01, 0.02);
expect(object.getAnimationFrame()).to.be(0);
});
});
@@ -302,7 +302,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
runtimeScene.renderAndStep(stepDurationInMilliseconds);
expect(object._animationElapsedTime).to.be(
expect(object.getAnimationElapsedTime()).to.be(
2 * firstAnimationTimeBetweenFrames - stepDurationInMilliseconds / 1000
);
expect(object.getAnimationFrame()).to.be(1);
@@ -326,7 +326,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
}
// Almost at the animation beginning.
expect(object._animationElapsedTime).to.be.within(
expect(object.getAnimationElapsedTime()).to.be.within(
0.001,
stepDurationInMilliseconds / 60
);
@@ -335,13 +335,13 @@ describe('gdjs.SpriteRuntimeObject', () => {
runtimeScene.renderAndStep(stepDurationInMilliseconds);
// Reached the animation beginning.
expect(object._animationElapsedTime).to.be(0);
expect(object.getAnimationElapsedTime()).to.be(0);
expect(object.getAnimationFrame()).to.be(0);
runtimeScene.renderAndStep(stepDurationInMilliseconds);
// No change.
expect(object._animationElapsedTime).to.be(0);
expect(object.getAnimationElapsedTime()).to.be(0);
expect(object.getAnimationFrame()).to.be(0);
});
@@ -364,7 +364,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
}
// Almost at the animation beginning.
expect(object._animationElapsedTime).to.be.within(
expect(object.getAnimationElapsedTime()).to.be.within(
0.001,
stepDurationInMilliseconds / 60
);
@@ -373,7 +373,7 @@ describe('gdjs.SpriteRuntimeObject', () => {
runtimeScene.renderAndStep(stepDurationInMilliseconds);
// Reached the animation beginning.
expect(object._animationElapsedTime).to.be.within(
expect(object.getAnimationElapsedTime()).to.be.within(
3 * firstAnimationTimeBetweenFrames - 0.004,
3 * firstAnimationTimeBetweenFrames - 0.003
);
@@ -395,16 +395,16 @@ describe('gdjs.SpriteRuntimeObject', () => {
runtimeScene.renderAndStep(stepDurationInMilliseconds);
expect(object._animationElapsedTime).to.not.be(0);
expect(object.getAnimationElapsedTime()).to.not.be(0);
object.setAnimation(1);
expect(object.getAnimationFrame()).to.be(0);
expect(object._animationElapsedTime).to.be(0);
expect(object.getAnimationElapsedTime()).to.be(0);
runtimeScene.renderAndStep(stepDurationInMilliseconds);
expect(object._animationElapsedTime).to.not.be(0);
expect(object.getAnimationElapsedTime()).to.not.be(0);
});
it('should reset the elapsed time on a frame when changing animation frame', () => {
@@ -420,18 +420,18 @@ describe('gdjs.SpriteRuntimeObject', () => {
runtimeScene.renderAndStep(stepDurationInMilliseconds);
expect(object._animationElapsedTime).to.not.be(0);
expect(object.getAnimationElapsedTime()).to.not.be(0);
object.setAnimationFrame(2);
expect(object.getAnimationFrame()).to.be(2);
expect(object._animationElapsedTime).to.be(
expect(object.getAnimationElapsedTime()).to.be(
2 * firstAnimationTimeBetweenFrames
);
runtimeScene.renderAndStep(stepDurationInMilliseconds);
expect(object._animationElapsedTime).to.not.be(0);
expect(object.getAnimationElapsedTime()).to.not.be(0);
});
});
});

View File

@@ -772,6 +772,8 @@ interface CustomObjectConfiguration {
[Value] MapStringPropertyDescriptor GetInitialInstanceProperties([Const, Ref] InitialInstance instance, [Ref] Project project, [Ref] Layout scene);
boolean UpdateInitialInstanceProperty([Ref] InitialInstance instance, [Const] DOMString name, [Const] DOMString value, [Ref] Project project, [Ref] Layout scene);
[Ref] SpriteAnimationList GetAnimations();
};
CustomObjectConfiguration implements ObjectConfiguration;
@@ -1173,6 +1175,8 @@ interface InitialInstance {
void SetLocked(boolean lock);
boolean IsSealed();
void SetSealed(boolean seal);
boolean ShouldKeepRatio();
void SetShouldKeepRatio(boolean keepRatio);
long GetZOrder();
void SetZOrder(long zOrder);
[Const, Ref] DOMString GetLayer();
@@ -1386,8 +1390,8 @@ interface ParameterOptions {
interface AbstractFunctionMetadata {
[Ref] AbstractFunctionMetadata AddParameter([Const] DOMString type,
[Const] DOMString description,
[Const] DOMString optionalObjectType,
boolean parameterIsOptional);
[Const] optional DOMString optionalObjectType,
optional boolean parameterIsOptional);
[Ref] AbstractFunctionMetadata AddCodeOnlyParameter(
[Const] DOMString type, [Const] DOMString supplementaryInformation);
[Ref] AbstractFunctionMetadata SetDefaultValue([Const] DOMString defaultValue);
@@ -1437,8 +1441,8 @@ interface InstructionMetadata {
[Ref] InstructionMetadata SetRelevantForCustomObjectEventsOnly();
[Ref] InstructionMetadata AddParameter([Const] DOMString type,
[Const] DOMString description,
[Const] DOMString optionalObjectType,
boolean parameterIsOptional);
[Const] optional DOMString optionalObjectType,
optional boolean parameterIsOptional);
[Ref] InstructionMetadata AddCodeOnlyParameter(
[Const] DOMString type, [Const] DOMString supplementaryInformation);
[Ref] InstructionMetadata SetDefaultValue([Const] DOMString defaultValue);
@@ -1502,8 +1506,8 @@ interface ExpressionMetadata {
[Ref] ExpressionMetadata AddParameter(
[Const] DOMString type,
[Const] DOMString description,
[Const] DOMString optionalObjectType,
boolean parameterIsOptional);
[Const] optional DOMString optionalObjectType,
optional boolean parameterIsOptional);
[Ref] ExpressionMetadata AddCodeOnlyParameter(
[Const] DOMString type, [Const] DOMString supplementaryInformation);
[Ref] ExpressionMetadata SetDefaultValue([Const] DOMString defaultValue);
@@ -1524,8 +1528,8 @@ ExpressionMetadata implements AbstractFunctionMetadata;
interface MultipleInstructionMetadata {
[Ref] MultipleInstructionMetadata AddParameter([Const] DOMString type,
[Const] DOMString description,
[Const] DOMString optionalObjectType,
boolean parameterIsOptional);
[Const] optional DOMString optionalObjectType,
optional boolean parameterIsOptional);
[Ref] MultipleInstructionMetadata AddCodeOnlyParameter(
[Const] DOMString type, [Const] DOMString supplementaryInformation);
[Ref] MultipleInstructionMetadata SetDefaultValue([Const] DOMString defaultValue);
@@ -1853,7 +1857,7 @@ interface BehaviorMetadata {
[Ref] Behavior Get();
BehaviorsSharedData GetSharedDataInstance();
[Value] MapStringPropertyDescriptor GetProperties();
[Value] MapStringPropertyDescriptor GetSharedProperties();
};
@@ -2790,6 +2794,10 @@ interface EventsBasedObject {
[Ref] EventsBasedObject MarkAsRenderedIn3D(boolean isRenderedIn3D);
boolean IsRenderedIn3D();
[Ref] EventsBasedObject MarkAsAnimatable(boolean isAnimatable);
boolean IsAnimatable();
[Ref] EventsBasedObject MarkAsTextContainer(boolean isTextContainer);
boolean IsTextContainer();
[Const, Value] DOMString STATIC_GetPropertyActionName([Const] DOMString propertyName);
[Const, Value] DOMString STATIC_GetPropertyConditionName([Const] DOMString propertyName);
@@ -3169,8 +3177,8 @@ interface Animation {
void SetUseMultipleDirections(boolean enable);
};
interface SpriteObject {
void SpriteObject();
interface SpriteAnimationList {
void SpriteAnimationList();
void AddAnimation([Const, Ref] Animation animation);
[Ref] Animation GetAnimation(unsigned long index);
@@ -3181,12 +3189,18 @@ interface SpriteObject {
void SwapAnimations(unsigned long first, unsigned long second);
void MoveAnimation(unsigned long oldIndex, unsigned long newIndex);
void SetUpdateIfNotVisible(boolean updateIfNotVisible);
boolean GetUpdateIfNotVisible();
boolean AdaptCollisionMaskAutomatically();
void SetAdaptCollisionMaskAutomatically(boolean adaptCollisionMaskAutomatically);
};
interface SpriteObject {
void SpriteObject();
[Ref] SpriteAnimationList GetAnimations();
void SetUpdateIfNotVisible(boolean updateIfNotVisible);
boolean GetUpdateIfNotVisible();
};
SpriteObject implements ObjectConfiguration;
interface Model3DAnimation {
@@ -3608,7 +3622,7 @@ interface MetadataDeclarationHelper {
[Ref] MapStringString behaviorMethodMangledNames);
[Ref] ObjectMetadata STATIC_GenerateObjectMetadata(
[Const, Ref] Project project,
[Ref] Project project,
[Ref] PlatformExtension extension,
[Const, Ref] EventsFunctionsExtension eventsFunctionsExtension,
[Const, Ref] EventsBasedObject eventsBasedObject,

View File

@@ -81,6 +81,7 @@ target_link_libraries(GD "-s MODULARIZE=1")
target_link_libraries(GD "-s EXPORT_NAME=\"initializeGDevelopJs\"") # Global function name for browsers
target_link_libraries(GD "-s TOTAL_MEMORY=48MB") # Get some initial memory size that is a bit bigger than the default.
target_link_libraries(GD "-s ALLOW_MEMORY_GROWTH=1")
target_link_libraries(GD "-s NODEJS_CATCH_EXIT=0") # Don't print the entire GDCore code on error when running in node
target_link_libraries(GD "-s ERROR_ON_UNDEFINED_SYMBOLS=0")
target_link_libraries(GD "-s \"EXPORTED_FUNCTIONS=['_free']\"")

View File

@@ -1522,7 +1522,7 @@ describe('libGD.js', function () {
anim1.setDirectionsCount(1);
anim1.getDirection(0).addSprite(sprite1);
gd.castObject(obj.getConfiguration(), gd.SpriteObject).addAnimation(
gd.castObject(obj.getConfiguration(), gd.SpriteObject).getAnimations().addAnimation(
anim1
);
@@ -1589,7 +1589,7 @@ describe('libGD.js', function () {
const animation = new gd.Animation();
animation.setDirectionsCount(1);
animation.getDirection(0).addSprite(sprite1);
spriteConfiguration.addAnimation(animation);
spriteConfiguration.getAnimations().addAnimation(animation);
let worker = extend(new gd.ArbitraryResourceWorkerJS(project.getResourcesManager()), {
exposeImage: function (image) {
@@ -1620,7 +1620,7 @@ describe('libGD.js', function () {
animation.getDirection(0).addSprite(sprite1);
animation.getDirection(0).addSprite(sprite2);
animation.getDirection(0).addSprite(sprite1);
spriteObject.addAnimation(animation);
spriteObject.getAnimations().addAnimation(animation);
const spriteObject2 = new gd.SpriteObject();
const animation2 = new gd.Animation();
@@ -1628,7 +1628,7 @@ describe('libGD.js', function () {
animation2.getDirection(0).addSprite(sprite1);
animation2.getDirection(0).addSprite(sprite3);
animation2.getDirection(0).addSprite(sprite1);
spriteObject2.addAnimation(animation2);
spriteObject2.getAnimations().addAnimation(animation2);
const resourcesInUse = new gd.ResourcesInUseHelper();
@@ -1681,7 +1681,7 @@ describe('libGD.js', function () {
animation.getDirection(0).addSprite(sprite1);
animation.getDirection(0).addSprite(sprite2);
animation.getDirection(0).addSprite(sprite1);
spriteObject.addAnimation(animation);
spriteObject.getAnimations().addAnimation(animation);
const object2 = project.insertNewObject(
project,
@@ -1697,7 +1697,7 @@ describe('libGD.js', function () {
animation2.getDirection(0).addSprite(sprite1);
animation2.getDirection(0).addSprite(sprite3);
animation2.getDirection(0).addSprite(sprite1);
spriteObject2.addAnimation(animation2);
spriteObject2.getAnimations().addAnimation(animation2);
{
const objectsCollector = new gd.ObjectsUsingResourceCollector(project.getResourcesManager(), 'Image1');
@@ -3047,17 +3047,19 @@ describe('libGD.js', function () {
});
it('can have animations', function () {
let obj = new gd.SpriteObject();
obj.addAnimation(new gd.Animation());
obj.addAnimation(new gd.Animation());
expect(obj.getAnimationsCount()).toBe(2);
obj.removeAnimation(1);
expect(obj.getAnimationsCount()).toBe(1);
const obj = new gd.SpriteObject();
const animations = obj.getAnimations();
animations.addAnimation(new gd.Animation());
animations.addAnimation(new gd.Animation());
expect(animations.getAnimationsCount()).toBe(2);
animations.removeAnimation(1);
expect(animations.getAnimationsCount()).toBe(1);
});
it('can swap animations', function () {
let obj = new gd.SpriteObject();
obj.removeAllAnimations();
const obj = new gd.SpriteObject();
const animations = obj.getAnimations();
animations.removeAllAnimations();
let anim1 = new gd.Animation();
let anim2 = new gd.Animation();
let sprite1 = new gd.Sprite();
@@ -3071,14 +3073,14 @@ describe('libGD.js', function () {
anim1.getDirection(0).addSprite(sprite1);
anim2.getDirection(0).addSprite(sprite2);
obj.addAnimation(anim1);
obj.addAnimation(anim2);
animations.addAnimation(anim1);
animations.addAnimation(anim2);
expect(
obj.getAnimation(0).getDirection(0).getSprite(0).getImageName()
animations.getAnimation(0).getDirection(0).getSprite(0).getImageName()
).toBe('image1');
obj.swapAnimations(0, 1);
animations.swapAnimations(0, 1);
expect(
obj.getAnimation(0).getDirection(0).getSprite(0).getImageName()
animations.getAnimation(0).getDirection(0).getSprite(0).getImageName()
).toBe('image2');
});

View File

@@ -1022,7 +1022,7 @@ describe('MetadataDeclarationHelper', () => {
},
});
it('can create metadata for custom object default instructions and expressions', () => {
it('can create metadata for custom object default capabilities', () => {
const extension = new gd.PlatformExtension();
const project = new gd.Project();
@@ -1048,11 +1048,22 @@ describe('MetadataDeclarationHelper', () => {
expect(extension.getExtensionObjectsTypes().at(0)).toBe('MyObject');
const objectMetadata = extension.getObjectMetadata('MyObject');
// The capabilities replaced the deprecated instructions below.
expectArray(
objectMetadata.getDefaultBehaviors().toNewVectorString().toJSArray()
).toContainAll([
"ResizableCapability::ResizableBehavior",
"ScalableCapability::ScalableBehavior",
"FlippableCapability::FlippableBehavior",
"OpacityCapability::OpacityBehavior",
"EffectCapability::EffectBehavior",
]);
expectArray(objectMetadata.getAllActions().keys().toJSArray()).toContainAll(
[
// Private
'MyObject::SetRotationCenter',
// Public
// Deprecated
'MyObject::Width',
'Width',
'MyObject::Height',
@@ -1078,6 +1089,7 @@ describe('MetadataDeclarationHelper', () => {
expectArray(
objectMetadata.getAllConditions().keys().toJSArray()
).toContainAll([
// Deprecated
'MyObject::ScaleX',
'MyObject::ScaleY',
'MyObject::FlippedX',
@@ -1089,7 +1101,9 @@ describe('MetadataDeclarationHelper', () => {
expectArray(
objectMetadata.getAllExpressions().keys().toJSArray()
).toContainAll(['ScaleX', 'ScaleY', 'Opacity']);
).toContainAll([
// Deprecated
'ScaleX', 'ScaleY', 'Opacity']);
expectArray(
objectMetadata.getAllStrExpressions().keys().toJSArray()
@@ -1099,6 +1113,51 @@ describe('MetadataDeclarationHelper', () => {
project.delete();
});
it('can create metadata for custom object with all capabilities', () => {
const extension = new gd.PlatformExtension();
const project = new gd.Project();
const eventExtension = project.insertNewEventsFunctionsExtension(
'MyExtension',
0
);
const eventsBasedObject = eventExtension
.getEventsBasedObjects()
.insertNew('MyObject', 0);
eventsBasedObject.markAsRenderedIn3D(true);
eventsBasedObject.markAsAnimatable(true);
eventsBasedObject.markAsTextContainer(true);
const objectMethodMangledNames = new gd.MapStringString();
gd.MetadataDeclarationHelper.generateObjectMetadata(
project,
extension,
eventExtension,
eventsBasedObject,
objectMethodMangledNames
);
objectMethodMangledNames.delete();
expect(extension.getExtensionObjectsTypes().size()).toBe(1);
expect(extension.getExtensionObjectsTypes().at(0)).toBe('MyObject');
const objectMetadata = extension.getObjectMetadata('MyObject');
expectArray(
objectMetadata.getDefaultBehaviors().toNewVectorString().toJSArray()
).toContainAll([
"ResizableCapability::ResizableBehavior",
"ScalableCapability::ScalableBehavior",
"FlippableCapability::FlippableBehavior",
// No effect nor opacity capabilities for 3D objects.
"Scene3D::Base3DBehavior",
"AnimatableCapability::AnimatableBehavior",
"TextContainerCapability::TextContainerBehavior",
]);
extension.delete();
project.delete();
});
it('can create metadata for object actions', () => {
const extension = new gd.PlatformExtension();
const project = new gd.Project();

View File

@@ -8,9 +8,6 @@
"name": "GDevelop.js",
"version": "0.0.1",
"license": "MIT",
"dependencies": {
"prettier": "^2.1.2"
},
"devDependencies": {
"@types/node": "^20.3.1",
"extend": "^2.0.1",
@@ -25,6 +22,7 @@
"grunt-shell": "^2.1.0",
"grunt-string-replace": "^1.3.1",
"jest": "^29.7.0",
"prettier": "^3.2.2",
"shelljs": "^0.8.4",
"webidl-tools": "github:4ian/webidl-tools#348f9c03afc9d8f278efccdd74543e265a41fd11"
}
@@ -7545,14 +7543,18 @@
}
},
"node_modules/prettier": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz",
"integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==",
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.2.tgz",
"integrity": "sha512-HTByuKZzw7utPiDO523Tt2pLtEyK7OibUD9suEJQrPUCYQqrHr74GGX6VidMrovbf/I50mPqr8j/II6oBAuc5A==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=10.13.0"
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/pretty-bytes": {

View File

@@ -33,6 +33,7 @@
"grunt-shell": "^2.1.0",
"grunt-string-replace": "^1.3.1",
"jest": "^29.7.0",
"prettier": "^3.2.2",
"shelljs": "^0.8.4",
"webidl-tools": "github:4ian/webidl-tools#348f9c03afc9d8f278efccdd74543e265a41fd11"
},
@@ -45,8 +46,5 @@
"<rootDir>/emsdk/",
"<rootDir>/node_modules/"
]
},
"dependencies": {
"prettier": "^2.1.2"
}
}

View File

@@ -1,7 +1,7 @@
// @ts-check
import { readFileSync, writeFileSync } from 'fs';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import { readFileSync, writeFileSync } from 'node:fs';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const bindingsFile = readFileSync(
@@ -9,6 +9,69 @@ const bindingsFile = readFileSync(
'utf-8'
);
/** @type {Record<string, {returnType: string, inputType?: string}>} */
const castFunctions = {
StandardEvent: { inputType: 'Event', returnType: 'StandardEvent' },
RepeatEvent: { inputType: 'Event', returnType: 'RepeatEvent' },
WhileEvent: { inputType: 'Event', returnType: 'WhileEvent' },
ForEachEvent: { inputType: 'Event', returnType: 'ForEachEvent' },
ForEachChildVariableEvent: {
inputType: 'Event',
returnType: 'ForEachChildVariableEvent',
},
CommentEvent: { inputType: 'Event', returnType: 'CommentEvent' },
GroupEvent: { inputType: 'Event', returnType: 'GroupEvent' },
LinkEvent: { inputType: 'Event', returnType: 'LinkEvent' },
JsCodeEvent: { inputType: 'Event', returnType: 'JsCodeEvent' },
Platform: { inputType: 'JsPlatform', returnType: 'Platform' },
SpriteConfiguration: {
inputType: 'ObjectConfiguration',
returnType: 'SpriteObject',
},
TiledSpriteConfiguration: {
inputType: 'ObjectConfiguration',
returnType: 'TiledSpriteObject',
},
PanelSpriteConfiguration: {
inputType: 'ObjectConfiguration',
returnType: 'PanelSpriteObject',
},
TextObjectConfiguration: {
inputType: 'ObjectConfiguration',
returnType: 'TextObject',
},
ShapePainterConfiguration: {
inputType: 'ObjectConfiguration',
returnType: 'ShapePainterObject',
},
TextEntryObject: {
inputType: 'ObjectConfiguration',
returnType: 'TextEntryObject',
},
ParticleEmitterConfiguration: {
inputType: 'ObjectConfiguration',
returnType: 'ParticleEmitterObject',
},
CustomObjectConfiguration: {
inputType: 'ObjectConfiguration',
returnType: 'CustomObjectConfiguration',
},
Model3DConfiguration: {
inputType: 'ObjectConfiguration',
returnType: 'Model3DObjectConfiguration',
},
SpineConfiguration: {
inputType: 'ObjectConfiguration',
returnType: 'SpineObjectConfiguration',
},
ObjectJsImplementation: { returnType: 'ObjectJsImplementation' },
ImageResource: { inputType: 'Resource', returnType: 'ImageResource' },
};
const PrimitiveTypes = new Map([
['DOMString', 'string'],
['long', 'number'],
@@ -69,6 +132,8 @@ class Parser {
static readUntil(thisCharacter, skipOverIt = true) {
let token = '';
while (this.currentCharacter !== thisCharacter) {
if (this.isDone)
throw new Error(`Never reached character '${thisCharacter}'!`);
token += this.currentCharacter;
this.parserPosition++;
}
@@ -83,17 +148,23 @@ class Parser {
Parser.skipWhitespaces();
// Read the type
let type = Parser.readUntil(' ');
/** @type {string} */
let type;
let optional = false;
if (type === 'optional') optional = true;
while (type === 'unsigned' || type === 'optional') {
// Re-read the type since unsigned is an unnecessary prefix for typescript
let attribute = false;
do {
Parser.skipWhitespaces();
type = Parser.readUntil(' ');
}
if (type === 'optional') optional = true;
if (type === 'attribute') attribute = true;
} while (
type === 'unsigned' ||
type === 'optional' ||
type === 'attribute'
);
Parser.skipWhitespaces();
return { type, optional };
return { type, optional, attribute };
}
static readIdentifier() {
@@ -137,29 +208,58 @@ for (const [_, enumName, enumCode] of bindingsFile.matchAll(
members.push(` ${memberName} = ${i++},`);
}
enums.push(
`enum ${enumName} {
`export enum ${enumName} {
${members.join('\n')}
}`
);
}
const interfaces = [];
for (const [_, interfaceName, interfaceCode] of bindingsFile.matchAll(
/interface\s+([a-zA-Z]+)\s+{\r?\n?([^}]*)\r?\n}/gm
const freeFunctions = [];
for (const [
_,
implementationName,
interfaceName,
interfaceCode,
] of bindingsFile.matchAll(
/(?:\[JSImplementation=([a-zA-Z0-9]+)\]\r?\n?)?interface\s+([a-zA-Z0-9]+)\s+{(?:}|(?:\r?\n?([^}]*)\r?\n}))/gm
)) {
if (!interfaceCode) {
interfaces.push(
`export class ${interfaceName} extends EmscriptenObject {}`
);
continue;
}
const methods = [];
const attributes = [];
Parser.setSource(interfaceCode);
while (!Parser.isDone) {
const { type: returnType, optional: optionalReturn } = Parser.readType();
const {
type: returnType,
optional: optionalReturn,
attribute: isAttribute,
} = Parser.readType();
if (isAttribute) {
const attributeName = Parser.readUntil(';');
attributes.push(
`${attributeName}${optionalReturn ? '?' : ''}: ${returnType};`
);
continue;
}
let methodName = Parser.readUntil('(');
const isStatic = methodName.includes('STATIC_');
const isFree = methodName.includes('FREE_');
const isConstructor = returnType === 'void' && methodName === interfaceName;
// Remove prefixes which are not part of the actual function name
methodName = methodName
.replace('WRAPPED_', '')
.replace('MAP_', '')
.replace('FREE_', '')
.replace('CLONE_', '')
.replace('STATIC_', '');
// Convert PascalCase to camelCase
methodName = methodName[0].toLowerCase() + methodName.slice(1);
@@ -195,34 +295,50 @@ for (const [_, interfaceName, interfaceCode] of bindingsFile.matchAll(
Parser.parserPosition++;
Parser.skipWhitespaces();
methods.push(
`${isStatic ? 'static ' : ''}${
isConstructor ? `constructor` : methodName
}(${parameters
.map(
({ name, type, optional, defaultValue }) =>
`${name}${optional ? '?' : ''}: ${
PrimitiveTypes.has(type) ? PrimitiveTypes.get(type) : type
}${defaultValue !== none ? ` = ${defaultValue}` : ''}`
)
.join(', ')}): ${
PrimitiveTypes.has(returnType)
? PrimitiveTypes.get(returnType)
: returnType
};`
);
const method = `${isStatic ? 'static ' : ''}${
isConstructor ? `constructor` : methodName
}(${parameters
.map(
({ name, type, optional, defaultValue }) =>
`${name}${optional ? '?' : ''}: ${
PrimitiveTypes.has(type) ? PrimitiveTypes.get(type) : type
}`
)
.join(', ')})${
isConstructor
? ''
: `: ${
PrimitiveTypes.has(returnType)
? PrimitiveTypes.get(returnType)
: returnType
}`
};`;
if (isFree) freeFunctions.push(`export function ${method}`);
methods.push(method);
}
const explicitlyInheritedClass = bindingsFile.match(
new RegExp(`(?<![a-zA-Z0-9])${interfaceName} implements ([a-zA-Z0-9]+)`)
);
const inheritedClass =
implementationName ||
(!!explicitlyInheritedClass && explicitlyInheritedClass[1]);
interfaces.push(
`export class ${interfaceName} extends EmscriptenObject {
${methods.join('\n ')}
`export class ${interfaceName} extends ${
inheritedClass ? inheritedClass : 'EmscriptenObject'
} {${methods.length ? '\n ' + methods.join('\n ') : ''}${
attributes.length ? '\n ' + attributes.join('\n ') : ''
}
}`
);
}
const dts = `// Automatically generated by GDevelop.js/scripts/generate-dts.js
type float = number;
class EmscriptenObject {
declare class EmscriptenObject {
/** The object's index in the WASM memory, and thus its unique identifier. */
ptr: number;
@@ -237,14 +353,91 @@ class EmscriptenObject {
* If the object is owned by your code, you should still call this method when adequate, as
* otherwise the memory will never be freed, causing a memory leak, which is to be avoided.
*/
destroy(): void;
delete(): void;
}
${enums.join('\n\n')}
${interfaces.join('\n\n')}
${freeFunctions.join('\n\n')}
${Object.entries(castFunctions)
.map(
([interfaceName, { returnType, inputType = 'EmscriptenObject' }]) => `
export function as${interfaceName}(object: ${inputType}): ${returnType};
`
)
.join('')}
export const Object: typeof gdObject;
/**
* Initialises the Platforms included in the build (currently, only the JsPlatform),
* and loads all built-in extensions into the platform.
* To be called once when the library is first loaded.
*/
export const initializePlatforms: typeof ProjectHelper.initializePlatforms;
/**
* Returns the pointer in WASM memory to an object. It is a number that uniquely
* represents that instance of the object.
*
* @see {@link wrapPointer} to convert a pointer back to an object.
*/
export function getPointer(object: EmscriptenObject): number;
type ClassConstructor<T> = {
new (...args: any[]): T;
};
/**
* Wraps a pointer with a wrapper class, allowing to use the object located at the
* pointer's destination as an instance of that class.
*
* @see {@link getPointer} to get a pointer from an object.
*/
export function wrapPointer<T extends EmscriptenObject>(ptr: number, objectClass: ClassConstructor<T>): T;
/**
* Casts an object to another class type.
*
* **Careful** - this is not a conversion function.
* This only changes the class type and functions exposed, not the underlying memory.
* Only cast to another class if you are certain that the underlying memory is of that type!
*/
export function castObject<T extends EmscriptenObject>(object: EmscriptenObject, objectClass: ClassConstructor<T>): T;
/**
* Checks whether two objects are pointing to the same underlying memory.
* A reference to the object itself is not trustworthy, since there may be multiple
* wrapper objects (which allow to call C++ function on C++ memory) for a single
* pointer ("real object").
*
* This function must be therefore used to check for referential equality instead of
* JavaScript's standard equality operators when handling Emscripten objects.
*/
export function compare<T extends EmscriptenObject>(object1: T, object2: T): boolean;
/**
* Call this to free the object's underlying memory. It may not be used afterwards.
*
* **Call with care** - if the object owns some other objects, those will also be destroyed,
* or if this object is owned by another object that does not expect it to be externally deleted
* (e.g. it is a child of a map), objects will be put in an invalid state that will most likely
* crash the app.
*
* If the object is owned by your code, you should still call this method when adequate, as
* otherwise the memory will never be freed, causing a memory leak, which is to be avoided.
*
* The alias {@link EmscriptenObject.delete} is recommended instead, for readability.
*/
export function destroy(object: EmscriptenObject): void;
export as namespace gd;
declare global {
const gd: typeof gd;
}
`;
writeFileSync(__dirname + '/../types.d.ts', dts);

595
GDevelop.js/types.d.ts vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdAbstractFunctionMetadata {
addParameter(type: string, description: string, optionalObjectType: string, parameterIsOptional: boolean): gdAbstractFunctionMetadata;
addParameter(type: string, description: string, optionalObjectType?: string, parameterIsOptional?: boolean): gdAbstractFunctionMetadata;
addCodeOnlyParameter(type: string, supplementaryInformation: string): gdAbstractFunctionMetadata;
setDefaultValue(defaultValue: string): gdAbstractFunctionMetadata;
setParameterLongDescription(longDescription: string): gdAbstractFunctionMetadata;

View File

@@ -6,6 +6,7 @@ declare class gdCustomObjectConfiguration extends gdObjectConfiguration {
updateProperty(name: string, value: string): boolean;
getInitialInstanceProperties(instance: gdInitialInstance, project: gdProject, scene: gdLayout): gdMapStringPropertyDescriptor;
updateInitialInstanceProperty(instance: gdInitialInstance, name: string, value: string, project: gdProject, scene: gdLayout): boolean;
getAnimations(): gdSpriteAnimationList;
delete(): void;
ptr: number;
};

View File

@@ -11,6 +11,10 @@ declare class gdEventsBasedObject extends gdAbstractEventsBasedEntity {
getDefaultName(): string;
markAsRenderedIn3D(isRenderedIn3D: boolean): gdEventsBasedObject;
isRenderedIn3D(): boolean;
markAsAnimatable(isAnimatable: boolean): gdEventsBasedObject;
isAnimatable(): boolean;
markAsTextContainer(isTextContainer: boolean): gdEventsBasedObject;
isTextContainer(): boolean;
static getPropertyActionName(propertyName: string): string;
static getPropertyConditionName(propertyName: string): string;
static getPropertyExpressionName(propertyName: string): string;

View File

@@ -19,6 +19,8 @@ declare class gdInitialInstance {
setLocked(lock: boolean): void;
isSealed(): boolean;
setSealed(seal: boolean): void;
shouldKeepRatio(): boolean;
setShouldKeepRatio(keepRatio: boolean): void;
getZOrder(): number;
setZOrder(zOrder: number): void;
getLayer(): string;

View File

@@ -0,0 +1,16 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdSpriteAnimationList {
constructor(): void;
addAnimation(animation: gdAnimation): void;
getAnimation(index: number): gdAnimation;
getAnimationsCount(): number;
removeAnimation(index: number): void;
removeAllAnimations(): void;
hasNoAnimations(): boolean;
swapAnimations(first: number, second: number): void;
moveAnimation(oldIndex: number, newIndex: number): void;
adaptCollisionMaskAutomatically(): boolean;
setAdaptCollisionMaskAutomatically(adaptCollisionMaskAutomatically: boolean): void;
delete(): void;
ptr: number;
};

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