Compare commits

...

64 Commits

Author SHA1 Message Date
Clément Pasteau
0ffc88a470 Small fixes (#4116)
Do not show in changelog
2022-07-18 18:25:27 +02:00
Clément Pasteau
b389b3cc90 Fix onboarding flow for web (#4115) 2022-07-18 16:22:50 +02:00
Clément Pasteau
41a70ec1e7 Bump IDE version to 139 (#4113) 2022-07-18 12:26:07 +02:00
D8H
1844d826f9 [PathFinding] Add a warn for too long searches (#4112)
* Don't show in changelogs
2022-07-18 12:25:54 +02:00
github-actions[bot]
e7e45e9c9b Update translations [skip ci] (#4102)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2022-07-18 12:14:34 +02:00
Clément Pasteau
7f7dc0e07f Final fixes release 139 (#4111)
* Fix MultiAutoComplete resetting after each input
* Always open a scene when opening a project
2022-07-18 12:13:43 +02:00
D8H
7af0153ceb [Platformer] Fix: platformer characters can now jump when moving against a wall that is part of the same object as the floor. (#4106)
* This bug could happen when using a tilemap collision mask object and not sustaining the jump.
2022-07-18 12:07:15 +02:00
D8H
9aaabbcc1d Fix the box of collision masks in the editor (#4105)
* Don't show in changelogs
2022-07-18 10:14:56 +02:00
Florian Rival
55cc75e990 Update wording of games not visible on Liluo on the games dashboard
Don't show in changelog
2022-07-13 18:47:19 +02:00
AlexandreS
d3d6c50790 Fix custom tooltip crashing when payload is undefined (#4101)
Don't show in changelog
2022-07-13 18:04:05 +02:00
github-actions[bot]
c7f2e4312c Update translations [skip ci] (#4058)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2022-07-13 15:49:36 +02:00
Peter Anderson
38cff7bbce Fix typos: replace 'Musics' with 'Music' (#4099) 2022-07-13 15:36:27 +02:00
AlexandreS
761de213ac Fix tabs and links colors
Do not show in changelog
2022-07-13 15:14:05 +02:00
D8H
f871b64011 [TileMap] Collision mask object (#3313)
* The collision mask is read from the tile maps exported by Tiled 1.9+.
* Several collision masks can be used for instance to have platforms and ladders.
* Take a look to the wiki for more details https://wiki.gdevelop.io/gdevelop5/objects/tilemap
2022-07-13 15:12:12 +02:00
AlexandreS
9441d3a2d2 Adapt themes' colors for better contrast across the app 2022-07-13 11:31:36 +02:00
Florian Rival
e86348727e Add a button, in the Profile, to manage online the subscription to GDevelop services (#4096)
This allows to easily update a payment method or switch to another plan/cancel (though this can already be done from the interface)
2022-07-13 10:59:41 +02:00
Peter Anderson
754845e5cd Fix grammar for the description of the Platform behavior (#4098) 2022-07-13 10:49:50 +02:00
Fannie Yan
1b86c23b92 Replace leaderboards when a user opens a game template (#4094)
* Replace leaderboards when a user opens a game template
2022-07-12 15:33:55 +02:00
AlexandreS
3c44602486 Fix Physics2 behaviors (#4074)
* Make it possible to activate and reactivate physics 2 behaviour
* Fix behaviour for objects modified or destroyed during events life cycle
* Make Physics2 Collision condition valid in any step of behaviour life cycles
2022-07-12 14:46:49 +02:00
D8H
b0f1e962a4 Automatically select and rename new behaviors or functions in the extension editor (#4092) 2022-07-11 16:27:22 +02:00
D8H
c48c86a51f Make radian or degree explicit in descriptions for angles (#4091) 2022-07-11 14:47:21 +02:00
Florian Rival
2b83ec9871 Revert color changes in themes (#4089)
* Primary color is the one that is displaying well, with good contrast, on the background.
* Secondary is the "accent" one that works well for raised button, selected buttons, etc...

Don't show in changelog
2022-07-11 10:36:15 +02:00
Florian Rival
c340f8ad06 Fix display of games showcase
Don't show in changelog
2022-07-08 17:53:41 +02:00
Clément Pasteau
07341f1e00 Improve the new Homepage Learn section by categorising tutorials
* Tutorials are now organised into Full Games, Game Mechanics, and official videos from Beginner to Advanced!
2022-07-08 16:06:57 +02:00
Sebastian Krzyszkowiak
7402d4f29f Add an option in the Resources editor to preload audio files in cache without decoding (#4010)
* This is helpful to load the audio files at the loading of the game (so even if the internet connection is lost, they will be available in the "browser cache") but without decoding them in memory (which could make too much work for low end devices, resulting in crashes)
2022-07-07 12:56:32 +02:00
AlexandreS
8599a1cfa7 Allow to display up to 50 leaderboard entries in Liluo/in-game 2022-07-06 15:39:59 +02:00
Clément Pasteau
98bfc7a4e0 Revamp GDevelop Homepage
* New "Get Started" section, to guide newcomers on where to start with GDevelop
* New "Build" section, the entry point for your projects
* New "Learn" section, the entry point to tutorials and future help tools
* New "Play" section, the entry point for all games created with GDevelop
* New "Community" section, the entry point to stay informed with the GDevelop world
2022-07-06 14:00:46 +02:00
D8H
cef352276d Add a duplicate action in the functions lists contextual menu. (#4085) 2022-07-06 12:33:52 +02:00
Fannie Yan
49ea27fd54 Slightly rework games dashboard (#4084)
Don't show in changelog
2022-07-06 12:12:24 +02:00
D8H
23b5b16bdf Select copied element on behaviors and functions lists (#4083) 2022-07-06 11:06:12 +02:00
Arthur Pacaud
2124133b4a Add an option to wait for a network request action to end before running other actions (#4023)
* In the future, there will be other actions where you can optionally wait for their tasks to be finished before running the rest of the actions. It makes logic easier and more straightforward to express in events.
* This is similar to the Wait action, except that it's optional.
2022-07-05 23:58:08 +02:00
Florian Rival
905344f396 Fix contrast of expressions in Rosé Pine theme 2022-07-05 15:08:43 +02:00
Florian Rival
84b78dfe0f Order extensions according to what is specified in the extensions repository (#4080)
Don't show in changelog
2022-07-03 22:47:18 +02:00
Ehan
1d8086b2f4 Add a new "Rosé Pine" theme (Code Editor & UI Theme) (#4079)
* Based on https://rosepinetheme.com/ and adapted by @EhanAhamed for GDevelop.
2022-07-03 18:20:19 +02:00
Florian Rival
22d5d46601 Add a toggle to display community extensions (#4078)
* Community extensions are extensions made by a community member, but which are not reviewed by the GDevelop extension team.
  They can be useful, but also need to be carefully reviewed if you use some (inspect them or reach out to their author in case of doubt).
* If you want to submit an extension, see the [GitHub repository of community extensions](https://github.com/gdevelopapp/gdevelop-extensions).
2022-07-02 13:36:37 +02:00
Fannie Yan
a5d56c37c9 Allow user to manage builds (#4075)
* In build tab:
  - Allow users to edit build names
  - Allow users to delete builds
* In feedback tab:
  - Display build name if a feedback is linked to one and allow users to filter feedbacks on builds
2022-07-01 10:44:29 +02:00
Florian Rival
5025ad9fb9 Avoid bad expression warnings in the console when using force actions without changing the force type (#4073) 2022-06-30 19:20:32 +02:00
D8H
daae78e802 Add version locks for rechart. (#4076)
Don't show in changelogs.
2022-06-30 16:28:02 +02:00
Florian Rival
9ee4b704da Clean more deprecated include files in extensions (#4072)
Don't show in changelog
2022-06-30 12:09:17 +02:00
D8H
a8991b7c9b Add game analytics charts (#4046)
The game dash board shows new charts to understand how well a game is doing on several aspects:
* the popularity (number of players)
* the player engagement (how long the game is played)

Note that players data are anonymized.
2022-06-30 12:06:03 +02:00
Florian Rival
cf061638b8 Avoid warnings in the console during preview/export by cleaning old C++ include files (#4070)
Only show in developer changelog
2022-06-28 19:26:25 +02:00
Arthur Pacaud
0686e02e27 Don't show objects already in a group in the selector to add a new object to this group (#4069) 2022-06-28 10:52:46 +02:00
Arthur Pacaud
e65b576e4b Adding actions to tween the value of a scene variable or a layer camera position (#4022)
* Like tweens added on objects, these tweens can be manipulated: paused, resumed or stopped.
2022-06-27 23:56:16 +02:00
Florian Rival
aacfed02a1 Allow the web-app to use relative resources if found when the project is opened from a public URL (#4066)
* For example, this can be useful to open a desktop project stored on a public space like GitHub (using the "raw" raw.githubusercontent.com/... urls given by GitHub)

Only show in developer changelog
2022-06-27 15:59:13 +02:00
Fannie Yan
6a25a9ad77 Allow user to open their games for feedbacks (#4045)
* Rework slighlty the game dashboard to allow users to make their game discoverable on Liluo.io, add a banner asking for feedback on their game pages on Liluo.io and add a banner asking for feedback on their build pages
* Add a tab Feedback when managing games, accessible through the games dashboard that allows users to see and process the feedbacks given to their game.
2022-06-27 15:23:43 +02:00
Clément Pasteau
e1140609d0 Add a button to start (or redo) the onboarding on the homepage
* Also add new languages FR, PT & ES
2022-06-27 15:18:15 +02:00
AlexandreS
81ef11163d Bump newIDE version 2022-06-21 16:41:38 +02:00
AlexandreS
fcc19a6dcf Fix collision bug introduced when fixing a collision bug 2022-06-21 16:41:18 +02:00
github-actions[bot]
bcad2d5667 Update translations [skip ci] (#4042)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2022-06-21 16:01:54 +02:00
D8H
3c83e5d24a No longer count pauses (when the tab is hidden) for the players session duration statistics (#4054) 2022-06-21 15:04:41 +02:00
Florian Rival
d07088900f Bump newIDE version 2022-06-16 18:28:13 +02:00
github-actions[bot]
fcb0c27e23 Update translations [skip ci] (#4030) 2022-06-16 17:06:43 +02:00
Fannie Yan
a6ee1c3e1c Allow more freedom on horizontal movements in events sheet (#4037)
* Allow more freedom on horizontal movements in events sheet
2022-06-16 11:09:26 +02:00
D8H
5669eae198 [Physics] Add mass and moment of inertia expressions (#4000) 2022-06-16 10:29:28 +02:00
AlexandreS
8f97a5ba69 Fix: Detect Physics2 contacts between objects that happen between frames 2022-06-16 10:12:15 +02:00
D8H
d57a755b2f Extract the analytics panel from the game detail dialog (#4038)
* Don't show in changelogs
2022-06-15 19:33:14 +02:00
Fannie Yan
5118421de7 Suggest all expression types in Text fields (#4033)
* Suggest all expressions in autocompletion suggestion and in expression selector for Text parameter fields
* Automatically convert number expressions to Text (using ToString()) when needed
2022-06-15 15:58:06 +02:00
Arthur Pacaud
ff15a37da7 Add WebManifest to exported web games (#4021)
* This allows a game hosted on a website to have a proper icon and orientation when a shortcut to it is added to the home screen on Android or iOS
2022-06-14 15:15:35 +02:00
Florian Rival
beac19089f Upgrade gh-pages to avoid history of the published web-app to take useless space
* history option was not working previously

Don't show in changelog
2022-06-13 15:45:37 +02:00
Florian Rival
e86e4ef9f5 Update all dark themes and rework the light theme to use the modern look'n'feel (#4028) 2022-06-13 12:54:10 +02:00
github-actions[bot]
693a2dbd2c Update translations [skip ci] (#4006)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2022-06-13 12:32:11 +02:00
AlexandreS
23318c2c28 Fix: Remove clicks on checkboxes that are far from the label on the same line 2022-06-13 10:55:11 +02:00
Clément Pasteau
3e811f1b9d Fix asset thumbnail sometimes having a wrong size when navigating the Asset store 2022-06-13 10:26:46 +02:00
Florian Rival
9ea6c034c3 Fix category of "Error of last save attempt" leaderboard expression 2022-06-12 17:47:24 +02:00
482 changed files with 28158 additions and 6933 deletions

View File

@@ -468,6 +468,14 @@ gd::String EventsCodeGenerator::GenerateActionCode(
action, *this, context);
}
// Get the correct function name depending on whether it should be async or
// not.
const gd::String& functionCallName =
instrInfos.IsAsync() &&
(!instrInfos.IsOptionallyAsync() || action.IsAwaited())
? instrInfos.codeExtraInformation.asyncFunctionCallName
: instrInfos.codeExtraInformation.functionCallName;
// Be sure there is no lack of parameter.
while (action.GetParameters().size() < instrInfos.parameters.size()) {
vector<gd::Expression> parameters = action.GetParameters();
@@ -522,6 +530,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
action.GetParameters(), instrInfos.parameters, context);
actionCode += GenerateObjectAction(realObjects[i],
objInfo,
functionCallName,
arguments,
instrInfos,
context,
@@ -556,6 +565,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
GenerateBehaviorAction(realObjects[i],
action.GetParameter(1).GetPlainString(),
autoInfo,
functionCallName,
arguments,
instrInfos,
context,
@@ -567,8 +577,11 @@ gd::String EventsCodeGenerator::GenerateActionCode(
} else {
vector<gd::String> arguments = GenerateParametersCodes(
action.GetParameters(), instrInfos.parameters, context);
actionCode +=
GenerateFreeAction(arguments, instrInfos, context, optionalAsyncCallbackName);
actionCode += GenerateFreeAction(functionCallName,
arguments,
instrInfos,
context,
optionalAsyncCallbackName);
}
return actionCode;
@@ -678,7 +691,8 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
} else if (ParameterMetadata::IsObject(metadata.type)) {
// It would be possible to run a gd::ExpressionCodeGenerator if later
// objects can have nested objects, or function returning objects.
argOutput = GenerateObject(parameter.GetPlainString(), metadata.type, context);
argOutput =
GenerateObject(parameter.GetPlainString(), metadata.type, context);
} else if (metadata.type == "relationalOperator") {
auto parameterString = parameter.GetPlainString();
argOutput += parameterString == "=" ? "==" : parameterString;
@@ -716,13 +730,15 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "yesorno") {
auto parameterString = parameter.GetPlainString();
argOutput += (parameterString == "yes" || parameterString == "oui") ? GenerateTrue()
: GenerateFalse();
argOutput += (parameterString == "yes" || parameterString == "oui")
? GenerateTrue()
: GenerateFalse();
} else if (metadata.type == "trueorfalse") {
auto parameterString = parameter.GetPlainString();
// This is duplicated in AdvancedExtension.cpp for GDJS
argOutput += (parameterString == "True" || parameterString == "Vrai") ? GenerateTrue()
: GenerateFalse();
argOutput += (parameterString == "True" || parameterString == "Vrai")
? GenerateTrue()
: GenerateFalse();
}
// Code only parameter type
else if (metadata.type == "inlineCode") {
@@ -1082,6 +1098,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
}
gd::String EventsCodeGenerator::GenerateFreeAction(
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -1095,21 +1112,21 @@ gd::String EventsCodeGenerator::GenerateFreeAction(
call = GenerateOperatorCall(
instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName,
functionCallName,
instrInfos.codeExtraInformation.optionalAssociatedInstruction);
else if (instrInfos.codeExtraInformation.accessType ==
gd::InstructionMetadata::ExtraInformation::Mutators)
call =
GenerateMutatorCall(instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName);
functionCallName);
else
call = GenerateCompoundOperatorCall(
instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName);
functionCallName);
} else {
call = instrInfos.codeExtraInformation.functionCallName + "(" +
call = functionCallName + "(" +
GenerateArgumentsList(arguments) + ")";
}
@@ -1123,6 +1140,7 @@ gd::String EventsCodeGenerator::GenerateFreeAction(
gd::String EventsCodeGenerator::GenerateObjectAction(
const gd::String& objectName,
const gd::ObjectMetadata& objInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -1136,27 +1154,25 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
call = GenerateOperatorCall(
instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName,
functionCallName,
instrInfos.codeExtraInformation.optionalAssociatedInstruction,
2);
else
call = GenerateCompoundOperatorCall(
instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName,
2);
instrInfos, arguments, functionCallName, 2);
return "For each picked object \"" + objectName + "\", call " + call +
".\n";
} else {
gd::String argumentsStr = GenerateArgumentsList(arguments, 1);
call = instrInfos.codeExtraInformation.functionCallName + "(" +
argumentsStr + ")";
call = functionCallName + "(" + argumentsStr + ")";
return "For each picked object \"" + objectName + "\", call " + call + "(" +
argumentsStr + ")" +
(optionalAsyncCallbackName.empty() ? "" : (", then call" + optionalAsyncCallbackName)) +
(optionalAsyncCallbackName.empty()
? ""
: (", then call" + optionalAsyncCallbackName)) +
".\n";
}
}
@@ -1165,6 +1181,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
const gd::String& objectName,
const gd::String& behaviorName,
const gd::BehaviorMetadata& autoInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -1178,26 +1195,28 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
call = GenerateOperatorCall(
instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName,
functionCallName,
instrInfos.codeExtraInformation.optionalAssociatedInstruction,
2);
else
call = GenerateCompoundOperatorCall(
instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName,
functionCallName,
2);
return "For each picked object \"" + objectName + "\", call " + call +
" for behavior \"" + behaviorName + "\".\n";
} else {
gd::String argumentsStr = GenerateArgumentsList(arguments, 2);
call = instrInfos.codeExtraInformation.functionCallName + "(" +
call = functionCallName + "(" +
argumentsStr + ")";
return "For each picked object \"" + objectName + "\", call " + call + "(" +
argumentsStr + ")" + " for behavior \"" + behaviorName + "\"" +
(optionalAsyncCallbackName.empty() ? "" : (", then call" + optionalAsyncCallbackName)) +
(optionalAsyncCallbackName.empty()
? ""
: (", then call" + optionalAsyncCallbackName)) +
".\n";
}
}

View File

@@ -154,9 +154,10 @@ class GD_CORE_API EventsCodeGenerator {
* \param context Context used for generation
* \return Code
*/
gd::String GenerateActionCode(gd::Instruction& action,
EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
gd::String GenerateActionCode(
gd::Instruction& action,
EventsCodeGenerationContext& context,
const gd::String& optionalAsyncCallbackName = "");
struct CallbackDescriptor {
CallbackDescriptor(const gd::String functionName_,
@@ -174,7 +175,8 @@ class GD_CORE_API EventsCodeGenerator {
*/
const gd::String argumentsList;
/**
* A set of all objects that need to be backed up to be passed to the callback code.
* A set of all objects that need to be backed up to be passed to the
* callback code.
*/
const std::set<gd::String> requiredObjects;
};
@@ -507,10 +509,10 @@ class GD_CORE_API EventsCodeGenerator {
* - currentScene: Reference to the current runtime scene.
* - objectList : a map containing lists of objects which are specified by the
object name in another parameter.
* - objectListOrEmptyIfJustDeclared : Same as `objectList` but do not pick object if
they are not already picked.
* - objectPtr: Return a reference to the object specified by the object name in
another parameter. Example:
* - objectListOrEmptyIfJustDeclared : Same as `objectList` but do not pick
object if they are not already picked.
* - objectPtr: Return a reference to the object specified by the object name
in another parameter. Example:
* \code
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Target object"))
@@ -700,6 +702,7 @@ class GD_CORE_API EventsCodeGenerator {
gd::EventsCodeGenerationContext& context);
virtual gd::String GenerateFreeAction(
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -708,6 +711,7 @@ class GD_CORE_API EventsCodeGenerator {
virtual gd::String GenerateObjectAction(
const gd::String& objectName,
const gd::ObjectMetadata& objInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -717,6 +721,7 @@ class GD_CORE_API EventsCodeGenerator {
const gd::String& objectName,
const gd::String& behaviorName,
const gd::BehaviorMetadata& autoInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -775,8 +780,8 @@ class GD_CORE_API EventsCodeGenerator {
bool hasProjectAndLayout; ///< true only if project and layout are valid
///< references. If false, they should not be used.
const gd::Project* project; ///< The project being used.
const gd::Layout* scene; ///< The scene being generated.
const gd::Project* project; ///< The project being used.
const gd::Layout* scene; ///< The scene being generated.
bool errorOccurred; ///< Must be set to true if an error occured.
bool compilationForRuntime; ///< Is set to true if the code generation is

View File

@@ -75,7 +75,8 @@ void BaseEvent::PreprocessAsyncActions(const gd::Platform& platform) {
const auto& action = actionsList->at(aId);
const gd::InstructionMetadata& actionMetadata =
gd::MetadataProvider::GetActionMetadata(platform, action.GetType());
if (actionMetadata.IsAsync()) {
if (actionMetadata.IsAsync() &&
(!actionMetadata.IsOptionallyAsync() || action.IsAwaited())) {
gd::InstructionsList remainingActions;
remainingActions.InsertInstructions(
*actionsList, aId + 1, actionsList->size() - 1);

View File

@@ -72,6 +72,22 @@ class GD_CORE_API Instruction {
*/
void SetInverted(bool inverted_) { inverted = inverted_; }
/**
* \brief Return true if the async instruction should be awaited.
* This is not relevant if the instruction is not optionally asynchronous.
*
* \return true if the instruction is to be awaited
*/
bool IsAwaited() const { return awaitAsync; }
/**
* \brief Set if the async instruction is to be awaited or not.
* This is not relevant if the instruction is not optionally asynchronous.
*
* \param inverted true if the instruction must be awaited
*/
void SetAwaited(bool awaited) { awaitAsync = awaited; }
/**
* \brief Return the number of parameters of the instruction.
*/
@@ -139,7 +155,9 @@ class GD_CORE_API Instruction {
* Useful to get reference to the original instruction in memory during code
* generation, to ensure stable unique identifiers.
*/
std::weak_ptr<Instruction> GetOriginalInstruction() { return originalInstruction; };
std::weak_ptr<Instruction> GetOriginalInstruction() {
return originalInstruction;
};
friend std::shared_ptr<Instruction> CloneRememberingOriginalElement(
std::shared_ptr<Instruction> instruction);
@@ -148,6 +166,9 @@ class GD_CORE_API Instruction {
gd::String type; ///< Instruction type
bool inverted; ///< True if the instruction if inverted. Only applicable for
///< instruction used as conditions by events
bool awaitAsync =
false; ///< Tells the code generator whether the optionally asynchronous
///< instruction should be generated as asynchronous (awaited) or not.
mutable std::vector<gd::Expression>
parameters; ///< Vector containing the parameters
gd::InstructionsList subInstructions; ///< Sub instructions, if applicable.

View File

@@ -269,6 +269,9 @@ void gd::EventsListSerialization::UnserializeInstructionsFrom(
instrElement.GetChild("type", 0, "Type")
.GetBoolAttribute("inverted", false, "Contraire"));
instruction.SetAwaited(
instrElement.GetChild("type", 0, "Type").GetBoolAttribute("await"));
// Read parameters
vector<gd::Expression> parameters;
@@ -346,6 +349,8 @@ void gd::EventsListSerialization::SerializeInstructionsTo(
if (list[k].IsInverted())
instruction.GetChild("type").SetAttribute("inverted", true);
if (list[k].IsAwaited())
instruction.GetChild("type").SetAttribute("await", true);
// Parameters
SerializerElement& parameters = instruction.AddChild("parameters");

View File

@@ -14,14 +14,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(
extension
.SetExtensionInformation(
"BuiltinAudio",
_("Sounds and musics"),
_("Sounds and music"),
_("GDevelop provides several conditions and actions to play audio "
"files. They can be either long musics or short sound effects."),
"files. They can be either long music or short sound effects."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/audio")
.SetCategory("Audio");
extension.AddInstructionOrExpressionGroupMetadata(_("Sounds and musics"))
extension.AddInstructionOrExpressionGroupMetadata(_("Sounds and music"))
.SetIcon("res/actions/music24.png");
extension

View File

@@ -219,7 +219,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddAction("SetAngle",
_("Angle"),
_("Change the angle of rotation of an object."),
_("Change the angle of rotation of an object (in degrees)."),
_("the angle"),
_("Angle"),
"res/actions/direction24.png",
@@ -289,7 +289,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Speed on X axis (in pixels per second)"))
.AddParameter("expression", _("Speed on Y axis (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"));
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0");
obj.AddAction("AddForceAL",
_("Add a force (angle)"),
@@ -305,7 +306,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("expression", _("Angle"))
.AddParameter("expression", _("Speed (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced();
obj.AddAction(
@@ -323,7 +325,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
.AddParameter("expression", _("Speed (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced();
obj.AddAction(
@@ -756,7 +759,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("object", _("Object"))
.AddParameter("objectPtr", _("Target Object"))
.AddParameter("expression", _("Speed (in pixels per second)"))
.AddParameter("forceMultiplier", _("Force multiplier"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced();
obj.AddAction(
@@ -776,7 +780,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
.AddParameter("objectPtr", _("Rotate around this object"))
.AddParameter("expression", _("Speed (in degrees per second)"))
.AddParameter("expression", _("Distance (in pixels)"))
.AddParameter("forceMultiplier", _("Force multiplier"))
.AddParameter("forceMultiplier", _("Force multiplier"), "", true)
.SetDefaultValue("0")
.MarkAsAdvanced();
obj.AddAction("MettreAutour",
@@ -993,7 +998,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddExpression("ForceAngle",
_("Angle of the sum of forces"),
_("Angle of the sum of forces"),
_("Angle of the sum of forces (in degrees)"),
_("Movement using forces"),
"res/actions/force.png")
.AddParameter("object", _("Object"));
@@ -1126,8 +1131,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddExpression("AngleToObject",
_("Angle between two objects"),
_("Compute the angle between two objects. If you need the "
"angle to an arbitrary position, use AngleToPosition."),
_("Compute the angle between two objects (in degrees). "
"If you need the angle to an arbitrary position, "
"use AngleToPosition."),
_("Angle"),
"res/actions/position.png")
.AddParameter("object", _("Object"))
@@ -1160,8 +1166,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
obj.AddExpression("AngleToPosition",
_("Angle between an object and a position"),
_("Compute the angle between the object center and a "
"\"target\" position. If you need the angle between two "
"objects, use AngleToObject."),
"\"target\" position (in degrees). If you need the angle "
"between two objects, use AngleToObject."),
_("Angle"),
"res/actions/position.png")
.AddParameter("object", _("Object"))

View File

@@ -195,7 +195,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
"number",
"CameraAngle",
_("Angle of a camera of a layer"),
_("the angle of rotation of a camera"),
_("the angle of rotation of a camera (in degrees)"),
_("the angle of camera (layer: _PARAM3_, camera: _PARAM4_)"),
"",
"res/conditions/camera24.png")

View File

@@ -96,13 +96,13 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
_("Difference between two angles"),
"",
"res/mathfunction.png")
.AddParameter("expression", _("First angle"))
.AddParameter("expression", _("Second angle"));
.AddParameter("expression", _("First angle, in degrees"))
.AddParameter("expression", _("Second angle, in degrees"));
extension
.AddExpression("AngleBetweenPositions",
_("Angle between two positions"),
_("Compute the angle between two positions."),
_("Compute the angle between two positions (in degrees)."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("First point X position"))
@@ -159,7 +159,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("acos",
_("Arccosine"),
_("Arccosine"),
_("Arccosine, return an angle (in radian). "
"`ToDeg` allows to convert it to degrees."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -175,7 +176,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("asin",
_("Arcsine"),
_("Arcsine"),
_("Arcsine, return an angle (in radian). "
"`ToDeg` allows to convert it to degrees."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -191,7 +193,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("atan",
_("Arctangent"),
_("Arctangent"),
_("Arctangent, return an angle (in radian). "
"`ToDeg` allows to convert it to degrees."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -258,7 +261,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("cos",
_("Cosine"),
_("Cosine of a number"),
_("Cosine of an angle (in radian). "
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -400,7 +404,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("sin",
_("Sine"),
_("Sine of a number"),
_("Sine of an angle (in radian). "
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));
@@ -424,7 +429,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
extension
.AddExpression("tan",
_("Tangent"),
_("Tangent of a number"),
_("Tangent of an angle (in radian). "
"If you want to use degrees, use`ToRad`: `tan(ToRad(45))`."),
"",
"res/mathfunction.png")
.AddParameter("expression", _("Expression"));

View File

@@ -21,9 +21,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/all-features/timers-and-time");
extension.AddInstructionOrExpressionGroupMetadata(
_("Timers and time")
)
extension.AddInstructionOrExpressionGroupMetadata(_("Timers and time"))
.SetIcon("res/conditions/timer24.png");
// Deprecated and replaced by CompareTimer
@@ -158,8 +156,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/timer.svg",
"res/timer.svg")
.AddParameter("expression", "Time to wait in seconds")
.SetHelpPath("/all-features/timers-and-time/wait-action")
.SetAsync();
.SetHelpPath("/all-features/timers-and-time/wait-action");
extension
.AddExpression("TimeDelta",

View File

@@ -22,7 +22,6 @@ InstructionMetadata::InstructionMetadata()
canHaveSubInstructions(false),
hidden(true),
usageComplexity(5),
isAsync(false),
isPrivate(false),
isObjectInstruction(false),
isBehaviorInstruction(false) {}
@@ -46,7 +45,6 @@ InstructionMetadata::InstructionMetadata(const gd::String& extensionNamespace_,
extensionNamespace(extensionNamespace_),
hidden(false),
usageComplexity(5),
isAsync(false),
isPrivate(false),
isObjectInstruction(false),
isBehaviorInstruction(false) {}

View File

@@ -6,10 +6,10 @@
#ifndef INSTRUCTIONMETADATA_H
#define INSTRUCTIONMETADATA_H
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <algorithm>
#include "GDCore/Events/Instruction.h"
#include "GDCore/String.h"
@@ -104,16 +104,16 @@ class GD_CORE_API InstructionMetadata {
* background, executing the instructions following it before the frame after
* it resolved.
*/
bool IsAsync() const { return isAsync; }
bool IsAsync() const {
return !codeExtraInformation.asyncFunctionCallName.empty();
}
/**
* Set that the instruction is asynchronous - it will be running in the
* background, executing the instructions following it before the frame after
* it resolved.
* Check if the instruction asynchronicity is optional. If it is, it can either
* be used synchronously or asynchronously, with one function for each.
*/
InstructionMetadata &SetAsync() {
isAsync = true;
return *this;
bool IsOptionallyAsync() const {
return IsAsync() && !codeExtraInformation.functionCallName.empty();
}
/**
@@ -319,14 +319,26 @@ class GD_CORE_API InstructionMetadata {
virtual ~ExtraInformation(){};
/**
* Set the function name which will be used when generating the code.
* \param functionName the name of the function to call
* Set the name of the function which will be called in the generated code.
* \param functionName the name of the function to call.
*/
ExtraInformation &SetFunctionName(const gd::String &functionName_) {
functionCallName = functionName_;
return *this;
}
/**
* Set the name of the function, doing asynchronous work, which will be called in
* the generated code. This function should return an asynchronous task
* (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
*
* \param functionName the name of the function doing asynchronous work to call.
*/
ExtraInformation &SetAsyncFunctionName(const gd::String &functionName_) {
asyncFunctionCallName = functionName_;
return *this;
}
/**
* Declare if the instruction being declared is somewhat manipulating in a
* standard way.
@@ -354,7 +366,7 @@ class GD_CORE_API InstructionMetadata {
* .AddParameter("object", _("Object"), "Text", false)
* .AddParameter("operator", _("Modification operator"), "string")
* .AddParameter("string", _("String"))
* .SetFunctionName("SetString").SetManipulatedType("string").SetGetter("GetString").SetIncludeFile("MyExtension/TextObject.h");
* .SetFunctionName("SetString").SetManipulatedType("string").SetGetter("GetString");
*
* DECLARE_END_OBJECT_ACTION()
* \endcode
@@ -422,6 +434,7 @@ class GD_CORE_API InstructionMetadata {
bool HasCustomCodeGenerator() const { return hasCustomCodeGenerator; }
gd::String functionCallName;
gd::String asyncFunctionCallName;
gd::String type;
AccessType accessType;
gd::String optionalAssociatedInstruction;
@@ -454,15 +467,26 @@ class GD_CORE_API InstructionMetadata {
}
/**
* \brief Set the function that should be called when generating the source
* code from events.
* \param functionName the name of the function to call
* Set the name of the function which will be called in the generated code.
* \param functionName the name of the function to call.
* \note Shortcut for `codeExtraInformation.SetFunctionName`.
*/
ExtraInformation &SetFunctionName(const gd::String &functionName) {
return codeExtraInformation.SetFunctionName(functionName);
}
/**
* Set the name of the function, doing asynchronous work, which will be called in
* the generated code. This function should return an asynchronous task
* (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
*
* \param functionName the name of the function doing asynchronous work to call.
* \note Shortcut for `codeExtraInformation.SetAsyncFunctionName`.
*/
ExtraInformation &SetAsyncFunctionName(const gd::String &functionName) {
return codeExtraInformation.SetAsyncFunctionName(functionName);
}
std::vector<ParameterMetadata> parameters;
private:
@@ -479,7 +503,6 @@ class GD_CORE_API InstructionMetadata {
int usageComplexity; ///< Evaluate the instruction from 0 (simple&easy to
///< use) to 10 (complex to understand)
bool isPrivate;
bool isAsync;
bool isObjectInstruction;
bool isBehaviorInstruction;
gd::String requiredBaseObjectCapability;

View File

@@ -0,0 +1,41 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsLeaderboardsLister.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
bool EventsLeaderboardsLister::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const gd::InstructionMetadata& instrInfo =
isCondition ? MetadataProvider::GetConditionMetadata(
project.GetCurrentPlatform(), instruction.GetType())
: MetadataProvider::GetActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
for (int i = 0; i < instruction.GetParametersCount() &&
i < instrInfo.GetParametersCount();
++i)
if (instrInfo.GetParameter(i).type == "leaderboardId") {
leaderboardIds.insert(instruction.GetParameter(i).GetPlainString());
}
return false;
}
EventsLeaderboardsLister::~EventsLeaderboardsLister() {}
} // namespace gd

View File

@@ -0,0 +1,48 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EventsLeaderboardsLister_H
#define EventsLeaderboardsLister_H
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class Project;
class EventsList;
}
namespace gd {
/**
* \brief List the leaderboard ids in the instructions.
*
* \ingroup IDE
*/
class GD_CORE_API EventsLeaderboardsLister : public ArbitraryEventsWorker {
public:
EventsLeaderboardsLister(gd::Project& project_) : project(project_){};
virtual ~EventsLeaderboardsLister();
/**
* Return the values of all leaderboardIds found in the events.
*/
const std::set<gd::String>& GetLeaderboardIds() { return leaderboardIds; }
private:
virtual bool DoVisitInstruction(gd::Instruction& instruction,
bool isCondition);
std::set<gd::String> leaderboardIds;
gd::Project& project;
};
} // namespace gd
#endif // EventsLeaderboardsLister_H

View File

@@ -0,0 +1,49 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/IDE/Events/EventsLeaderboardsRenamer.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
namespace gd {
bool EventsLeaderboardsRenamer::DoVisitInstruction(gd::Instruction& instruction,
bool isCondition) {
const gd::InstructionMetadata& instrInfo =
isCondition ? MetadataProvider::GetConditionMetadata(
project.GetCurrentPlatform(), instruction.GetType())
: MetadataProvider::GetActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
for (int i = 0; i < instruction.GetParametersCount() &&
i < instrInfo.GetParametersCount();
++i) {
const gd::ParameterMetadata parameter = instrInfo.GetParameter(i);
if (parameter.type == "leaderboardId") {
const gd::String leaderboardId =
instruction.GetParameter(i).GetPlainString();
if (leaderboardIdMap.find(leaderboardId) != leaderboardIdMap.end()) {
instruction.SetParameter(i, leaderboardIdMap[leaderboardId]);
}
}
}
return false;
}
EventsLeaderboardsRenamer::~EventsLeaderboardsRenamer() {}
} // namespace gd

View File

@@ -0,0 +1,46 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EventsLeaderboardsRenamer_H
#define EventsLeaderboardsRenamer_H
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class Project;
class EventsList;
}
namespace gd {
/**
* \brief Replace the leaderboard ids in the instructions.
*
* \ingroup IDE
*/
class GD_CORE_API EventsLeaderboardsRenamer : public ArbitraryEventsWorker {
public:
EventsLeaderboardsRenamer(
gd::Project& project_,
const std::map<gd::String, gd::String>& leaderboardIdMap_)
: project(project_), leaderboardIdMap(leaderboardIdMap_){};
virtual ~EventsLeaderboardsRenamer();
private:
virtual bool DoVisitInstruction(gd::Instruction& instruction,
bool isCondition);
std::map<gd::String, gd::String> leaderboardIdMap;
gd::Project& project;
};
} // namespace gd
#endif // EventsLeaderboardsRenamer_H

View File

@@ -184,11 +184,17 @@ std::map<gd::String, gd::PropertyDescriptor> AudioResource::GetProperties()
const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[_("Preload as sound")]
.SetDescription(_("Loads the fully decoded file into cache, so it can be played right away as Sound with no further delays."))
.SetValue(preloadAsSound ? "true" : "false")
.SetType("Boolean");
properties[_("Preload as music")]
.SetDescription(_("Prepares the file for immediate streaming as Music (does not wait for complete download)."))
.SetValue(preloadAsMusic ? "true" : "false")
.SetType("Boolean");
properties[_("Preload in cache")]
.SetDescription(_("Loads the complete file into cache, but does not decode it into memory until requested."))
.SetValue(preloadInCache ? "true" : "false")
.SetType("Boolean");
return properties;
}
@@ -199,6 +205,8 @@ bool AudioResource::UpdateProperty(const gd::String& name,
preloadAsSound = value == "1";
else if (name == _("Preload as music"))
preloadAsMusic = value == "1";
else if (name == _("Preload in cache"))
preloadInCache = value == "1";
return true;
}
@@ -569,6 +577,7 @@ void AudioResource::UnserializeFrom(const SerializerElement& element) {
SetFile(element.GetStringAttribute("file"));
SetPreloadAsMusic(element.GetBoolAttribute("preloadAsMusic"));
SetPreloadAsSound(element.GetBoolAttribute("preloadAsSound"));
SetPreloadInCache(element.GetBoolAttribute("preloadInCache"));
}
void AudioResource::SerializeTo(SerializerElement& element) const {
@@ -576,6 +585,7 @@ void AudioResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("file", GetFile());
element.SetAttribute("preloadAsMusic", PreloadAsMusic());
element.SetAttribute("preloadAsSound", PreloadAsSound());
element.SetAttribute("preloadInCache", PreloadInCache());
}
void FontResource::SetFile(const gd::String& newFile) {

View File

@@ -223,7 +223,7 @@ class GD_CORE_API ImageResource : public Resource {
*/
class GD_CORE_API AudioResource : public Resource {
public:
AudioResource() : Resource(), preloadAsMusic(false), preloadAsSound(false) {
AudioResource() : Resource(), preloadAsMusic(false), preloadAsSound(false), preloadInCache(false) {
SetKind("audio");
};
virtual ~AudioResource(){};
@@ -263,10 +263,21 @@ class GD_CORE_API AudioResource : public Resource {
*/
void SetPreloadAsSound(bool enable = true) { preloadAsSound = enable; }
/**
* \brief Return true if the audio resource should be preloaded in cache (without decoding into memory).
*/
bool PreloadInCache() const { return preloadInCache; }
/**
* \brief Set if the audio resource should be preloaded in cache (without decoding into memory).
*/
void SetPreloadInCache(bool enable = true) { preloadInCache = enable; }
private:
gd::String file;
bool preloadAsSound;
bool preloadAsMusic;
bool preloadInCache;
};
/**

View File

@@ -1,5 +1,5 @@
// @ts-check
describe.only('gdjs.AnchorRuntimeBehavior', function () {
describe('gdjs.AnchorRuntimeBehavior', function () {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: { resources: [] },

View File

@@ -34,7 +34,6 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
std::make_shared<DestroyOutsideBehavior>(),
std::shared_ptr<gd::BehaviorsSharedData>());
#if defined(GD_IDE_ONLY)
aut.AddCondition("ExtraBorder",
_("Additional border"),
_("Compare the additional border that the object must cross "
@@ -47,8 +46,7 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "DestroyOutside")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetExtraBorder")
.SetIncludeFile("DestroyOutsideBehavior/DestroyOutsideRuntimeBehavior.h");
.SetFunctionName("GetExtraBorder");
aut.AddAction("ExtraBorder",
_("Additional border"),
@@ -63,7 +61,5 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
.UseStandardOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("SetExtraBorder")
.SetGetter("GetExtraBorder")
.SetIncludeFile("DestroyOutsideBehavior/DestroyOutsideRuntimeBehavior.h");
#endif
.SetGetter("GetExtraBorder");
}

View File

@@ -24,7 +24,6 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddInstructionOrExpressionGroupMetadata(_("Inventories"))
.SetIcon("CppPlatform/Extensions/Inventoryicon.png");
#if defined(GD_IDE_ONLY)
extension
.AddAction("Add",
_("Add an item"),
@@ -37,8 +36,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.SetFunctionName("InventoryTools::Add")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::Add");
extension
.AddAction("Remove",
@@ -52,8 +50,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.SetFunctionName("InventoryTools::Remove")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::Remove");
extension
.AddCondition("Count",
@@ -68,8 +65,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("InventoryTools::Count")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::Count");
extension
.AddCondition("Has",
@@ -84,8 +80,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.SetFunctionName("InventoryTools::Has")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::Has");
extension
.AddAction("SetMaximum",
@@ -103,8 +98,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.AddParameter("expression", _("Maximum count"))
.SetFunctionName("InventoryTools::SetMaximum")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::SetMaximum");
extension
.AddAction("SetUnlimited",
@@ -121,8 +115,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.AddParameter("yesorno", _("Allow an unlimited amount?"))
.SetFunctionName("InventoryTools::SetUnlimited")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::SetUnlimited");
extension
.AddCondition("IsFull",
@@ -137,8 +130,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.SetFunctionName("InventoryTools::Has")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::Has");
extension
.AddAction("Equip",
@@ -154,8 +146,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.AddParameter("yesorno", _("Equip?"))
.SetFunctionName("InventoryTools::Equip")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::Equip");
extension
.AddCondition("IsEquipped",
@@ -169,8 +160,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.SetFunctionName("InventoryTools::IsEquipped")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::IsEquipped");
extension
.AddAction("SerializeToVariable",
@@ -185,8 +175,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Inventory name"))
.AddParameter("scenevar", _("Scene variable"))
.SetFunctionName("InventoryTools::SerializeToVariable")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::SerializeToVariable");
extension
.AddAction("UnserializeFromVariable",
@@ -200,8 +189,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Inventory name"))
.AddParameter("scenevar", _("Scene variable"))
.SetFunctionName("InventoryTools::UnserializeFromVariable")
.SetIncludeFile("Inventory/InventoryTools.h");
.SetFunctionName("InventoryTools::UnserializeFromVariable");
extension
.AddExpression("Count",
@@ -212,7 +200,5 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Inventory name"))
.AddParameter("string", _("Item name"))
.SetFunctionName("InventoryTools::Count")
.SetIncludeFile("Inventory/InventoryTools.h");
#endif
.SetFunctionName("InventoryTools::Count");
}

View File

@@ -133,7 +133,7 @@ module.exports = {
'LastSaveError',
_('Error of last save attempt'),
_('Get the error of the last save attempt.'),
_('Error of last save attempt in leaderboard _PARAM0_'),
_('Save score'),
'JsPlatform/Extensions/leaderboard.svg'
)
.addParameter('leaderboardId', _('Leaderboard'), '', true)

View File

@@ -28,8 +28,6 @@ void DeclareLinkedObjectsExtension(gd::PlatformExtension& extension) {
extension.AddInstructionOrExpressionGroupMetadata(_("Linked objects"))
.SetIcon("CppPlatform/Extensions/LinkedObjectsicon24.png");
#if defined(GD_IDE_ONLY)
extension
.AddAction("LinkObjects",
_("Link two objects"),
@@ -44,8 +42,7 @@ void DeclareLinkedObjectsExtension(gd::PlatformExtension& extension) {
.AddParameter("objectPtr", _("Object 1"))
.AddParameter("objectPtr", _("Object 2"))
.SetFunctionName("GDpriv::LinkedObjects::LinkObjects")
.SetIncludeFile("LinkedObjects/LinkedObjectsTools.h");
.SetFunctionName("GDpriv::LinkedObjects::LinkObjects");
extension
.AddAction("RemoveLinkBetween",
@@ -60,8 +57,7 @@ void DeclareLinkedObjectsExtension(gd::PlatformExtension& extension) {
.AddParameter("objectPtr", _("Object 1"))
.AddParameter("objectPtr", _("Object 2"))
.SetFunctionName("GDpriv::LinkedObjects::RemoveLinkBetween")
.SetIncludeFile("LinkedObjects/LinkedObjectsTools.h");
.SetFunctionName("GDpriv::LinkedObjects::RemoveLinkBetween");
extension
.AddAction("RemoveAllLinksOf",
@@ -75,8 +71,7 @@ void DeclareLinkedObjectsExtension(gd::PlatformExtension& extension) {
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("objectPtr", _("Object"))
.SetFunctionName("GDpriv::LinkedObjects::RemoveAllLinksOf")
.SetIncludeFile("LinkedObjects/LinkedObjectsTools.h");
.SetFunctionName("GDpriv::LinkedObjects::RemoveAllLinksOf");
extension
.AddCondition("PickObjectsLinkedTo",
@@ -94,8 +89,7 @@ void DeclareLinkedObjectsExtension(gd::PlatformExtension& extension) {
.AddParameter("objectPtr", _("...if they are linked to this object"))
.AddCodeOnlyParameter("eventsFunctionContext", "")
.SetFunctionName("GDpriv::LinkedObjects::PickObjectsLinkedTo")
.SetIncludeFile("LinkedObjects/LinkedObjectsTools.h");
.SetFunctionName("GDpriv::LinkedObjects::PickObjectsLinkedTo");
extension
.AddAction(
@@ -112,8 +106,5 @@ void DeclareLinkedObjectsExtension(gd::PlatformExtension& extension) {
.AddParameter("objectPtr", _("...if they are linked to this object"))
.AddCodeOnlyParameter("eventsFunctionContext", "")
.SetFunctionName("GDpriv::LinkedObjects::PickObjectsLinkedTo")
.SetIncludeFile("LinkedObjects/LinkedObjectsTools.h");
#endif
.SetFunctionName("GDpriv::LinkedObjects::PickObjectsLinkedTo");
}

View File

@@ -36,9 +36,6 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/PanelSpriteIcon.png")
.SetCategoryFullName(_("General"));
#if defined(GD_IDE_ONLY)
obj.SetIncludeFile("PanelSpriteObject/PanelSpriteObject.h");
obj.AddCondition("Opacity",
_("Opacity"),
_("Compare the opacity of a Panel Sprite, between 0 (fully "
@@ -94,8 +91,7 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetWidth")
.SetGetter("GetWidth")
.SetIncludeFile("PanelSpriteObject/PanelSpriteObject.h");
.SetGetter("GetWidth");
obj.AddCondition("Width",
_("Width"),
@@ -107,8 +103,7 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetWidth")
.SetIncludeFile("PanelSpriteObject/PanelSpriteObject.h");
.SetFunctionName("GetWidth");
obj.AddAction("Height",
_("Height"),
@@ -121,8 +116,7 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetHeight")
.SetGetter("GetHeight")
.SetIncludeFile("PanelSpriteObject/PanelSpriteObject.h");
.SetGetter("GetHeight");
obj.AddCondition("Height",
_("Height"),
@@ -135,8 +129,7 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("SetHeight")
.SetGetter("GetHeight")
.SetIncludeFile("PanelSpriteObject/PanelSpriteObject.h");
.SetGetter("GetHeight");
obj.AddAction("Angle",
"Angle",
@@ -150,8 +143,7 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetAngle")
.SetGetter("GetAngle")
.SetIncludeFile("PanelSpriteObject/PanelSpriteObject.h");
.SetGetter("GetAngle");
obj.AddCondition("Angle",
"Angle",
@@ -165,8 +157,7 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "PanelSprite")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("SetAngle")
.SetGetter("GetAngle")
.SetIncludeFile("PanelSpriteObject/PanelSpriteObject.h");
.SetGetter("GetAngle");
obj.AddAction("Image",
_("Image name"),
@@ -178,7 +169,5 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "PanelSprite")
.AddParameter("string", _("Image name"))
.AddCodeOnlyParameter("currentScene", "0")
.SetFunctionName("ChangeAndReloadImage")
.SetIncludeFile("PanelSpriteObject/PanelSpriteObject.h");
#endif
.SetFunctionName("ChangeAndReloadImage");
}

View File

@@ -39,8 +39,6 @@ void DeclareParticleSystemExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/particleSystemicon.png")
.SetCategoryFullName(_("General"));
obj.SetIncludeFile("ParticleSystem/ParticleEmitterObject.h");
// Declaration is too big to be compiled by GCC in one file, unless you have
// 4GB+ ram. :/
ExtensionSubDeclaration1(obj);

View File

@@ -45,8 +45,7 @@ void ExtensionSubDeclaration1(gd::ObjectMetadata& obj) {
.AddParameter("object", _("Object"), "ParticleEmitter")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetAngle")
.SetGetter("GetAngle")
.SetIncludeFile("ParticleSystem/ParticleEmitterObject.h");
.SetGetter("GetAngle");
obj.AddCondition("EmitterAngle",
_("Emission angle"),

View File

@@ -37,10 +37,6 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
std::make_shared<PathfindingBehavior>(),
std::make_shared<gd::BehaviorsSharedData>());
#if defined(GD_IDE_ONLY)
aut.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
aut.AddAction("SetDestination",
_("Move to a position"),
_("Move the object to a position"),
@@ -54,8 +50,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Destination X position"))
.AddParameter("expression", _("Destination Y position"))
.SetFunctionName("MoveTo")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("MoveTo");
aut.AddCondition("PathFound",
_("Path found"),
@@ -67,8 +62,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("PathFound")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("PathFound");
aut.AddCondition("DestinationReached",
_("Destination reached"),
@@ -80,8 +74,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("DestinationReached")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("DestinationReached");
aut.AddAction("CellWidth",
_("Width of the cells"),
@@ -95,8 +88,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetCellWidth")
.SetGetter("GetCellWidth")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetGetter("GetCellWidth");
aut.AddCondition("CellWidth",
_("Width of the virtual grid"),
@@ -109,8 +101,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetCellWidth")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetCellWidth");
aut.AddAction("CellHeight",
_("Height of the cells"),
@@ -124,8 +115,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetCellHeight")
.SetGetter("GetCellHeight")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetGetter("GetCellHeight");
aut.AddCondition("CellHeight",
_("Height of the virtual grid"),
@@ -138,8 +128,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetCellHeight")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetCellHeight");
aut.AddAction("Acceleration",
_("Acceleration"),
@@ -153,8 +142,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetAcceleration")
.SetGetter("GetAcceleration")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetGetter("GetAcceleration");
aut.AddCondition("Acceleration",
_("Acceleration"),
@@ -167,8 +155,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetAcceleration")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetAcceleration");
aut.AddAction("MaxSpeed",
_("Maximum speed"),
@@ -182,8 +169,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetMaxSpeed")
.SetGetter("GetMaxSpeed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetGetter("GetMaxSpeed");
aut.AddCondition("MaxSpeed",
_("Maximum speed"),
@@ -196,8 +182,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetMaxSpeed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetMaxSpeed");
aut.AddAction("Speed",
_("Speed"),
@@ -211,8 +196,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetSpeed")
.SetGetter("GetSpeed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetGetter("GetSpeed");
aut.AddCondition("Speed",
_("Speed on its path"),
@@ -225,8 +209,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetSpeed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetSpeed");
aut.AddScopedCondition("MovementAngleIsAround",
_("Angle of movement on its path"),
@@ -254,8 +237,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetAngularMaxSpeed")
.SetGetter("GetAngularMaxSpeed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetGetter("GetAngularMaxSpeed");
aut.AddCondition(
"AngularMaxSpeed",
@@ -269,8 +251,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetAngularMaxSpeed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetAngularMaxSpeed");
aut.AddAction(
"AngleOffset",
@@ -285,8 +266,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetAngleOffset")
.SetGetter("GetAngleOffset")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetGetter("GetAngleOffset");
aut.AddCondition("AngleOffset",
_("Rotation offset"),
@@ -299,8 +279,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetAngleOffset")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetAngleOffset");
aut.AddAction(
"ExtraBorder",
@@ -316,8 +295,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetExtraBorder")
.SetGetter("GetExtraBorder")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetGetter("GetExtraBorder");
aut.AddCondition("ExtraBorder",
_("Extra border"),
@@ -331,8 +309,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetExtraBorder")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetExtraBorder");
aut.AddAction(
"AllowDiagonals",
@@ -346,8 +323,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.AddParameter("yesorno", _("Allow?"))
.SetFunctionName("SetAllowDiagonals")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("SetAllowDiagonals");
aut.AddCondition("DiagonalsAllowed",
_("Diagonal movement"),
@@ -360,8 +336,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("DiagonalsAllowed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("DiagonalsAllowed");
aut.AddAction("RotateObject",
_("Rotate the object"),
@@ -374,8 +349,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.AddParameter("yesorno", _("Rotate object?"))
.SetFunctionName("SetRotateObject")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("SetRotateObject");
aut.AddCondition("ObjectRotated",
_("Object rotated"),
@@ -388,8 +362,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("IsObjectRotated")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("IsObjectRotated");
aut.AddExpression("GetNodeX",
_("Get a waypoint X position"),
@@ -399,8 +372,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.AddParameter("expression", _("Node index (start at 0!)"))
.SetFunctionName("GetNodeX")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetNodeX");
aut.AddExpression("GetNodeY",
_("Get a waypoint Y position"),
@@ -410,8 +382,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.AddParameter("expression", _("Node index (start at 0!)"))
.SetFunctionName("GetNodeY")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetNodeY");
aut.AddExpression("NextNodeIndex",
_("Index of the next waypoint"),
@@ -420,8 +391,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetNextNodeIndex")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetNextNodeIndex");
aut.AddExpression("NodeCount",
_("Waypoint count"),
@@ -430,8 +400,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetNodeCount")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetNodeCount");
aut.AddExpression("NextNodeX",
_("Get next waypoint X position"),
@@ -440,8 +409,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetNextNodeX")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetNextNodeX");
aut.AddExpression("NextNodeY",
_("Get next waypoint Y position"),
@@ -450,8 +418,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetNextNodeY")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetNextNodeY");
aut.AddExpression("LastNodeX",
_("Last waypoint X position"),
@@ -460,8 +427,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetLastNodeX")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetLastNodeX");
aut.AddExpression("LastNodeY",
_("Last waypoint Y position"),
@@ -470,8 +436,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetLastNodeY")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetLastNodeY");
aut.AddExpression("DestinationX",
_("Destination X position"),
@@ -480,8 +445,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetDestinationX")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetDestinationX");
aut.AddExpression("DestinationY",
_("Destination Y position"),
@@ -490,8 +454,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetDestinationY")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetDestinationY");
aut.AddExpression("Acceleration",
_("Acceleration"),
@@ -500,8 +463,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetAcceleration")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetAcceleration");
aut.AddExpression("MaxSpeed",
_("Maximum speed"),
@@ -510,8 +472,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetMaxSpeed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetMaxSpeed");
aut.AddExpression("Speed",
_("Speed"),
@@ -520,8 +481,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetSpeed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetSpeed");
aut.AddExpression("AngularMaxSpeed",
_("Angular maximum speed"),
@@ -530,8 +490,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetAngularMaxSpeed")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetAngularMaxSpeed");
aut.AddExpression("AngleOffset",
_("Rotation offset"),
@@ -540,8 +499,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetAngleOffset")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetAngleOffset");
aut.AddExpression("ExtraBorder",
_("Extra border size"),
@@ -550,8 +508,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetExtraBorder")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetExtraBorder");
aut.AddExpression("CellWidth",
_("Width of a cell"),
@@ -560,8 +517,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetCellWidth")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetCellWidth");
aut.AddExpression("CellHeight",
_("Height of a cell"),
@@ -570,8 +526,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.SetFunctionName("GetCellHeight")
.SetIncludeFile("PathfindingBehavior/PathfindingRuntimeBehavior.h");
.SetFunctionName("GetCellHeight");
aut.AddExpression("MovementAngle",
_("Angle of movement on its path"),
@@ -603,7 +558,6 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
.UseStandardParameters("number");
#endif
}
{
gd::BehaviorMetadata& aut = extension.AddBehavior(
@@ -617,10 +571,6 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
std::make_shared<PathfindingObstacleBehavior>(),
std::make_shared<gd::BehaviorsSharedData>());
#if defined(GD_IDE_ONLY)
aut.SetIncludeFile(
"PathfindingBehavior/PathfindingObstacleRuntimeBehavior.h");
aut.AddAction("Cost",
_("Cost"),
_("Change the cost of going through the object."),
@@ -633,9 +583,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetCost")
.SetGetter("GetCost")
.SetIncludeFile(
"PathfindingBehavior/PathfindingObstacleRuntimeBehavior.h");
.SetGetter("GetCost");
aut.AddCondition("Cost",
_("Cost"),
@@ -648,9 +596,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetCost")
.SetIncludeFile(
"PathfindingBehavior/PathfindingObstacleRuntimeBehavior.h");
.SetFunctionName("GetCost");
aut.AddAction("SetImpassable",
_("Should object be impassable?"),
@@ -663,9 +609,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
.AddParameter("yesorno", _("Impassable?"))
.SetFunctionName("SetImpassable")
.SetIncludeFile(
"PathfindingBehavior/PathfindingObstacleRuntimeBehavior.h");
.SetFunctionName("SetImpassable");
aut.AddCondition("IsImpassable",
_("Is object impassable?"),
@@ -677,9 +621,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
.SetFunctionName("IsImpassable")
.SetIncludeFile(
"PathfindingBehavior/PathfindingObstacleRuntimeBehavior.h");
.SetFunctionName("IsImpassable");
aut.AddExpression("Cost",
_("Cost"),
@@ -688,10 +630,6 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/AStaricon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
.SetFunctionName("GetCost")
.SetIncludeFile(
"PathfindingBehavior/PathfindingObstacleRuntimeBehavior.h");
#endif
.SetFunctionName("GetCost");
}
}

View File

@@ -648,6 +648,9 @@ namespace gdjs {
while (this._openNodes.length !== 0) {
//Make sure we do not search forever.
if (iterationCount++ > maxIterationCount) {
console.warn(
`No path was found after covering ${maxIterationCount} cells.`
);
return false;
}

View File

@@ -419,7 +419,7 @@ module.exports = {
sharedData
)
.setIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
.addIncludeFile('Extensions/Physics2Behavior/box2d.js');
.addIncludeFile('Extensions/Physics2Behavior/box2d.js')
// Global
aut
@@ -1396,8 +1396,8 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('X component (N.m)'))
.addParameter('expression', _('Y component (N.m)'))
.addParameter('expression', _('X component (in Newton * seconds or kilogram * meter per second)'))
.addParameter('expression', _('Y component (in Newton * seconds or kilogram * meter per second)'))
.addParameter('expression', _('Applying X position'))
.addParameter('expression', _('Applying Y position'))
.getCodeExtraInformation()
@@ -1420,7 +1420,7 @@ module.exports = {
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Angle'))
.addParameter('expression', _('Length (N.m)'))
.addParameter('expression', _('Length (in Newton * seconds or kilogram * meter per second)'))
.addParameter('expression', _('Applying X position'))
.addParameter('expression', _('Applying Y position'))
.getCodeExtraInformation()
@@ -1442,7 +1442,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('expression', _('Length (N.m)'))
.addParameter('expression', _('Length (in Newton * seconds or kilogram * meter per second)'))
.addParameter('expression', _('X position'))
.addParameter('expression', _('Y position'))
.addParameter('expression', _('Applying X position'))
@@ -1486,18 +1486,44 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('applyAngularImpulse');
aut
aut
.addExpression(
'MassCenterX',
_('Mass center X'),
_('Mass center X'),
'Mass',
_('Mass'),
_('Return the mass of the object (in kilograms)'),
'',
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.getCodeExtraInformation()
.setFunctionName('getMassCenterX');
.setFunctionName('getMass');
aut
.addExpression(
'Inertia',
_('Inertia'),
_('Return the rotational inertia of the object (in kilograms * meters * meters)'),
'',
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.getCodeExtraInformation()
.setFunctionName('getInertia');
aut
.addExpression(
'MassCenterX',
_('Mass center X'),
_('Mass center X'),
'',
'res/physics32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.getCodeExtraInformation()
.setFunctionName('getMassCenterX');
aut
.addExpression(
@@ -3732,9 +3758,9 @@ module.exports = {
.addCondition(
'Collision',
_('Collision'),
_('Test if two objects collide.'),
_('Check if two objects collide.'),
_('_PARAM0_ is colliding with _PARAM2_'),
'',
'',
'res/physics32.png',
'res/physics32.png'
)
@@ -3746,6 +3772,42 @@ module.exports = {
.setIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.setFunctionName('gdjs.physics2.objectsCollide');
extension
.addCondition(
'CollisionStarted',
_('Collision started'),
_('Check if two objects just started colliding during this frame.'),
_('_PARAM0_ started colliding with _PARAM2_'),
_('Collision'),
'res/physics32.png',
'res/physics32.png'
)
.addParameter('objectList', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('objectList', _('Object'), '', false)
.addCodeOnlyParameter('conditionInverted', '')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.setFunctionName('gdjs.physics2.haveObjectsStartedColliding');
extension
.addCondition(
'CollisionStopped',
_('Collision stopped'),
_('Check if two objects just stopped colliding at this frame.'),
_('_PARAM0_ stopped colliding with _PARAM2_'),
_('Collision'),
'res/physics32.png',
'res/physics32.png'
)
.addParameter('objectList', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
.addParameter('objectList', _('Object'), '', false)
.addCodeOnlyParameter('conditionInverted', '')
.getCodeExtraInformation()
.setIncludeFile('Extensions/Physics2Behavior/physics2tools.js')
.setFunctionName('gdjs.physics2.haveObjectsStoppedColliding');
return extension;
},

View File

@@ -20,7 +20,13 @@ namespace gdjs {
// Start with 1 so the user is safe from default variables value (0)
joints: any = {};
// List of physics behavior in the runtimeScene. It should be updated
// when a new physics object is created (constructor), on destruction (onDestroy),
// on behavior activation (onActivate) and on behavior deactivation (onDeActivate).
_registeredBehaviors: Set<Physics2RuntimeBehavior>;
constructor(runtimeScene, sharedData) {
this._registeredBehaviors = new Set();
this.gravityX = sharedData.gravityX;
this.gravityY = sharedData.gravityY;
this.scaleX = sharedData.scaleX === 0 ? 100 : sharedData.scaleX;
@@ -48,13 +54,16 @@ namespace gdjs {
// Get associated behaviors
const behaviorA = contact.GetFixtureA().GetBody()
.gdjsAssociatedBehavior;
.gdjsAssociatedBehavior as Physics2RuntimeBehavior | null;
const behaviorB = contact.GetFixtureB().GetBody()
.gdjsAssociatedBehavior;
.gdjsAssociatedBehavior as Physics2RuntimeBehavior | null;
// Let each behavior know about the contact against the other
behaviorA.currentContacts.push(behaviorB);
behaviorB.currentContacts.push(behaviorA);
if (!behaviorA || !behaviorB) {
return;
}
behaviorA.onContactBegin(behaviorB);
behaviorB.onContactBegin(behaviorA);
};
this.contactListener.EndContact = function (contactPtr) {
// Get the contact
@@ -70,19 +79,16 @@ namespace gdjs {
// Get associated behaviors
const behaviorA = contact.GetFixtureA().GetBody()
.gdjsAssociatedBehavior;
.gdjsAssociatedBehavior as Physics2RuntimeBehavior | null;
const behaviorB = contact.GetFixtureB().GetBody()
.gdjsAssociatedBehavior;
.gdjsAssociatedBehavior as Physics2RuntimeBehavior | null;
// Remove each other contact
let i = behaviorA.currentContacts.indexOf(behaviorB);
if (i !== -1) {
behaviorA.currentContacts.splice(i, 1);
}
i = behaviorB.currentContacts.indexOf(behaviorA);
if (i !== -1) {
behaviorB.currentContacts.splice(i, 1);
if (!behaviorA || !behaviorB) {
return;
}
behaviorA.onContactEnd(behaviorB);
behaviorB.onContactEnd(behaviorA);
};
this.contactListener.PreSolve = function () {};
this.contactListener.PostSolve = function () {};
@@ -104,6 +110,40 @@ namespace gdjs {
return runtimeScene.physics2SharedData;
}
/**
* Add a physics object to the list of existing object.
*/
addToBehaviorsList(physicsBehavior: gdjs.Physics2RuntimeBehavior) {
this._registeredBehaviors.add(physicsBehavior);
}
/**
* Remove a physics object to the list of existing object.
*/
removeFromBehaviorsList(physicsBehavior: gdjs.Physics2RuntimeBehavior) {
this._registeredBehaviors.delete(physicsBehavior);
}
/**
* Reset all contactsStartedThisFrame and contactsEndedThisFrame of all
* registered physics behavior.
*/
resetStartedAndEndedCollisions() {
for (const physicsBehavior of this._registeredBehaviors) {
physicsBehavior.contactsStartedThisFrame.length = 0;
physicsBehavior.contactsEndedThisFrame.length = 0;
}
}
/**
* Update all registered body.
*/
updateBodiesFromObjects() {
for (const physicsBehavior of this._registeredBehaviors) {
physicsBehavior.updateBodyFromObject();
}
}
step(deltaTime) {
this.frameTime += deltaTime;
if (this.frameTime >= this.timeStep) {
@@ -241,11 +281,40 @@ namespace gdjs {
layers: any;
masks: any;
shapeScale: number = 1;
currentContacts: any;
/**
* Array containing the beginning of contacts reported by onContactBegin. Each contact
* should be unique to avoid recording glitches where the object loses and regain
* contact between two frames. The array is updated each time the method
* onContactBegin is called by the listener, which is only called when stepping
* the world i.e. in the first preEvent called by a physics behavior. This array is
* cleared just before stepping the world.
*/
contactsStartedThisFrame: Array<Physics2RuntimeBehavior>;
/**
* Array containing the end of contacts reported by onContactEnd. The array is updated
* each time the method onContactEnd is called by the listener, which can be called at
* any time. This array is cleared just before stepping the world.
*/
contactsEndedThisFrame: Array<Physics2RuntimeBehavior>;
/**
* Array containing the exact current contacts with the objects. It is updated
* each time the methods onContactBegin and onContactEnd are called by the contact
* listener.
*/
currentContacts: Array<Physics2RuntimeBehavior>;
destroyedDuringFrameLogic: boolean;
_body: any = null;
_sharedData: any;
_tempb2Vec2: any;
/**
* sharedData is a reference to the shared data of the scene, that registers
* every physics behavior that is created so that collisions can be cleared
* before stepping the world.
*/
_sharedData: Physics2SharedData;
// Avoid creating new vectors all the time
_tempb2Vec2Sec: any;
@@ -281,14 +350,18 @@ namespace gdjs {
this.gravityScale = behaviorData.gravityScale;
this.layers = behaviorData.layers;
this.masks = behaviorData.masks;
this.currentContacts = this.currentContacts || [];
this.contactsStartedThisFrame = [];
this.contactsEndedThisFrame = [];
this.currentContacts = [];
this.currentContacts.length = 0;
this.destroyedDuringFrameLogic = false;
this._sharedData = Physics2SharedData.getSharedData(
runtimeScene,
behaviorData.name
);
this._tempb2Vec2 = new Box2D.b2Vec2();
this._tempb2Vec2Sec = new Box2D.b2Vec2();
this._sharedData.addToBehaviorsList(this);
}
// Stores a Box2D pointer of created vertices
@@ -373,6 +446,7 @@ namespace gdjs {
}
onDeActivate() {
this._sharedData.removeFromBehaviorsList(this);
if (this._body !== null) {
// When a body is deleted, Box2D removes automatically its joints, leaving an invalid pointer in our joints list
this._sharedData.clearBodyJoints(this._body);
@@ -387,9 +461,22 @@ namespace gdjs {
this._sharedData.world.DestroyBody(this._body);
this._body = null;
}
this.contactsEndedThisFrame.length = 0;
this.contactsStartedThisFrame.length = 0;
this.currentContacts.length = 0;
}
onActivate() {
this._sharedData.addToBehaviorsList(this);
this.contactsEndedThisFrame.length = 0;
this.contactsStartedThisFrame.length = 0;
this.currentContacts.length = 0;
this.updateBodyFromObject();
}
onDestroy() {
this.destroyedDuringFrameLogic = true;
this.onDeActivate();
}
@@ -637,8 +724,7 @@ namespace gdjs {
recreateShape() {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Destroy the old shape
@@ -669,7 +755,8 @@ namespace gdjs {
return this._body;
}
createBody() {
createBody(): boolean {
if (!this.activated() || this.destroyedDuringFrameLogic) return false;
// Generate the body definition
const bodyDef = new Box2D.b2BodyDef();
@@ -707,57 +794,60 @@ namespace gdjs {
// Update cached size
this._objectOldWidth = this.owner.getWidth();
this._objectOldHeight = this.owner.getHeight();
return true;
}
doStepPreEvents(runtimeScene) {
// Create a body if there is not one
if (this._body === null) {
this.createBody();
}
// Step the world if not done this frame yet
if (!this._sharedData.stepped) {
// Reset started and ended contacts array for all physics instances.
this._sharedData.resetStartedAndEndedCollisions();
this._sharedData.updateBodiesFromObjects();
this._sharedData.step(
runtimeScene.getTimeManager().getElapsedTime() / 1000.0
);
}
// Copy transform from body to the GD object
this.owner.setX(
this._body.GetPosition().get_x() * this._sharedData.scaleX -
this.owner.getWidth() / 2 +
this.owner.getX() -
this.owner.getDrawableX()
);
this.owner.setY(
this._body.GetPosition().get_y() * this._sharedData.scaleY -
this.owner.getHeight() / 2 +
this.owner.getY() -
this.owner.getDrawableY()
);
this.owner.setAngle(gdjs.toDegrees(this._body.GetAngle()));
// Copy transform from body to the GD object.
// It's possible the behavior was either deactivated or the object deleted
// just before this doStepPreEvents (for example, another behavior deleted
// the object during its own doStepPreEvents). If the body is null, we just
// don't do anything (but still run the physics simulation - this is independent).
if (this._body !== null) {
this.owner.setX(
this._body.GetPosition().get_x() * this._sharedData.scaleX -
this.owner.getWidth() / 2 +
this.owner.getX() -
this.owner.getDrawableX()
);
this.owner.setY(
this._body.GetPosition().get_y() * this._sharedData.scaleY -
this.owner.getHeight() / 2 +
this.owner.getY() -
this.owner.getDrawableY()
);
this.owner.setAngle(gdjs.toDegrees(this._body.GetAngle()));
}
// Update cached transform
// Update cached transform.
this._objectOldX = this.owner.getX();
this._objectOldY = this.owner.getY();
this._objectOldAngle = this.owner.getAngle();
}
doStepPostEvents(runtimeScene) {
this._updateBodyFromObject();
// Reset world step to update next frame
this._sharedData.stepped = false;
}
onObjectHotReloaded() {
this._updateBodyFromObject();
this.updateBodyFromObject();
}
_updateBodyFromObject() {
updateBodyFromObject() {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// The object size has changed, recreate the shape.
@@ -854,8 +944,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update body type
@@ -878,8 +967,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update body type
@@ -902,8 +990,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update body type
@@ -926,8 +1013,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update body bullet flag
@@ -941,8 +1027,7 @@ namespace gdjs {
setFixedRotation(enable): void {
this.fixedRotation = enable;
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
this._body.SetFixedRotation(this.fixedRotation);
}
@@ -954,8 +1039,7 @@ namespace gdjs {
setSleepingAllowed(enable): void {
this.canSleep = enable;
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
this._body.SetSleepingAllowed(this.canSleep);
}
@@ -963,7 +1047,7 @@ namespace gdjs {
isSleeping(): boolean {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return true;
}
// Get the body sleeping state
@@ -990,8 +1074,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update the body density
@@ -1019,8 +1102,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update the body friction
@@ -1054,8 +1136,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update the body restitution
@@ -1084,8 +1165,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update the body linear damping
@@ -1107,8 +1187,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update the body angular damping
@@ -1130,8 +1209,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update the body gravity scale
@@ -1167,8 +1245,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update the body layers
@@ -1206,8 +1283,7 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
if (!this.createBody()) return;
}
// Update the body masks
@@ -1219,8 +1295,7 @@ namespace gdjs {
getLinearVelocityX(): float {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return 0;
if (!this.createBody()) return 0;
}
// Get the linear velocity on X
@@ -1230,7 +1305,7 @@ namespace gdjs {
setLinearVelocityX(linearVelocityX): void {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Set the linear velocity on X
@@ -1245,8 +1320,7 @@ namespace gdjs {
getLinearVelocityY(): float {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return 0;
if (!this.createBody()) return 0;
}
// Get the linear velocity on Y
@@ -1256,7 +1330,7 @@ namespace gdjs {
setLinearVelocityY(linearVelocityY): void {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Set the linear velocity on Y
@@ -1268,11 +1342,10 @@ namespace gdjs {
);
}
getLinearVelocityLength() {
getLinearVelocityLength(): float {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return 0;
if (!this.createBody()) return 0;
}
// Get the linear velocity length
@@ -1282,10 +1355,10 @@ namespace gdjs {
).Length();
}
getAngularVelocity() {
getAngularVelocity(): float {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return 0;
}
// Get the angular velocity
@@ -1295,7 +1368,7 @@ namespace gdjs {
setAngularVelocity(angularVelocity): void {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Set the angular velocity
@@ -1305,7 +1378,7 @@ namespace gdjs {
applyForce(forceX, forceY, positionX, positionY) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Wake up the object
@@ -1324,7 +1397,7 @@ namespace gdjs {
applyPolarForce(angle, length, positionX, positionY) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Wake up the object
@@ -1344,7 +1417,7 @@ namespace gdjs {
applyForceTowardPosition(length, towardX, towardY, positionX, positionY) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Wake up the object
@@ -1367,7 +1440,7 @@ namespace gdjs {
applyImpulse(impulseX, impulseY, positionX, positionY) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Wake up the object
@@ -1386,7 +1459,7 @@ namespace gdjs {
applyPolarImpulse(angle, length, positionX, positionY) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Wake up the object
@@ -1406,7 +1479,7 @@ namespace gdjs {
applyImpulseTowardPosition(length, towardX, towardY, positionX, positionY) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Wake up the object
@@ -1429,7 +1502,7 @@ namespace gdjs {
applyTorque(torque) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Wake up the object
@@ -1442,7 +1515,7 @@ namespace gdjs {
applyAngularImpulse(angularImpulse) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Wake up the object
@@ -1452,10 +1525,34 @@ namespace gdjs {
this._body.ApplyAngularImpulse(angularImpulse);
}
getMass(): float {
// If there is no body, set a new one
if (this._body === null) {
if (!this.createBody()) return 0;
}
// Wake up the object
this._body.SetAwake(true);
return this._body.GetMass();
}
getInertia(): float {
// If there is no body, set a new one
if (this._body === null) {
if (!this.createBody()) return 0;
}
// Wake up the object
this._body.SetAwake(true);
return this._body.GetInertia();
}
getMassCenterX(): float {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return 0;
}
// Get the mass center on X
@@ -1465,7 +1562,7 @@ namespace gdjs {
getMassCenterY(): float {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return 0;
}
// Get the mass center on Y
@@ -1476,8 +1573,7 @@ namespace gdjs {
isJointFirstObject(jointId): boolean {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return false;
if (!this.createBody()) return false;
}
// Get the joint
@@ -1495,8 +1591,7 @@ namespace gdjs {
isJointSecondObject(jointId): boolean {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return false;
if (!this.createBody()) return false;
}
// Get the joint
@@ -1609,7 +1704,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// If there is no second object or it doesn't share the behavior, return
@@ -1780,7 +1875,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Set joint settings
@@ -1849,7 +1944,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// If there is no second object or it doesn't share the behavior, return
@@ -2140,7 +2235,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// If there is no second object or it doesn't share the behavior, return
@@ -2465,7 +2560,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// If there is no second object or it doesn't share the behavior, return
@@ -2639,7 +2734,7 @@ namespace gdjs {
addGearJoint(jointId1, jointId2, ratio, collideConnected, variable) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Get the first joint
@@ -2760,7 +2855,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// Set joint settings
@@ -2935,7 +3030,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// If there is no second object or it doesn't share the behavior, return
@@ -3211,7 +3306,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// If there is no second object or it doesn't share the behavior, return
@@ -3345,7 +3440,7 @@ namespace gdjs {
addRopeJoint(x1, y1, other, x2, y2, maxLength, collideConnected, variable) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// If there is no second object or it doesn't share the behavior, return
@@ -3452,7 +3547,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// If there is no second object or it doesn't share the behavior, return
@@ -3580,7 +3675,7 @@ namespace gdjs {
) {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
if (!this.createBody()) return;
}
// If there is no second object or it doesn't share the behavior, return
@@ -3797,8 +3892,35 @@ namespace gdjs {
joint.GetBodyB().SetAwake(true);
}
// Collision
static collisionTest(
onContactBegin(otherBehavior: Physics2RuntimeBehavior) {
this.currentContacts.push(otherBehavior);
// There might be contacts that end during the frame and
// start again right away. It is considered a glitch
// and should not be detected.
let i = this.contactsEndedThisFrame.indexOf(otherBehavior);
if (i !== -1) {
this.contactsEndedThisFrame.splice(i, 1);
} else {
this.contactsStartedThisFrame.push(otherBehavior);
}
}
onContactEnd(otherBehavior: Physics2RuntimeBehavior) {
this.contactsEndedThisFrame.push(otherBehavior);
const index = this.currentContacts.indexOf(otherBehavior);
if (index !== -1) {
this.currentContacts.splice(index, 1);
}
}
/**
* @deprecated Prefer using `Physics2RuntimeBehavior.areObjectsColliding`.
*/
static collisionTest = Physics2RuntimeBehavior.areObjectsColliding;
static areObjectsColliding(
object1: gdjs.RuntimeObject,
object2: gdjs.RuntimeObject,
behaviorName: string
@@ -3806,18 +3928,60 @@ namespace gdjs {
// Test if the second object is in the list of contacts of the first one
const behavior1 = object1.getBehavior(
behaviorName
) as Physics2RuntimeBehavior;
if (!!behavior1) {
for (let i = 0, len = behavior1.currentContacts.length; i < len; ++i) {
if (behavior1.currentContacts[i].owner === object2) {
return true;
}
}
) as Physics2RuntimeBehavior | null;
if (!behavior1) return false;
if (
behavior1.currentContacts.some((behavior) => behavior.owner === object2)
) {
return true;
}
// If a contact has started at this frame and ended right away, it
// won't appear in current contacts but the condition should return
// true anyway.
if (
behavior1.contactsStartedThisFrame.some(
(behavior) => behavior.owner === object2
)
) {
return true;
}
// No contact found
return false;
}
static hasCollisionStartedBetween(
object1: gdjs.RuntimeObject,
object2: gdjs.RuntimeObject,
behaviorName: string
): boolean {
// Test if the second object is in the list of contacts of the first one
const behavior1 = object1.getBehavior(
behaviorName
) as Physics2RuntimeBehavior | null;
if (!behavior1) return false;
return behavior1.contactsStartedThisFrame.some(
(behavior) => behavior.owner === object2
);
}
static hasCollisionStoppedBetween(
object1: gdjs.RuntimeObject,
object2: gdjs.RuntimeObject,
behaviorName: string
): boolean {
// Test if the second object is in the list of contacts of the first one
const behavior1 = object1.getBehavior(
behaviorName
) as Physics2RuntimeBehavior | null;
if (!behavior1) return false;
return behavior1.contactsEndedThisFrame.some(
(behavior) => behavior.owner === object2
);
}
}
gdjs.registerBehavior(
'Physics2::Physics2Behavior',

View File

@@ -7,7 +7,37 @@ namespace gdjs {
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(
gdjs.Physics2RuntimeBehavior.collisionTest,
gdjs.Physics2RuntimeBehavior.areObjectsColliding,
objectsLists1,
objectsLists2,
inverted,
behaviorName
);
};
export const haveObjectsStartedColliding = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(
gdjs.Physics2RuntimeBehavior.hasCollisionStartedBetween,
objectsLists1,
objectsLists2,
inverted,
behaviorName
);
};
export const haveObjectsStoppedColliding = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(
gdjs.Physics2RuntimeBehavior.hasCollisionStoppedBetween,
objectsLists1,
objectsLists2,
inverted,

View File

@@ -0,0 +1,801 @@
describe('Physics2RuntimeBehavior', () => {
class FakeAutoRemoveBehavior extends gdjs.RuntimeBehavior {
shouldDeleteInPreEvent = false;
constructor(runtimeScene, behaviorData, owner) {
super(runtimeScene, behaviorData, owner);
}
setShouldAutoRemoveInPreEvent(shouldDeleteInPreEvent) {
this.shouldDeleteInPreEvent = shouldDeleteInPreEvent;
}
doStepPreEvents(runtimeScene) {
if (this.shouldDeleteInPreEvent) {
this.owner.deleteFromScene(runtimeScene);
}
}
}
gdjs.registerBehavior(
'Physics2::FakeAutoRemoveBehavior',
FakeAutoRemoveBehavior
);
function assertCollision(object1, object2, options) {
expect(
gdjs.Physics2RuntimeBehavior.hasCollisionStartedBetween(
object1,
object2,
'Physics2'
)
).to.be(options.started);
expect(
gdjs.Physics2RuntimeBehavior.areObjectsColliding(
object1,
object2,
'Physics2'
)
).to.be(options.collision);
expect(
gdjs.Physics2RuntimeBehavior.hasCollisionStoppedBetween(
object1,
object2,
'Physics2'
)
).to.be(options.stopped);
}
function createGameWithSceneWithPhysics2SharedData() {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: { resources: [] },
properties: { windowWidth: 1000, windowHeight: 1000 },
});
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [
{
name: '',
visibility: true,
cameras: [],
effects: [],
ambientLightColorR: 127,
ambientLightColorB: 127,
ambientLightColorG: 127,
isLightingLayer: false,
followBaseLayerCamera: false,
},
],
variables: [],
r: 0,
v: 0,
b: 0,
mangledName: 'Scene1',
name: 'Scene1',
stopSoundsOnStartup: false,
title: '',
behaviorsSharedData: [],
objects: [],
instances: [],
});
runtimeScene.setInitialSharedDataForBehavior('Physics2', {
gravityX: 0,
gravityY: 0,
scaleX: 1,
scaleY: 1,
});
return [runtimeGame, runtimeScene];
}
function createObjectWithPhysicsBehavior(runtimeScene, behaviorProperties) {
const object = new gdjs.TestRuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
name: 'Physics2',
type: 'Physics2::Physics2Behavior',
bodyType: 'Dynamic',
bullet: false,
fixedRotation: true,
canSleep: false,
shape: 'Box',
shapeDimensionA: 0,
shapeDimensionB: 0,
shapeOffsetX: 0,
shapeOffsetY: 0,
polygonOrigin: 'Center',
vertices: [],
density: 1.0,
friction: 0.01,
restitution: 1,
linearDamping: 0,
angularDamping: 0.1,
gravityScale: 1,
layers: 1,
masks: 1,
...behaviorProperties,
},
],
variables: [],
effects: [],
});
object.setCustomWidthAndHeight(10, 10);
runtimeScene.addObject(object);
/** @type {{behavior: gdjs.Physics2RuntimeBehavior, object: gdjs.RuntimeObject}} */
return { object, behavior: object.getBehavior('Physics2') };
}
describe('Behavior activation and reactivation', () => {
let runtimeGame;
let runtimeScene;
beforeEach(() => {
[runtimeGame, runtimeScene] = createGameWithSceneWithPhysics2SharedData();
});
it('should not leave a living body after removing an object', () => {
const { object, behavior } = createObjectWithPhysicsBehavior(
runtimeScene
);
// First render to have the behavior set up
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.getBody()).not.to.be(null);
expect(behavior._sharedData._registeredBehaviors.size).to.be(1);
expect(behavior._sharedData._registeredBehaviors.has(behavior)).to.be(
true
);
// Delete object from scene
object.deleteFromScene(runtimeScene);
expect(behavior.destroyedDuringFrameLogic).to.be(true);
expect(behavior.getBody()).to.be(null);
expect(behavior._sharedData._registeredBehaviors.size).to.be(0);
expect(behavior._sharedData._registeredBehaviors.has(behavior)).to.be(
false
);
// Call a few methods on the behavior
behavior.setLinearDamping(2);
behavior.setGravityScale(2);
// Body should still not exist
expect(behavior.getBody()).to.be(null);
});
it("doesn't raise errors if an object with a deactivated physics2 behavior is removed", () => {
const { object, behavior } = createObjectWithPhysicsBehavior(
runtimeScene
);
// First render to have the behavior set up
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.getBody()).not.to.be(null);
expect(behavior._sharedData._registeredBehaviors.size).to.be(1);
expect(behavior._sharedData._registeredBehaviors.has(behavior)).to.be(
true
);
object.activateBehavior('Physics2', false);
expect(behavior.getBody()).to.be(null);
expect(behavior._sharedData._registeredBehaviors.size).to.be(0);
expect(behavior._sharedData._registeredBehaviors.has(behavior)).to.be(
false
);
object.deleteFromScene(runtimeScene);
expect(behavior.destroyedDuringFrameLogic).to.be(true);
expect(behavior.getBody()).to.be(null);
expect(behavior._sharedData._registeredBehaviors.size).to.be(0);
});
it("should not recreate object's body when setting or getting behavior properties", () => {
const { object, behavior } = createObjectWithPhysicsBehavior(
runtimeScene
);
// First render to have the behavior set up
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.getBody()).not.to.be(null);
// Deactivate behavior
object.activateBehavior('Physics2', false);
expect(behavior.getBody()).to.be(null);
// Call bunch of methods that should have no impact on the object's body
behavior.setDensity(123);
behavior.setRestitution(0.5);
behavior.getLinearVelocityLength();
behavior.applyImpulse(10, -20, 0, 0);
behavior.getMassCenterX();
// Object's body should still not exist
expect(behavior.getBody()).to.be(null);
// Reactivate behavior
object.activateBehavior('Physics2', true);
expect(behavior.getBody()).not.to.be(null);
// Behavior should have recorded what was called with its setters while it was de-activated.
expect(behavior.getDensity()).to.be(123);
expect(behavior.getRestitution()).to.be(0.5);
});
it('should clear contacts when deactivating the physics2 behavior', () => {
const fps = 60;
runtimeGame.setGameResolutionSize(1000, 1000);
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / fps) * 1000;
};
// Create objects not in contact
const {
object: object1,
behavior: object1Behavior,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Dynamic',
});
object1.setPosition(100, 0);
const {
object: object2,
behavior: object2Behavior,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Static',
restitution: 0,
});
object1.setPosition(0, 0);
expect(object1Behavior.getBody()).not.to.be(null);
expect(object2Behavior.getBody()).not.to.be(null);
expect(object1Behavior._sharedData._registeredBehaviors.size).to.be(2);
expect(
object1Behavior._sharedData._registeredBehaviors.has(object1Behavior)
).to.be(true);
expect(
object1Behavior._sharedData._registeredBehaviors.has(object2Behavior)
).to.be(true);
// Put objects in contact and assert collision started during the frame
runtimeScene.setEventsFunction(() => {
object1.setPosition(10, 0);
object2.setPosition(20, 0);
});
runtimeScene.renderAndStep(1000 / fps);
// After post event, collision should be present
assertCollision(object1, object2, {
started: true,
collision: true,
stopped: false,
});
// Reset scene events
runtimeScene.setEventsFunction(() => {});
// Deactivate physics behavior and test that collisions are cleared.
object1.activateBehavior('Physics2', false);
assertCollision(object1, object2, {
started: false,
collision: false,
// It should be false because the condition does not have sense anymore
// since the behavior is deactivated.
stopped: false,
});
// Objects should have 0 contacts in memory.
expect(object1Behavior.currentContacts.length).to.be(0);
expect(object1Behavior.contactsEndedThisFrame.length).to.be(0);
expect(object1Behavior.contactsStartedThisFrame.length).to.be(0);
expect(object1Behavior._sharedData._registeredBehaviors.size).to.be(1);
expect(
object1Behavior._sharedData._registeredBehaviors.has(object1Behavior)
).to.be(false);
expect(
object1Behavior._sharedData._registeredBehaviors.has(object2Behavior)
).to.be(true);
runtimeScene.renderAndStep(1000 / fps);
// Reactivate physics behavior and test contact
// is not immediately back on but after the first render.
object1.activateBehavior('Physics2', true);
expect(object1Behavior.currentContacts.length).to.be(0);
expect(object1Behavior.contactsEndedThisFrame.length).to.be(0);
expect(object1Behavior.contactsStartedThisFrame.length).to.be(0);
expect(object1Behavior._sharedData._registeredBehaviors.size).to.be(2);
expect(
object1Behavior._sharedData._registeredBehaviors.has(object1Behavior)
).to.be(true);
expect(
object1Behavior._sharedData._registeredBehaviors.has(object2Behavior)
).to.be(true);
runtimeScene.renderAndStep(1000 / fps);
assertCollision(object1, object2, {
started: true,
collision: true,
stopped: false,
});
});
it('should not raise an error if the object is deleted before its physics2 pre-event is run', () => {
const object = new gdjs.TestRuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
// Make FakeAutoRemover run before the Physics2 behavior.
{
name: 'FakeAutoRemover',
type: 'Physics2::FakeAutoRemoveBehavior',
},
{
name: 'Physics2',
type: 'Physics2::Physics2Behavior',
bodyType: 'Dynamic',
bullet: false,
fixedRotation: true,
canSleep: false,
shape: 'Box',
shapeDimensionA: 0,
shapeDimensionB: 0,
shapeOffsetX: 0,
shapeOffsetY: 0,
polygonOrigin: 'Center',
vertices: [],
density: 1.0,
friction: 0.01,
restitution: 1,
linearDamping: 0,
angularDamping: 0.1,
gravityScale: 1,
layers: 1,
masks: 1,
},
],
variables: [],
effects: [],
});
object.setCustomWidthAndHeight(10, 10);
runtimeScene.addObject(object);
/** @type {FakeAutoRemoveBehavior} */
const fakeAutoRemoverBehavior = object.getBehavior('FakeAutoRemover');
const behavior = object.getBehavior('Physics2');
// First render to have the behavior set up
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.getBody()).not.to.be(null);
expect(behavior._sharedData._registeredBehaviors.size).to.be(1);
expect(behavior._sharedData._registeredBehaviors.has(behavior)).to.be(
true
);
fakeAutoRemoverBehavior.setShouldAutoRemoveInPreEvent(true);
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.destroyedDuringFrameLogic).to.be(true);
expect(behavior.getBody()).to.be(null);
expect(behavior._sharedData._registeredBehaviors.size).to.be(0);
});
});
describe('Contacts computation', () => {
let runtimeGame;
let runtimeScene;
beforeEach(() => {
[runtimeGame, runtimeScene] = createGameWithSceneWithPhysics2SharedData();
});
it('should detect a collision even if the contact stated and ended during the same frame', () => {
// Use a low fps to reproduce more easily.
const fps = 2;
runtimeGame.setGameResolutionSize(1000, 1000);
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / fps) * 1000;
};
const {
object: movingObject,
behavior: movingObjectBehavior,
} = createObjectWithPhysicsBehavior(runtimeScene);
const {
object: staticObject,
behavior: staticObjectBehavior,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Static',
});
staticObject.setPosition(0, 25);
movingObject.setPosition(0, 0);
movingObjectBehavior.setLinearVelocityY(40000);
let hasBounced = false;
let stepIndex = 0;
runtimeScene.setEventsFunction(() => {
if (movingObjectBehavior.getLinearVelocityY() > 0) {
// If the moving object has a positive velocity, it hasn't bounced
// on the static object
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: false,
});
} else {
hasBounced = true;
expect(movingObject.getY() < staticObject.getY()).to.be(true);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: true,
});
}
});
while (stepIndex < 10 && !hasBounced) {
runtimeScene.renderAndStep(1000 / fps);
stepIndex++;
}
runtimeScene.setEventsFunction(() => {});
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: false,
});
if (!hasBounced) {
throw new Error('Contact did not happen, nothing was tested.');
}
});
it("should detect a collision when the contact doesn't end the same frame it started", () => {
// Use a high fps to reproduce more easily.
const fps = 50;
runtimeGame.setGameResolutionSize(1000, 1000);
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / fps) * 1000;
};
const {
behavior: movingObjectBehavior,
object: movingObject,
} = createObjectWithPhysicsBehavior(runtimeScene);
const {
behavior: staticObjectBehavior,
object: staticObject,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Static',
});
staticObject.setPosition(0, 25);
movingObject.setPosition(0, 0);
movingObjectBehavior.setLinearVelocityY(40000);
let hasBegunBouncing = false;
let stepIndex = 0;
runtimeScene.setEventsFunction(() => {
if (movingObjectBehavior.getLinearVelocityY() > 0) {
// If the moving object has a positive velocity, it hasn't bounced
// on the static object
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: false,
});
} else {
hasBegunBouncing = true;
// At first frame, collision should have only started
expect(movingObject.getY() < staticObject.getY()).to.be(true);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: false,
});
}
});
while (stepIndex < 10 && !hasBegunBouncing) {
runtimeScene.renderAndStep(1000 / fps);
stepIndex++;
}
if (!hasBegunBouncing) {
throw new Error(
'Start of contact was not detected, nothing was tested.'
);
}
// At next frame, end of collision should be detected
let hasFinishedBouncing = false;
runtimeScene.setEventsFunction(() => {
hasFinishedBouncing = true;
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: true,
});
});
runtimeScene.renderAndStep(1000 / fps);
if (!hasFinishedBouncing) {
throw new Error('End of contact was not detected, nothing was tested.');
}
});
it('should not detect a new contact while already in contact with that same object (the contact jittered).', () => {
const fps = 50;
runtimeGame.setGameResolutionSize(1000, 1000);
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / fps) * 1000;
};
const {
behavior: movingObjectBehavior,
object: movingObject,
} = createObjectWithPhysicsBehavior(runtimeScene);
const {
behavior: staticObjectBehavior,
object: staticObject,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Static',
restitution: 0,
});
staticObject.setPosition(0, 9);
movingObject.setPosition(0, 0);
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: false,
});
runtimeScene.setEventsFunction(() => {
// Manually call onContactEnd and onContactBegin methods to simulate
// a loss of contact followed by a contact beginning during the preEvent.
movingObject
.getBehavior('Physics2')
.onContactEnd(staticObject.getBehavior('Physics2'));
movingObject
.getBehavior('Physics2')
.onContactBegin(staticObject.getBehavior('Physics2'));
assertCollision(movingObject, staticObject, {
started: false,
collision: true,
stopped: false,
});
});
runtimeScene.renderAndStep(1000 / fps);
});
it('should not detect a new contact if the contact ended and jittered.', () => {
const fps = 50;
runtimeGame.setGameResolutionSize(1000, 1000);
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / fps) * 1000;
};
const {
behavior: movingObjectBehavior,
object: movingObject,
} = createObjectWithPhysicsBehavior(runtimeScene);
const {
behavior: staticObjectBehavior,
object: staticObject,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Static',
restitution: 0,
});
staticObject.setPosition(0, 4);
movingObject.setPosition(0, 0);
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: false,
});
runtimeScene.setEventsFunction(() => {
// Manually call onContactEnd and onContactBegin methods to simulate
// a loss of contact followed by a contact beginning and another loss
// of contact during the event.
movingObject
.getBehavior('Physics2')
.onContactEnd(staticObject.getBehavior('Physics2'));
movingObject
.getBehavior('Physics2')
.onContactBegin(staticObject.getBehavior('Physics2'));
movingObject
.getBehavior('Physics2')
.onContactEnd(staticObject.getBehavior('Physics2'));
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: true,
});
});
runtimeScene.renderAndStep(1000 / fps);
});
it('it should end collision on resize (body updated in pre-event).', () => {
const fps = 50;
runtimeGame.setGameResolutionSize(1000, 1000);
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / fps) * 1000;
};
const {
behavior: movingObjectBehavior,
object: movingObject,
} = createObjectWithPhysicsBehavior(runtimeScene);
const {
behavior: staticObjectBehavior,
object: staticObject,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Static',
restitution: 0,
});
staticObject.setPosition(0, 9);
movingObject.setPosition(0, 0);
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: false,
});
// Resize.
runtimeScene.setEventsFunction(() => {
movingObject.setCustomWidthAndHeight(5, 5);
});
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: false,
collision: true,
stopped: false,
});
runtimeScene.setEventsFunction(() => {});
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: true,
});
});
it('it should end collision on object destruction (loss of contact begins during event).', () => {
const fps = 50;
runtimeGame.setGameResolutionSize(1000, 1000);
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / fps) * 1000;
};
const {
behavior: movingObjectBehavior,
object: movingObject,
} = createObjectWithPhysicsBehavior(runtimeScene);
const {
behavior: staticObjectBehavior,
object: staticObject,
} = createObjectWithPhysicsBehavior(runtimeScene, {
bodyType: 'Static',
restitution: 0,
});
staticObject.setPosition(0, 9);
movingObject.setPosition(0, 0);
runtimeScene.renderAndStep(1000 / fps);
assertCollision(movingObject, staticObject, {
started: true,
collision: true,
stopped: false,
});
// Destroy (postEvent operation).
runtimeScene.setEventsFunction(() => {
movingObject.deleteFromScene(runtimeScene);
});
runtimeScene.renderAndStep(1000 / fps);
// Collision should be reset on destroyed object and
// added to contactsEndedThisFrame array of the other object.
assertCollision(movingObject, staticObject, {
started: false,
collision: false,
stopped: false,
});
assertCollision(staticObject, movingObject, {
started: false,
collision: false,
stopped: true,
});
});
});
describe('onContactBegin', () => {
let runtimeGame;
let runtimeScene;
beforeEach(() => {
[runtimeGame, runtimeScene] = createGameWithSceneWithPhysics2SharedData();
});
it('should add behavior to list of started contacts', () => {
const fps = 50;
runtimeGame.setGameResolutionSize(1000, 1000);
const { behavior } = createObjectWithPhysicsBehavior(runtimeScene);
const { behavior: otherBehavior } = createObjectWithPhysicsBehavior(
runtimeScene
);
behavior.onContactBegin(otherBehavior);
expect(behavior.contactsStartedThisFrame.length).to.be(1);
expect(behavior.contactsStartedThisFrame[0]).to.be(otherBehavior);
});
it('should add behavior to list of started contacts and ended contacts', () => {
// From the user point of view the objects are colliding but it could be
// quick enough for it to happen between 2 game frames (the physics model
// uses modelling sub-steps). So contact beginning and end should be detected.
const fps = 50;
runtimeGame.setGameResolutionSize(1000, 1000);
const { behavior } = createObjectWithPhysicsBehavior(runtimeScene);
const { behavior: otherBehavior } = createObjectWithPhysicsBehavior(
runtimeScene
);
behavior.onContactBegin(otherBehavior);
expect(behavior.contactsStartedThisFrame.length).to.be(1);
expect(behavior.contactsStartedThisFrame[0]).to.be(otherBehavior);
expect(behavior.contactsEndedThisFrame.length).to.be(0);
behavior.onContactEnd(otherBehavior);
expect(behavior.contactsStartedThisFrame.length).to.be(1);
expect(behavior.contactsStartedThisFrame[0]).to.be(otherBehavior);
expect(behavior.contactsEndedThisFrame.length).to.be(1);
expect(behavior.contactsEndedThisFrame[0]).to.be(otherBehavior);
});
it('should not add behavior to list of started contacts if the behavior is also present in the list of ended contacts', () => {
// From the user point of view the objects are staying in contact with each other.
// They would be surprised if the conditions for a contact beginning and
// end were true.
const fps = 50;
runtimeGame.setGameResolutionSize(1000, 1000);
const { behavior } = createObjectWithPhysicsBehavior(runtimeScene);
const { behavior: otherBehavior } = createObjectWithPhysicsBehavior(
runtimeScene
);
behavior.onContactEnd(otherBehavior);
expect(behavior.contactsStartedThisFrame.length).to.be(0);
expect(behavior.contactsEndedThisFrame.length).to.be(1);
expect(behavior.contactsEndedThisFrame[0]).to.be(otherBehavior);
behavior.onContactBegin(otherBehavior);
expect(behavior.contactsStartedThisFrame.length).to.be(0);
expect(behavior.contactsEndedThisFrame.length).to.be(0);
});
});
});

View File

@@ -36,7 +36,6 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
std::make_shared<PhysicsBehavior>(),
std::make_shared<ScenePhysicsDatas>());
#if defined(GD_IDE_ONLY)
aut.AddAction("SetStatic",
_("Make the object static"),
_("Make the object immovable."),
@@ -47,8 +46,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetStatic")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetStatic");
aut.AddAction("SetDynamic",
_("Make the object dynamic"),
@@ -61,8 +59,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetDynamic")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetDynamic");
aut.AddCondition("IsDynamic",
_("The object is dynamic"),
@@ -75,8 +72,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.SetFunctionName("IsDynamic")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("IsDynamic");
aut.AddAction("SetFixedRotation",
_("Fix rotation"),
@@ -88,8 +84,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetFixedRotation")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetFixedRotation");
aut.AddAction(
"AddRevoluteJoint",
@@ -105,8 +100,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Hinge X position"))
.AddParameter("expression", _("Hinge Y position"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("AddRevoluteJoint")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("AddRevoluteJoint");
aut.AddAction("AddRevoluteJointBetweenObjects",
_("Add a hinge between two objects"),
@@ -131,8 +125,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
"",
true)
.SetDefaultValue("0")
.SetFunctionName("AddRevoluteJointBetweenObjects")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("AddRevoluteJointBetweenObjects");
aut.AddAction("ActAddGearJointBetweenObjects",
_("Add a gear between two objects"),
@@ -147,8 +140,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Ratio"), "", true)
.SetDefaultValue("1")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("AddGearJointBetweenObjects")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("AddGearJointBetweenObjects");
aut.AddAction("SetFreeRotation",
_("Make object's rotation free"),
@@ -160,8 +152,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetFreeRotation")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetFreeRotation");
aut.AddCondition("IsFixedRotation",
_("Fixed rotation"),
@@ -173,8 +164,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("IsFixedRotation")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("IsFixedRotation");
aut.AddAction("SetAsBullet",
_("Treat object like a bullet."),
@@ -187,8 +177,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetAsBullet")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetAsBullet");
aut.AddAction("DontSetAsBullet",
_("Do not treat object like a bullet"),
@@ -201,8 +190,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("DontSetAsBullet")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("DontSetAsBullet");
aut.AddCondition("IsBullet",
_("Object is treated like a bullet"),
@@ -214,8 +202,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("IsBullet")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("IsBullet");
aut.AddAction("ApplyImpulse",
_("Apply an impulse"),
@@ -229,8 +216,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("X component ( Newtons/Seconds )"))
.AddParameter("expression", _("Y component ( Newtons/Seconds )"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("ApplyImpulse")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("ApplyImpulse");
aut.AddAction("ApplyImpulseUsingPolarCoordinates",
_("Apply an impulse (angle)"),
@@ -246,8 +232,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Angle"))
.AddParameter("expression", _("Impulse value ( Newton/seconds )"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("ApplyImpulseUsingPolarCoordinates")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("ApplyImpulseUsingPolarCoordinates");
aut.AddAction(
"ApplyImpulseTowardPosition",
@@ -264,8 +249,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Y position"))
.AddParameter("expression", _("Impulse value ( Newton/seconds )"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("ApplyImpulseTowardPosition")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("ApplyImpulseTowardPosition");
aut.AddAction("ApplyForce",
_("Add a force"),
@@ -279,8 +263,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("X component ( Newtons )"))
.AddParameter("expression", _("Y component ( Newtons )"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("ApplyForce")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("ApplyForce");
aut.AddAction("ApplyForceUsingPolarCoordinates",
_("Apply a force ( angle )"),
@@ -295,8 +278,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Angle"))
.AddParameter("expression", _("Length of the force ( Newtons )"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("ApplyForceUsingPolarCoordinates")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("ApplyForceUsingPolarCoordinates");
aut.AddAction(
"ApplyForceTowardPosition",
@@ -313,8 +295,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Y position"))
.AddParameter("expression", _("Length of the force ( Newtons )"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("ApplyForceTowardPosition")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("ApplyForceTowardPosition");
aut.AddAction("ApplyTorque",
_("Add a torque (a rotation)"),
@@ -327,8 +308,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddParameter("expression", _("Torque value"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("ApplyTorque")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("ApplyTorque");
aut.AddAction("SetLinearVelocity",
_("Linear velocity"),
@@ -342,8 +322,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("X Coordinate"))
.AddParameter("expression", _("Y Coordinate"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetLinearVelocity")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetLinearVelocity");
aut.AddCondition(
"LinearVelocityX",
@@ -357,8 +336,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.UseStandardRelationalOperatorParameters("number")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetLinearVelocityX")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetLinearVelocityX");
aut.AddCondition(
"LinearVelocityY",
@@ -372,8 +350,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.UseStandardRelationalOperatorParameters("number")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetLinearVelocityY")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetLinearVelocityY");
aut.AddCondition("LinearVelocity",
_("Linear speed"),
@@ -386,8 +363,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.UseStandardRelationalOperatorParameters("number")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetLinearVelocity")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetLinearVelocity");
aut.AddAction("SetAngularVelocity",
_("Angular speed"),
@@ -400,8 +376,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddParameter("expression", _("New value"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetAngularVelocity")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetAngularVelocity");
aut.AddCondition("AngularVelocity",
_("Angular speed"),
@@ -414,8 +389,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.UseStandardRelationalOperatorParameters("number")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetAngularVelocity")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetAngularVelocity");
aut.AddCondition("LinearDamping",
_("Linear damping"),
@@ -428,8 +402,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.UseStandardRelationalOperatorParameters("number")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetLinearDamping")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetLinearDamping");
aut.AddCondition("CollisionWith",
_("Collision"),
@@ -445,8 +418,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddParameter("objectList", _("Object"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("CollisionWith")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("CollisionWith");
aut.AddAction("SetLinearDamping",
_("Linear damping"),
@@ -459,8 +431,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddParameter("expression", _("Value"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetLinearDamping")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetLinearDamping");
aut.AddCondition("AngularDamping",
_("Angular damping"),
@@ -473,8 +444,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.UseStandardRelationalOperatorParameters("number")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetAngularDamping")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetAngularDamping");
aut.AddAction("SetAngularDamping",
_("Angular damping"),
@@ -487,8 +457,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddParameter("expression", _("Value"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetAngularDamping")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetAngularDamping");
aut.AddAction("SetGravity",
_("Gravity"),
@@ -502,8 +471,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("X Coordinate"))
.AddParameter("expression", _("Y Coordinate"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetGravity")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetGravity");
aut.AddAction("SetPolygonScaleX",
_("Change the X scale of a collision polygon"),
@@ -518,8 +486,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddParameter("expression", _("Scale"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetPolygonScaleX")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetPolygonScaleX");
aut.AddAction("SetPolygonScaleY",
_("Change the Y scale of a collision polygon"),
@@ -534,8 +501,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddParameter("expression", _("Scale"))
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SetPolygonScaleY")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("SetPolygonScaleY");
aut.AddCondition(
"GetPolygonScaleX",
@@ -549,8 +515,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.UseStandardRelationalOperatorParameters("number")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetPolygonScaleX")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetPolygonScaleX");
aut.AddCondition(
"GetPolygonScaleY",
@@ -564,8 +529,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.UseStandardRelationalOperatorParameters("number")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetPolygonScaleY")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetPolygonScaleY");
aut.AddExpression("PolygonScaleX",
_("Collision polygon X scale"),
@@ -575,8 +539,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetPolygonScaleX")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetPolygonScaleX");
aut.AddExpression("PolygonScaleY",
_("Collision polygon Y scale"),
@@ -586,8 +549,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetPolygonScaleY")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetPolygonScaleY");
aut.AddExpression("LinearVelocity",
_("Linear speed"),
@@ -597,8 +559,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetLinearVelocity")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetLinearVelocity");
aut.AddExpression("LinearVelocityX",
_("X component"),
@@ -608,8 +569,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetLinearVelocityX")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetLinearVelocityX");
aut.AddExpression("LinearVelocityY",
_("Y component"),
@@ -619,8 +579,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetLinearVelocityY")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetLinearVelocityY");
aut.AddExpression("AngularVelocity",
_("Angular speed"),
@@ -630,8 +589,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetAngularVelocity")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetAngularVelocity");
aut.AddExpression("LinearDamping",
_("Linear damping"),
@@ -641,8 +599,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetLinearDamping")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
.SetFunctionName("GetLinearDamping");
aut.AddExpression("AngularDamping",
_("Angular damping"),
@@ -652,9 +609,6 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("GetAngularDamping")
.SetIncludeFile("PhysicsBehavior/PhysicsRuntimeBehavior.h");
#endif
.SetFunctionName("GetAngularDamping");
}
}

View File

@@ -778,7 +778,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
"PlatformBehavior",
_("Platform"),
"Platform",
_("Flag objects as being platforms where characters can run on."),
_("Flag objects as being platforms which characters can run on."),
"",
"CppPlatform/Extensions/platformicon.png",
"PlatformBehavior",

View File

@@ -1032,6 +1032,7 @@ namespace gdjs {
) {
continue;
}
if (
gdjs.RuntimeObject.collisionTest(
this.owner,
@@ -1714,6 +1715,17 @@ namespace gdjs {
behavior._overlappedJumpThru.push(this._floorPlatform!);
behavior._setFalling();
}
// It was originally in checkTransitionBeforeY.
// The character is ignoring the floor when moving on X to be able to
// follow up a slope when moving Y (it enter inside it).
// When the current floor and the wall the character is facing is part of
// the same instance, the wall is also ignored when moving on X, but the
// wall is too high to follow and it is seen as colliding an obstacle
// from behind.
// Moving against a wall before jumping in this configuration was making
// jumps being aborted.
behavior._checkTransitionJumping();
}
beforeMovingX() {
@@ -1726,10 +1738,8 @@ namespace gdjs {
checkTransitionBeforeY(timeDelta: float) {
const behavior = this._behavior;
//Go on a ladder
// Go on a ladder
behavior._checkTransitionOnLadder();
//Jumping
behavior._checkTransitionJumping();
}
beforeMovingY(timeDelta: float, oldX: float) {

View File

@@ -1606,4 +1606,183 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getY()).to.be(-30);
});
});
describe('(jump against a wall)', function () {
/** @type {gdjs.RuntimeScene} */
let runtimeScene;
/** @type {gdjs.RuntimeObject} */
let object;
/** @type {gdjs.PlatformerObjectRuntimeBehavior} */
let behavior;
beforeEach(function () {
runtimeScene = makePlatformerTestRuntimeScene();
// Put a platformer object on a platform
object = new gdjs.TestRuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
type: 'PlatformBehavior::PlatformerObjectBehavior',
name: 'auto1',
gravity: 1500,
maxFallingSpeed: 1500,
acceleration: 500,
deceleration: 1500,
maxSpeed: 500,
jumpSpeed: 900,
canGrabPlatforms: true,
ignoreDefaultControls: true,
slopeMaxAngle: 60,
jumpSustainTime: 0.2,
useLegacyTrajectory: false,
},
],
effects: [],
});
behavior = object.getBehavior('auto1');
object.setCustomWidthAndHeight(10, 20);
runtimeScene.addObject(object);
// The object is in the corner of the platform.
object.setPosition(80 - 10, 80 - 20);
});
[
{
wallBeing: 'distinct from the floor',
createPlatforms: (runtimeScene) => {
const floor = addPlatformObject(runtimeScene);
floor.setPosition(0, 80);
floor.setCustomWidthAndHeight(100, 20);
const wall = addPlatformObject(runtimeScene);
wall.setPosition(80, 0);
wall.setCustomWidthAndHeight(20, 100);
},
},
{
wallBeing: 'merged with the floor',
createPlatforms: (runtimeScene) => {
const platform = addFloorAndWallPlatformObject(runtimeScene);
platform.setPosition(0, 0);
},
},
].forEach(({ wallBeing, createPlatforms }) => {
it(`can jump while moving against a wall ${wallBeing}`, function () {
createPlatforms(runtimeScene);
// The object stays on the platform.
for (let i = 0; i < 5; ++i) {
runtimeScene.renderAndStep(1000 / 60);
}
expect(object.getY()).to.within(60 - epsilon, 60 + epsilon);
expect(behavior.isFalling()).to.be(false);
expect(behavior.isFallingWithoutJumping()).to.be(false);
expect(behavior.isMoving()).to.be(false);
// Jump without sustain.
behavior.simulateJumpKey();
behavior.simulateRightKey();
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.isJumping()).to.be(true);
// The object is jumping and get higher.
for (let i = 0; i < 5; ++i) {
const oldY = object.getY();
behavior.simulateRightKey();
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.isJumping()).to.be(true);
expect(object.getX()).to.be(80 - 10);
expect(object.getY()).to.be.lessThan(oldY);
}
});
});
});
describe('(jump from slopes)', function () {
/** @type {gdjs.RuntimeScene} */
let runtimeScene;
/** @type {gdjs.RuntimeObject} */
let object;
/** @type {gdjs.PlatformerObjectRuntimeBehavior} */
let behavior;
beforeEach(function () {
runtimeScene = makePlatformerTestRuntimeScene();
// Put a platformer object on a platform
object = new gdjs.TestRuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
type: 'PlatformBehavior::PlatformerObjectBehavior',
name: 'auto1',
gravity: 150,
maxFallingSpeed: 1500,
acceleration: 1000000,
deceleration: 1500,
maxSpeed: 2000,
// This is a very low speed relatively to other properties.
// This is not a playable configuration.
jumpSpeed: 100,
canGrabPlatforms: true,
ignoreDefaultControls: true,
slopeMaxAngle: 60,
jumpSustainTime: 0.2,
useLegacyTrajectory: false,
},
],
effects: [],
});
behavior = object.getBehavior('auto1');
object.setCustomWidthAndHeight(10, 20);
runtimeScene.addObject(object);
// The object is in the slope.
object.setPosition(0, 70);
const platform = addUpSlopePlatformObject(runtimeScene);
platform.setPosition(0, 0);
});
// This is a edge case. The test can be changed if necessary.
// Usually characters jump speed is higher than the speed on Y following a
// slope.
it('can jump while moving up on a slope', function () {
// The object stays on the platform.
for (let i = 0; i < 5; ++i) {
runtimeScene.renderAndStep(1000 / 60);
}
expect(object.getY()).to.within(70, 70 + 1);
expect(behavior.isFalling()).to.be(false);
expect(behavior.isFallingWithoutJumping()).to.be(false);
expect(behavior.isMoving()).to.be(false);
behavior.simulateRightKey();
runtimeScene.renderAndStep(1000 / 60);
// Jump without sustain.
behavior.simulateJumpKey();
behavior.simulateRightKey();
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.isJumping()).to.be(true);
// The object is jumping and is kind of sliding on the slope.
// This behavior is not expected but it avoid the character to be stuck
// into the floor in more common cases.
for (let i = 0; i < 19; ++i) {
const oldY = object.getY();
behavior.simulateRightKey();
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.isJumping()).to.be(true);
expect(object.getY()).to.be.lessThan(oldY);
// As soon as: behavior.getCurrentJumpSpeed() - behavior.getCurrentFallSpeed()
// The character is in the falling step of the jump and it can land.
expect(behavior.isFalling()).to.be(false);
}
// The character lands.
behavior.simulateRightKey();
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.isOnFloor()).to.be(true);
});
});
});

View File

@@ -136,7 +136,7 @@
const addTunnelPlatformObject = (runtimeScene) => {
const platform = new gdjs.TestSpriteRuntimeObject(runtimeScene, {
name: 'slope',
name: 'tunnel',
type: '',
behaviors: [
{
@@ -188,6 +188,68 @@
return platform;
};
/**
* @returns A platform with 2 hitboxes that can act as a floor and a wall at
* the same time.
* It can happen when a tile map collision mask object is used because all
* the platforms are part of the same object instance.
*/
const addFloorAndWallPlatformObject = (runtimeScene) => {
const platform = new gdjs.TestSpriteRuntimeObject(runtimeScene, {
name: 'elbow',
type: '',
behaviors: [
{
type: 'PlatformBehavior::PlatformBehavior',
name: 'Platform',
canBeGrabbed: true,
},
],
effects: [],
animations: [
{
name: 'animation',
directions: [
{
sprites: [
{
originPoint: { x: 0, y: 0 },
centerPoint: { x: 50, y: 50 },
points: [
{ name: 'Origin', x: 0, y: 0 },
{ name: 'Center', x: 50, y: 50 },
],
hasCustomCollisionMask: true,
customCollisionMask: [
// Wall
[
{ x: 80, y: 0 },
{ x: 80, y: 100 },
{ x: 100, y: 100 },
{ x: 100, y: 0 },
],
// Floor
[
{ x: 0, y: 80 },
{ x: 0, y: 100 },
{ x: 100, y: 100 },
{ x: 100, y: 80 },
],
],
},
],
},
],
},
],
});
runtimeScene.addObject(platform);
platform.setUnscaledWidthAndHeight(100, 300);
platform.setCustomWidthAndHeight(100, 300);
return platform;
};
const addJumpThroughPlatformObject = (runtimeScene) => {
const platform = new gdjs.TestRuntimeObject(runtimeScene, {
name: 'obj2',

View File

@@ -31,8 +31,6 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.SetCategoryFullName(_("General"));
#if defined(GD_IDE_ONLY)
obj.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
obj.AddAction(
"Rectangle",
_("Rectangle"),
@@ -48,8 +46,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Top Y position"))
.AddParameter("expression", _("Right X position"))
.AddParameter("expression", _("Bottom Y position"))
.SetFunctionName("DrawRectangle")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("DrawRectangle");
obj.AddAction("Circle",
_("Circle"),
@@ -64,8 +61,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("X position of center"))
.AddParameter("expression", _("Y position of center"))
.AddParameter("expression", _("Radius (in pixels)"))
.SetFunctionName("DrawCircle")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("DrawCircle");
obj.AddAction("Line",
_("Line"),
@@ -84,8 +80,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("X position of end point"))
.AddParameter("expression", _("Y position of end point"))
.AddParameter("expression", _("Thickness (in pixels)"))
.SetFunctionName("DrawLine")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("DrawLine");
obj.AddAction("LineV2",
_("Line"),
@@ -103,8 +98,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("X position of end point"))
.AddParameter("expression", _("Y position of end point"))
.AddParameter("expression", _("Thickness (in pixels)"))
.SetFunctionName("drawLineV2")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("drawLineV2");
obj.AddAction("Ellipse",
_("Ellipse"),
@@ -121,8 +115,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Y position of center"))
.AddParameter("expression", _("The width of the ellipse"))
.AddParameter("expression", _("The height of the ellipse"))
.SetFunctionName("DrawEllipse")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("DrawEllipse");
obj.AddAction("RoundedRectangle",
_("Rounded rectangle"),
@@ -140,8 +133,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Right X position"))
.AddParameter("expression", _("Bottom Y position"))
.AddParameter("expression", _("Radius (in pixels)"))
.SetFunctionName("DrawRoundedRectangle")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("DrawRoundedRectangle");
obj.AddAction(
"Star",
@@ -163,8 +155,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression",
_("Inner radius (in pixels, half radius by default)"))
.AddParameter("expression", _("Rotation (in degrees)"))
.SetFunctionName("DrawStar")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("DrawStar");
obj.AddAction("Arc",
_("Arc"),
@@ -187,8 +178,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("End angle of the arc (in degrees)"))
.AddParameter("yesorno", _("Anticlockwise"))
.AddParameter("yesorno", _("Close path"))
.SetFunctionName("DrawArc")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("DrawArc");
obj.AddAction("BezierCurve",
_("Bezier curve"),
@@ -210,8 +200,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Second Control point y"))
.AddParameter("expression", _("Destination point x"))
.AddParameter("expression", _("Destination point y"))
.SetFunctionName("drawBezierCurve")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("drawBezierCurve");
obj.AddAction("QuadraticCurve",
_("Quadratic curve"),
@@ -230,8 +219,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Control point y"))
.AddParameter("expression", _("Destination point x"))
.AddParameter("expression", _("Destination point y"))
.SetFunctionName("drawQuadraticCurve")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("drawQuadraticCurve");
obj.AddAction("BeginFillPath",
_("Begin fill path"),
@@ -248,8 +236,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("Start drawing x"))
.AddParameter("expression", _("Start drawing y"))
.SetFunctionName("beginFillPath")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("beginFillPath");
obj.AddAction("EndFillPath",
_("End fill path"),
@@ -261,8 +248,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"res/actions/endFillPath.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("endFillPath")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("endFillPath");
obj.AddAction("MovePathTo",
_("Move path drawing position"),
@@ -276,8 +262,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of start point"))
.AddParameter("expression", _("Y position of start point"))
.SetFunctionName("drawPathMoveTo")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("drawPathMoveTo");
obj.AddAction("PathLineTo",
_("Path line"),
@@ -294,8 +279,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("expression", _("X position of start point"))
.AddParameter("expression", _("Y position of start point"))
.SetFunctionName("drawPathLineTo")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("drawPathLineTo");
obj.AddAction("PathBezierCurveTo",
_("Path bezier curve"),
@@ -318,8 +302,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Second Control point y"))
.AddParameter("expression", _("Destination point x"))
.AddParameter("expression", _("Destination point y"))
.SetFunctionName("drawPathBezierCurveTo")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("drawPathBezierCurveTo");
obj.AddAction("PathArc",
_("Path arc"),
@@ -342,8 +325,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Start angle"))
.AddParameter("expression", _("End angle"))
.AddParameter("yesorno", _("Anticlockwise"))
.SetFunctionName("drawPathArc")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("drawPathArc");
obj.AddAction("PathQuadraticCurveTo",
_("Path quadratic curve"),
@@ -363,8 +345,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Control point y"))
.AddParameter("expression", _("Destination point x"))
.AddParameter("expression", _("Destination point y"))
.SetFunctionName("drawPathQuadraticCurveTo")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("drawPathQuadraticCurveTo");
obj.AddAction("closePath",
_("Close Path"),
@@ -377,8 +358,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"res/actions/closePath.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("closePath")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("closePath");
obj.AddScopedAction("ClearShapes",
_("Clear shapes"),
@@ -403,8 +383,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("yesorno", _("Clear between each frame"), "", true)
.SetDefaultValue("yes")
.SetFunctionName("SetClearBetweenFrames")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("SetClearBetweenFrames");
obj.AddCondition(
"ClearBetweenFrames",
@@ -416,8 +395,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"res/conditions/visibilite.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("IsClearedBetweenFrames")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("IsClearedBetweenFrames");
obj.AddAction("FillColor",
_("Fill color"),
@@ -429,8 +407,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("color", _("Fill color"))
.SetFunctionName("SetFillColor")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("SetFillColor");
obj.AddExpression("FillColorRed",
_("Filing color red component"),
@@ -438,8 +415,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"",
"res/actions/color.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetFillColorR")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetFillColorR");
obj.AddExpression("FillColorGreen",
_("Filing color green component"),
@@ -447,8 +423,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"",
"res/actions/color.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetFillColorG")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetFillColorG");
obj.AddExpression("FillColorBlue",
_("Filing color blue component"),
@@ -456,8 +431,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"",
"res/actions/color.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetFillColorB")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetFillColorB");
obj.AddAction("OutlineColor",
_("Outline color"),
@@ -469,8 +443,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("color", _("Color"))
.SetFunctionName("SetOutlineColor")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("SetOutlineColor");
obj.AddExpression("OutlineColorRed",
_("Outline color red component"),
@@ -478,8 +451,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"",
"res/actions/color.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetOutlineColorR")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetOutlineColorR");
obj.AddExpression("OutlineColorGreen",
_("Outline color green component"),
@@ -487,8 +459,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"",
"res/actions/color.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetOutlineColorG")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetOutlineColorG");
obj.AddExpression("OutlineColorBlue",
_("Outline color blue component"),
@@ -496,8 +467,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"",
"res/actions/color.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetOutlineColorB")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetOutlineColorB");
obj.AddAction("OutlineSize",
_("Outline size"),
@@ -510,8 +480,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetOutlineSize")
.SetGetter("GetOutlineSize")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetGetter("GetOutlineSize");
obj.AddCondition("OutlineSize",
_("Outline size"),
@@ -523,8 +492,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetOutlineSize")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetOutlineSize");
obj.AddExpression("OutlineSize",
_("Outline size"),
@@ -532,8 +500,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"",
"res/conditions/outlineSize.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetOutlineSize")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetOutlineSize");
obj.AddAction(
"FillOpacity",
@@ -547,8 +514,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetFillOpacity")
.SetGetter("GetFillOpacity")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetGetter("GetFillOpacity");
obj.AddCondition("FillOpacity",
_("Fill opacity"),
@@ -560,8 +526,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetFillOpacity")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetFillOpacity");
obj.AddExpression("FillOpacity",
_("Filling opacity"),
@@ -569,8 +534,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"",
"res/conditions/opacity.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetFillOpacity")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetFillOpacity");
obj.AddAction("OutlineOpacity",
_("Outline opacity"),
@@ -583,8 +547,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetOutlineOpacity")
.SetGetter("GetOutlineOpacity")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetGetter("GetOutlineOpacity");
obj.AddCondition("OutlineOpacity",
_("Outline opacity"),
@@ -596,8 +559,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetOutlineOpacity")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetOutlineOpacity");
obj.AddExpression("OutlineOpacity",
_("Outline opacity"),
@@ -605,8 +567,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"",
"res/conditions/opacity.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("GetOutlineOpacity")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("GetOutlineOpacity");
obj.AddAction(
"UseRelativeCoordinates",
@@ -620,8 +581,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Shape Painter object"), "Drawer")
.AddParameter("yesorno", _("Use relative coordinates?"), "", false)
.SetDefaultValue("true")
.SetFunctionName("setCoordinatesRelative")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("setCoordinatesRelative");
obj.AddCondition(
"AreCoordinatesRelative",
@@ -633,8 +593,7 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
"res/conditions/position.png")
.AddParameter("object", _("Shape Painter object"), "Drawer")
.SetFunctionName("AreCoordinatesRelative")
.SetIncludeFile("PrimitiveDrawing/ShapePainterObject.h");
.SetFunctionName("AreCoordinatesRelative");
obj.AddAction("Scale",
_("Scale"),

View File

@@ -29,8 +29,7 @@ void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/systeminfoicon.png",
"CppPlatform/Extensions/systeminfoicon.png")
.SetFunctionName("SystemInfo::IsMobile")
.SetIncludeFile("SystemInfo/SystemInfoTools.h");
.SetFunctionName("SystemInfo::IsMobile");
extension
.AddCondition("IsWebGLSupported",
@@ -43,8 +42,7 @@ void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/systeminfoicon.png")
.AddCodeOnlyParameter("currentScene", "")
.SetFunctionName("SystemInfo::IsWebGLSupported")
.SetIncludeFile("SystemInfo/SystemInfoTools.h");
.SetFunctionName("SystemInfo::IsWebGLSupported");
extension
.AddCondition(

View File

@@ -29,9 +29,6 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/textentry.png")
.SetCategoryFullName(_("Advanced"));
#if defined(GD_IDE_ONLY)
obj.SetIncludeFile("TextEntryObject/TextEntryObject.h");
obj.AddAction("String",
_("Text in memory"),
_("Modify text in memory of the object"),
@@ -43,8 +40,7 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TextEntry")
.UseStandardOperatorParameters("string")
.SetFunctionName("SetString")
.SetGetter("GetString")
.SetIncludeFile("TextEntryObject/TextEntryObject.h");
.SetGetter("GetString");
obj.AddCondition("String",
_("Text in memory"),
@@ -56,8 +52,7 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TextEntry")
.UseStandardRelationalOperatorParameters("string")
.SetFunctionName("GetString")
.SetIncludeFile("TextEntryObject/TextEntryObject.h");
.SetFunctionName("GetString");
obj.AddAction(
"Activate",
@@ -71,8 +66,7 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TextEntry")
.AddParameter("yesorno", _("Activate"))
.SetFunctionName("Activate")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("Activate");
obj.AddCondition("Activated",
_("Text input"),
@@ -83,8 +77,7 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/textentryicon.png")
.AddParameter("object", _("Object"), "TextEntry")
.SetFunctionName("IsActivated")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("IsActivated");
obj.AddStrExpression("String",
_("Text entered with keyboard"),
@@ -92,7 +85,5 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
_("Text entered with keyboard"),
"res/texteicon.png")
.AddParameter("object", _("Object"), "TextEntry")
.SetFunctionName("GetString")
.SetIncludeFile("TextObject/TextObject.h");
#endif
.SetFunctionName("GetString");
}

View File

@@ -33,9 +33,6 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/texticon.png")
.SetCategoryFullName(_("Texts"));
#if defined(GD_IDE_ONLY)
obj.SetIncludeFile("TextObject/TextObject.h");
obj.AddAction("String",
_("Modify the text"),
_("Modify the text of a Text object."),
@@ -47,8 +44,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("string")
.SetFunctionName("SetString")
.SetGetter("GetString")
.SetIncludeFile("TextObject/TextObject.h");
.SetGetter("GetString");
obj.AddCondition("String",
_("Compare the text"),
@@ -60,8 +56,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("string")
.SetFunctionName("GetString")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("GetString");
obj.AddAction("Font",
_("Font"),
@@ -73,8 +68,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.AddParameter("police", _("Font"))
.SetFunctionName("ChangeFont")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("ChangeFont");
obj.AddCondition("ScaleX",
_("Scale on X axis"),
@@ -86,8 +80,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetScaleX")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("GetScaleX");
obj.AddAction(
"ScaleX",
@@ -100,8 +93,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetScaleX")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("SetScaleX");
obj.AddCondition("ScaleY",
_("Scale on Y axis"),
@@ -113,8 +105,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetScaleY")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("GetScaleY");
obj.AddAction(
"ScaleY",
@@ -127,8 +118,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetScaleY")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("SetScaleY");
obj.AddAction(
"Scale",
@@ -141,8 +131,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetScale")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("SetScale");
obj.AddAction(
"ChangeColor",
@@ -155,8 +144,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.AddParameter("color", _("Color"))
.SetFunctionName("SetColor")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("SetColor");
obj.AddAction("SetGradient",
_("Gradient"),
@@ -229,8 +217,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetOpacity")
.SetGetter("GetOpacity")
.SetIncludeFile("TextObject/TextObject.h");
.SetGetter("GetOpacity");
obj.AddCondition("Opacity",
_("Opacity"),
@@ -243,8 +230,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetOpacity")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("GetOpacity");
obj.AddAction("SetSmooth",
_("Smoothing"),
@@ -256,8 +242,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.AddParameter("yesorno", _("Smooth the text"))
.SetFunctionName("SetSmooth")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("SetSmooth");
obj.AddCondition("Smoothed",
_("Smoothing"),
@@ -268,8 +253,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"res/conditions/opacity.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("IsSmoothed")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("IsSmoothed");
obj.AddAction("SetBold",
_("Bold"),
@@ -281,8 +265,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.AddParameter("yesorno", _("Set bold style"))
.SetFunctionName("SetBold")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("SetBold");
obj.AddCondition("IsBold",
_("Bold"),
@@ -293,8 +276,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"res/conditions/bold16.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("IsBold")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("IsBold");
obj.AddAction("SetItalic",
_("Italic"),
@@ -306,8 +288,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.AddParameter("yesorno", _("Set italic"))
.SetFunctionName("SetItalic")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("SetItalic");
obj.AddCondition("IsItalic",
_("Italic"),
@@ -318,8 +299,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"res/conditions/italic16.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("IsItalic")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("IsItalic");
obj.AddAction("SetUnderlined",
_("Underlined"),
@@ -331,8 +311,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.AddParameter("yesorno", _("Underline"))
.SetFunctionName("SetUnderlined")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("SetUnderlined");
obj.AddCondition("IsUnderlined",
_("Underlined"),
@@ -343,8 +322,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
"res/conditions/underline16.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("IsUnderlined")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("IsUnderlined");
obj.AddAction("Angle",
_("Angle"),
@@ -357,8 +335,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetAngle")
.SetGetter("GetAngle")
.SetIncludeFile("TextObject/TextObject.h");
.SetGetter("GetAngle");
obj.AddCondition("Angle",
_("Angle"),
@@ -370,8 +347,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetAngle")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("GetAngle");
obj.AddCondition("Padding",
_("Padding"),
@@ -413,8 +389,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Alignment"),
"[\"left\", \"center\", \"right\"]",
false)
.SetFunctionName("SetTextAlignment")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("SetTextAlignment");
obj.AddCondition("TextAlignment",
_("Alignment"),
@@ -485,8 +460,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Scale"),
"res/actions/scaleWidth.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("GetScaleX")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("GetScaleX");
obj.AddExpression("ScaleY",
_("Y Scale of a Text object"),
@@ -494,8 +468,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Scale"),
"res/actions/scaleHeight.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("GetScaleY")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("GetScaleY");
obj.AddExpression("Opacity",
_("Opacity of a Text object"),
@@ -503,8 +476,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Opacity"),
"res/actions/opacity.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("GetOpacity")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("GetOpacity");
obj.AddExpression("Angle",
_("Angle"),
@@ -512,8 +484,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
_("Rotation"),
"res/actions/rotate.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("GetAngle")
.SetIncludeFile("TextObject/TextObject.h");
.SetFunctionName("GetAngle");
obj.AddExpressionAndConditionAndAction("number",
"FontSize",
@@ -532,7 +503,5 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
obj.AddStrExpression(
"String", _("Text"), _("Text"), _("Text"), "res/texteicon.png")
.AddParameter("object", _("Object"), "Text")
.SetFunctionName("GetString")
.SetIncludeFile("TextObject/TextObject.h");
#endif
.SetFunctionName("GetString");
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,150 @@
/// <reference path="helper/TileMapHelper.d.ts" />
namespace gdjs {
export interface RuntimeScene {
tileMapCollisionMaskManager: gdjs.TileMap.TileMapRuntimeManager;
}
export namespace TileMap {
import PIXI = GlobalPIXIModule.PIXI;
const logger = new gdjs.Logger('Tilemap object');
/**
* A holder to share tile maps across the 2 extension objects.
*
* Every instance with the same files path in properties will
* share the same {@link EditableTileMap} and {@link TileTextureCache}.
*
* To use a tile map with collisions, a user can create 4 objects:
* - one for the the rendering
* - one for the solid platforms
* - one for the jumpthrus
* - one for the ladders
*
* To avoid to have 4 copies of the same tile map in memory, this manager
* puts the tile map in cache and avoid unnecessary parsing.
*
* @see {@link TileMapManager}
*/
export class TileMapRuntimeManager {
private _runtimeScene: gdjs.RuntimeScene;
/**
* Delegate that actually manage the caches without anything specific to
* GDJS.
* It allows to factorize code with the IDE.
*/
private _manager: TileMapHelper.TileMapManager;
/**
* @param runtimeScene The scene.
*/
private constructor(runtimeScene: gdjs.RuntimeScene) {
this._runtimeScene = runtimeScene;
this._manager = new TileMapHelper.TileMapManager();
}
/**
* @param runtimeScene Where to set the manager instance.
* @returns The shared manager.
*/
static getManager(
runtimeScene: gdjs.RuntimeScene
): TileMapRuntimeManager {
if (!runtimeScene.tileMapCollisionMaskManager) {
// Create the shared manager if necessary.
runtimeScene.tileMapCollisionMaskManager = new TileMapRuntimeManager(
runtimeScene
);
}
return runtimeScene.tileMapCollisionMaskManager;
}
/**
* @param tileMapJsonResourceName The resource name of the tile map.
* @param tileSetJsonResourceName The resource name of the tile set.
* @param callback A function called when the tile map is parsed.
*/
getOrLoadTileMap(
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
callback: (tileMap: TileMapHelper.EditableTileMap | null) => void
): void {
this._manager.getOrLoadTileMap(
this._loadTiledMap.bind(this),
tileMapJsonResourceName,
tileSetJsonResourceName,
pako,
callback
);
}
/**
* @param getTexture The method that loads the atlas image file in memory.
* @param atlasImageResourceName The resource name of the atlas image.
* @param tileMapJsonResourceName The resource name of the tile map.
* @param tileSetJsonResourceName The resource name of the tile set.
* @param callback A function called when the tiles textures are split.
*/
getOrLoadTextureCache(
getTexture: (textureName: string) => PIXI.BaseTexture<PIXI.Resource>,
atlasImageResourceName: string,
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
callback: (textureCache: TileMapHelper.TileTextureCache | null) => void
): void {
this._manager.getOrLoadTextureCache(
this._loadTiledMap.bind(this),
getTexture,
atlasImageResourceName,
tileMapJsonResourceName,
tileSetJsonResourceName,
callback
);
}
/**
* Parse both JSON and set the content of the tile set in the right
* attribute in the tile map to merge both parsed data.
*/
private _loadTiledMap(
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
callback: (tiledMap: TileMapHelper.TiledMap | null) => void
): void {
this._runtimeScene
.getGame()
.getJsonManager()
.loadJson(tileMapJsonResourceName, (error, tileMapJsonData) => {
if (error) {
logger.error(
'An error happened while loading a Tilemap JSON data:',
error
);
callback(null);
return;
}
const tiledMap = tileMapJsonData as TileMapHelper.TiledMap;
if (tileSetJsonResourceName) {
this._runtimeScene
.getGame()
.getJsonManager()
.loadJson(tileSetJsonResourceName, (error, tileSetJsonData) => {
if (error) {
logger.error(
'An error happened while loading Tileset JSON data:',
error
);
callback(null);
return;
}
const tileSet = tileSetJsonData as TileMapHelper.TiledTileset;
tileSet.firstgid = tiledMap.tilesets[0].firstgid;
tiledMap.tilesets = [tileSet];
callback(tiledMap);
});
} else {
callback(tiledMap);
}
});
}
}
}
}

View File

@@ -0,0 +1,87 @@
namespace gdjs {
export namespace TileMap {
import PIXI = GlobalPIXIModule.PIXI;
/**
* This render is only useful for debugging purposes.
* @see {@link PixiTileMapHelper.updatePixiCollisionMask}, the render used by the GUI.
*/
export class TileMapCollisionMaskRenderer {
_object: gdjs.TileMapCollisionMaskRuntimeObject;
_graphics: PIXI.Graphics;
constructor(
runtimeObject: gdjs.TileMapCollisionMaskRuntimeObject,
runtimeScene: gdjs.RuntimeScene
) {
this._object = runtimeObject;
this._graphics = new PIXI.Graphics();
runtimeScene
.getLayer('')
.getRenderer()
.addRendererObject(this._graphics, runtimeObject.getZOrder());
}
redrawCollisionMask() {
this._graphics.clear();
if (!this._object._debugMode) {
return;
}
this._graphics.lineStyle(
this._object._outlineSize,
this._object._outlineColor,
this._object._outlineOpacity / 255
);
for (const polygon of this._object.getHitBoxes()) {
const vertices = polygon.vertices;
if (vertices.length === 0) continue;
this._graphics.beginFill(
this._object._fillColor,
this._object._fillOpacity / 255
);
this._graphics.moveTo(vertices[0][0], vertices[0][1]);
for (let index = 1; index < vertices.length; index++) {
this._graphics.lineTo(vertices[index][0], vertices[index][1]);
}
this._graphics.closePath();
this._graphics.endFill();
}
}
getRendererObject() {
return this._graphics;
}
setWidth(width: float): void {
const tileMap = this._object._collisionTileMap;
this._graphics.scale.x = width / tileMap.getWidth();
this._graphics.pivot.x = width / 2;
}
setHeight(height: float): void {
const tileMap = this._object._collisionTileMap;
this._graphics.scale.y = height / tileMap.getHeight();
this._graphics.pivot.y = height / 2;
}
getWidth(): float {
const tileMap = this._object._collisionTileMap;
return tileMap.getWidth() * this._graphics.scale.x;
}
getHeight(): float {
const tileMap = this._object._collisionTileMap;
return tileMap.getHeight() * this._graphics.scale.y;
}
getScaleX(): float {
return this._graphics.scale.x;
}
getScaleY(): float {
return this._graphics.scale.y;
}
}
}
}

View File

@@ -0,0 +1,747 @@
/// <reference path="../helper/TileMapHelper.d.ts" />
namespace gdjs {
export namespace TileMap {
/**
* A tile map transformed with an affine transformation.
*
* @see {@link getHitboxesAround} It gives a fast access to hitboxes for collision handling.
*/
export class TransformedCollisionTileMap {
/**
* The model that describes the tile map.
*/
private _source: TileMapHelper.EditableTileMap;
tag: string;
private _layers: Map<integer, TransformedCollisionTileMapLayer>;
// TODO Tiled allows to offset the layers
/**
* The transformation from the time map coordinate (in pixels)
* to the scene coordinate (in pixels).
*/
private _transformation: gdjs.AffineTransformation = new gdjs.AffineTransformation();
/**
* The transformation from the scene coordinate (in pixels)
* to the time map coordinate (in pixels).
*/
private _inverseTransformation: gdjs.AffineTransformation = new gdjs.AffineTransformation();
/**
* This allows tiles to know if their hitboxes must be updated.
* @see {@link TransformedCollisionTile.affineTransformationUpToDateCount}
*/
_transformationUpToDateCount: integer = 1;
/**
* An reusable Point to avoid allocations.
*/
private static readonly workingPoint: FloatPoint = [0, 0];
/**
* @param source The model that describes the tile map.
*/
constructor(source: TileMapHelper.EditableTileMap, tag: string) {
this._source = source;
this.tag = tag;
this._layers = new Map<integer, TransformedCollisionTileMapLayer>();
for (const sourceLayer of source.getLayers()) {
// TODO A visitor could be used to avoid a cast.
if (!(sourceLayer instanceof TileMapHelper.EditableTileMapLayer)) {
// TODO Collision mask for object layers is not handled.
continue;
}
const tileLayer = sourceLayer as TileMapHelper.EditableTileMapLayer;
this._layers.set(
tileLayer.id,
new TransformedCollisionTileMapLayer(this, tileLayer)
);
}
}
/**
* @returns The transformation from the time map coordinate (in pixels)
* to the scene coordinate (in pixels).
*/
getTransformation(): gdjs.AffineTransformation {
return this._transformation;
}
/**
* @param transformation the transformation from the time map coordinate
* (in pixels) to the scene coordinate (in pixels).
*/
setTransformation(transformation: gdjs.AffineTransformation) {
this._transformation = transformation;
const inverseTransformation = this._inverseTransformation;
inverseTransformation.copyFrom(transformation);
inverseTransformation.invert();
this._invalidate();
}
private _invalidate() {
this._transformationUpToDateCount =
(this._transformationUpToDateCount + 1) % Number.MAX_SAFE_INTEGER;
}
/**
* @returns The tile map width in pixels.
*/
getWidth() {
return this._source.getWidth();
}
/**
* @returns The tile map height in pixels.
*/
getHeight() {
return this._source.getHeight();
}
/**
* @returns The tile width in pixels.
*/
getTileHeight() {
return this._source.getTileHeight();
}
/**
* @returns The tile height in pixels.
*/
getTileWidth() {
return this._source.getTileWidth();
}
/**
* @returns The number of tile columns in the map.
*/
getDimensionX() {
return this._source.getDimensionX();
}
/**
* @returns The number of tile rows in the map.
*/
getDimensionY() {
return this._source.getDimensionY();
}
/**
* @param tileId The tile identifier
* @returns The tile definition form the tile set.
*/
getTileDefinition(tileId: integer) {
return this._source.getTileDefinition(tileId);
}
/**
* @param layerId The layer identifier.
* @returns the layer
*/
getLayer(layerId: integer): TransformedCollisionTileMapLayer | undefined {
return this._layers.get(layerId);
}
/**
* @returns All the layers of the tile map.
*/
getLayers(): Iterable<TransformedCollisionTileMapLayer> {
return this._layers.values();
}
/**
* Check if a point is inside a tile with a given tag.
*
* It doesn't use the tile hitboxes.
* It only check the point is inside the tile square.
*
* @param x The X coordinate of the point to check.
* @param y The Y coordinate of the point to check.
* @param tag The tile tag
* @returns true when the point is inside a tile with a given tag.
*/
pointIsInsideTile(x: float, y: float, tag: string): boolean {
const workingPoint: FloatPoint =
TransformedCollisionTileMap.workingPoint;
workingPoint[0] = x;
workingPoint[1] = y;
this._inverseTransformation.transform(workingPoint, workingPoint);
return this._source.pointIsInsideTile(
workingPoint[0],
workingPoint[1],
tag
);
}
/**
* @param tag The tile tag.
* @param left The left border of the area in the scene.
* @param top The top border of the area in the scene.
* @param right The right border of the area in the scene.
* @param bottom The left border of the area in the scene.
* @returns At least all the hitboxes from the given area
* where tiles have the right tag.
*
* @see {@link gdjs.RuntimeObject.getHitboxesAround}
*/
getHitboxesAround(
tag: string,
left: float,
top: float,
right: float,
bottom: float
): Iterable<gdjs.Polygon> {
// Return the hitboxes from the tiles that overlap
// the AABB of the area in the tile map basis.
// Some of these tiles are not event in the given area
// but this is a good trade of between the number of
// useless returned hitboxes and the time to find them.
// Transform the vertices of the area
// from the scene basis to the tile map basis.
const inverseTransformation = this._inverseTransformation;
const workingPoint: FloatPoint =
TransformedCollisionTileMap.workingPoint;
workingPoint[0] = left;
workingPoint[1] = top;
inverseTransformation.transform(workingPoint, workingPoint);
const topLeftX = workingPoint[0];
const topLeftY = workingPoint[1];
workingPoint[0] = right;
workingPoint[1] = top;
inverseTransformation.transform(workingPoint, workingPoint);
const topRightX = workingPoint[0];
const topRightY = workingPoint[1];
workingPoint[0] = right;
workingPoint[1] = bottom;
inverseTransformation.transform(workingPoint, workingPoint);
const bottomRightX = workingPoint[0];
const bottomRightY = workingPoint[1];
workingPoint[0] = left;
workingPoint[1] = bottom;
inverseTransformation.transform(workingPoint, workingPoint);
const bottomLeftX = workingPoint[0];
const bottomLeftY = workingPoint[1];
// Calculate the AABB of the area in the tile map basis.
const xMin = Math.max(
0,
Math.floor(
Math.min(topLeftX, topRightX, bottomRightX, bottomLeftX) /
this._source.getTileWidth()
)
);
const xMax = Math.min(
this.getDimensionX() - 1,
Math.floor(
Math.max(topLeftX, topRightX, bottomRightX, bottomLeftX) /
this._source.getTileWidth()
)
);
const yMin = Math.max(
0,
Math.floor(
Math.min(topLeftY, topRightY, bottomRightY, bottomLeftY) /
this._source.getTileHeight()
)
);
const yMax = Math.min(
this.getDimensionY() - 1,
Math.floor(
Math.max(topLeftY, topRightY, bottomRightY, bottomLeftY) /
this._source.getTileHeight()
)
);
return this.getHitboxes(tag, xMin, yMin, xMax, yMax);
}
/**
* @param tag The tile tag.
* @param xMin The fist column to include.
* @param yMin The fist row to include.
* @param xMax The last column to include.
* @param yMax The last row to include.
* @returns All the hitboxes from the tiles overlapping
* the given area where tiles have the right tag.
*/
getHitboxes(
tag: string,
xMin: integer,
yMin: integer,
xMax: integer,
yMax: integer
): Iterable<gdjs.Polygon> {
return new MapCollisionMaskIterable(this, tag, xMin, yMin, xMax, yMax);
}
/**
* @param tag The tile tag.
* @returns All the hitboxes from the tiles having the right tag.
*/
getAllHitboxes(tag: string): Iterable<gdjs.Polygon> {
return this.getHitboxes(
tag,
0,
0,
this._source.getDimensionX() - 1,
this._source.getDimensionY() - 1
);
}
}
/**
* Iterable over the tile hitboxes of a given area and tag.
*/
class MapCollisionMaskIterable implements Iterable<gdjs.Polygon> {
map: TransformedCollisionTileMap;
tag: string;
xMin: integer;
yMin: integer;
xMax: integer;
yMax: integer;
/**
* Avoid to allocate an empty iterator each time
* the iterable is initialized.
*/
static emptyItr: Iterator<gdjs.Polygon> = {
next: () => ({ value: undefined, done: true }),
};
/**
* @param map The tile map.
* @param tag The tile tag.
* @param xMin The fist column to include.
* @param yMin The fist row to include.
* @param xMax The last column to include.
* @param yMax The last row to include.
*/
constructor(
map: TransformedCollisionTileMap,
tag: string,
xMin: integer,
yMin: integer,
xMax: integer,
yMax: integer
) {
this.map = map;
this.tag = tag;
this.xMin = xMin;
this.yMin = yMin;
this.xMax = xMax;
this.yMax = yMax;
}
[Symbol.iterator]() {
// Flatten the iterable of each layers into one.
let layerItr = this.map.getLayers()[Symbol.iterator]();
let listItr: Iterator<gdjs.Polygon> = MapCollisionMaskIterable.emptyItr;
return {
next: () => {
let listNext = listItr.next();
while (listNext.done) {
const layerNext = layerItr.next();
if (layerNext.done) {
return listNext;
}
listItr = layerNext.value
.getHitboxes(
this.tag,
this.xMin,
this.yMin,
this.xMax,
this.yMax
)
[Symbol.iterator]();
listNext = listItr.next();
}
return listNext;
},
};
}
}
/**
* A tile map layer transformed with an affine transformation.
*/
export class TransformedCollisionTileMapLayer {
/**
* The time map that contains this layer.
*/
readonly tileMap: TransformedCollisionTileMap;
/**
* The model that describes the tile map.
*/
readonly _source: TileMapHelper.EditableTileMapLayer;
private readonly _tiles: TransformedCollisionTile[][];
/**
* @param tileMap The time map that contains this layer.
* @param source The model that describes the tile map.
*/
constructor(
tileMap: TransformedCollisionTileMap,
source: TileMapHelper.EditableTileMapLayer
) {
this.tileMap = tileMap;
this._source = source;
this._tiles = [];
const dimX = this._source.getDimensionX();
const dimY = this._source.getDimensionY();
this._tiles.length = dimY;
for (let y = 0; y < dimY; y++) {
this._tiles[y] = [];
this._tiles[y].length = dimX;
for (let x = 0; x < dimX; x++) {
this._tiles[y][x] = new TransformedCollisionTile(this, x, y);
}
}
}
/**
* @param x The layer column.
* @param y The layer row.
* @return The tile from the tile set.
*/
get(x: integer, y: integer): TransformedCollisionTile | undefined {
const row = this._tiles[y];
return row ? row[x] : undefined;
}
/**
* The number of tile columns in the layer.
*/
getDimensionX() {
return this._tiles.length === 0 ? 0 : this._tiles[0].length;
}
/**
* The number of tile rows in the layer.
*/
getDimensionY() {
return this._tiles.length;
}
/**
* @returns The layer width in pixels.
*/
getWidth() {
return this._source.getWidth();
}
/**
* @returns The layer height in pixels.
*/
getHeight() {
return this._source.getHeight();
}
/**
* @param x The layer column.
* @param y The layer row.
* @returns true if the tile is flipped diagonally.
*/
isFlippedDiagonally(x: integer, y: integer) {
return this._source.isFlippedDiagonally(x, y);
}
/**
* @param x The layer column.
* @param y The layer row.
* @returns true if the tile is flipped vertically.
*/
isFlippedVertically(x: integer, y: integer) {
return this._source.isFlippedVertically(x, y);
}
/**
* @param x The layer column.
* @param y The layer row.
* @returns true if the tile is flipped horizontally.
*/
isFlippedHorizontally(x: integer, y: integer) {
return this._source.isFlippedHorizontally(x, y);
}
/**
* @param tag The tile tag.
* @param xMin The fist column to include.
* @param yMin The fist row to include.
* @param xMax The last column to include.
* @param yMax The last row to include.
* @returns All the hitboxes from the tiles overlapping
* the given area where tiles have the right tag.
*/
getHitboxes(
tag: string,
xMin: integer,
yMin: integer,
xMax: integer,
yMax: integer
): Iterable<gdjs.Polygon> {
return new LayerCollisionMaskIterable(
this,
tag,
xMin,
yMin,
xMax,
yMax
);
}
/**
* @param tag The tile tag.
* @returns All the hitboxes from the tiles having the right tag.
*/
getAllHitboxes(tag: string): Iterable<gdjs.Polygon> {
return this.getHitboxes(
tag,
0,
0,
this.getDimensionX() - 1,
this.getDimensionY() - 1
);
}
}
/**
* Iterable over the tile hitboxes of a given area and tag.
*/
class LayerCollisionMaskIterable implements Iterable<gdjs.Polygon> {
layer: TransformedCollisionTileMapLayer;
tag: string;
xMin: integer;
yMin: integer;
xMax: integer;
yMax: integer;
/**
* Avoid to allocate an empty iterator each time
* the iterable is initialized.
*/
static emptyItr: Iterator<gdjs.Polygon> = {
next: () => ({ value: undefined, done: true }),
};
/**
* @param map The tile map.
* @param tag The tile tag.
* @param xMin The fist column to include.
* @param yMin The fist row to include.
* @param xMax The last column to include.
* @param yMax The last row to include.
*/
constructor(
layer: TransformedCollisionTileMapLayer,
tag: string,
xMin: integer,
yMin: integer,
xMax: integer,
yMax: integer
) {
this.layer = layer;
this.tag = tag;
this.xMin = xMin;
this.yMin = yMin;
this.xMax = xMax;
this.yMax = yMax;
}
[Symbol.iterator]() {
// Flatten the iterable of each tile into one.
// xMin and yMin next increment
let x = this.xMax;
let y = this.yMin - 1;
let polygonItr: Iterator<gdjs.Polygon> =
LayerCollisionMaskIterable.emptyItr;
return {
next: () => {
let listNext = polygonItr.next();
while (listNext.done) {
x++;
if (x > this.xMax) {
y++;
x = this.xMin;
}
if (y > this.yMax) {
// done
return listNext;
}
const tile = this.layer.get(x, y);
if (!tile) {
continue;
}
const definition = tile.getDefinition();
if (!definition) {
continue;
}
if (definition.hasTag(this.tag)) {
polygonItr = tile.getHitboxes()[Symbol.iterator]();
listNext = polygonItr.next();
}
}
return listNext;
},
};
}
}
/**
* A tile transformed with an affine transformation.
*/
class TransformedCollisionTile {
/**
* The layer that contains this tile.
*/
readonly layer: TransformedCollisionTileMapLayer;
/**
* The column index in the layer.
*/
readonly x: integer;
/**
* The row index in the layer.
*/
readonly y: integer;
private readonly hitBoxes: gdjs.Polygon[];
private affineTransformationUpToDateCount: integer = 0;
/**
* An reusable AffineTransformation to avoid allocations.
*/
private static readonly workingTransformation: gdjs.AffineTransformation = new gdjs.AffineTransformation();
/**
*
* @param layer The layer that contains this tile.
* @param x The column index in the layer.
* @param y The row index in the layer.
*/
constructor(
layer: TransformedCollisionTileMapLayer,
x: integer,
y: integer
) {
this.layer = layer;
this.x = x;
this.y = y;
const definition = this.getDefinition();
this.hitBoxes = [];
if (definition) {
const tag = this.layer.tileMap.tag;
const definitionHitboxes = definition.getHitBoxes(tag);
if (definitionHitboxes) {
this.hitBoxes.length = definitionHitboxes.length;
for (
let polygonIndex = 0;
polygonIndex < this.hitBoxes.length;
polygonIndex++
) {
const polygon = new gdjs.Polygon();
this.hitBoxes[polygonIndex] = polygon;
polygon.vertices.length = definitionHitboxes[polygonIndex].length;
for (
let vertexIndex = 0;
vertexIndex < polygon.vertices.length;
vertexIndex++
) {
polygon.vertices[vertexIndex] = [0, 0];
}
}
}
}
}
/**
* @returns The tile definition from the tile set.
*/
getDefinition(): TileMapHelper.TileDefinition {
return this.layer.tileMap.getTileDefinition(
this.layer._source.get(this.x, this.y)!
)!;
}
private _isHitboxesUpToDate() {
return (
this.affineTransformationUpToDateCount ===
this.layer.tileMap._transformationUpToDateCount
);
}
private _setHitboxesUpToDate() {
this.affineTransformationUpToDateCount = this.layer.tileMap._transformationUpToDateCount;
}
/**
* @returns The hitboxes of this tile in the scene basis.
*/
getHitboxes(): Polygon[] {
if (this._isHitboxesUpToDate()) {
return this.hitBoxes;
}
const definition = this.getDefinition();
if (!definition) {
this._setHitboxesUpToDate();
// It should already be []
this.hitBoxes.length = 0;
return this.hitBoxes;
}
const tag = this.layer.tileMap.tag;
const definitionHitboxes = definition.getHitBoxes(tag);
if (!definitionHitboxes) {
this._setHitboxesUpToDate();
// It should already be []
this.hitBoxes.length = 0;
return this.hitBoxes;
}
const layerTransformation = this.layer.tileMap.getTransformation();
const width = this.layer.tileMap.getTileWidth();
const height = this.layer.tileMap.getTileHeight();
const tileTransformation =
TransformedCollisionTile.workingTransformation;
tileTransformation.setToTranslation(width * this.x, height * this.y);
if (this.layer.isFlippedHorizontally(this.x, this.y)) {
tileTransformation.flipX(width / 2);
}
if (this.layer.isFlippedVertically(this.x, this.y)) {
tileTransformation.flipY(height / 2);
}
if (this.layer.isFlippedDiagonally(this.x, this.y)) {
tileTransformation.flipDiagonally();
}
tileTransformation.preConcatenate(layerTransformation);
// The tile map can't change at runtime so the existing arrays can be
// reused safely.
for (
let polygonIndex = 0;
polygonIndex < this.hitBoxes.length;
polygonIndex++
) {
const defPolygon = definitionHitboxes[polygonIndex];
const polygon = this.hitBoxes[polygonIndex];
for (
let vertexIndex = 0;
vertexIndex < polygon.vertices.length;
vertexIndex++
) {
const defVertex = defPolygon[vertexIndex];
const vertex = polygon.vertices[vertexIndex];
tileTransformation.transform(defVertex, vertex);
}
}
this._setHitboxesUpToDate();
return this.hitBoxes;
}
}
}
}

View File

@@ -0,0 +1 @@
This library sources are located in [SharedLibs/TileMapHelper/](../../../SharedLibs/TileMapHelper/).

View File

@@ -0,0 +1,23 @@
import {
EditableTileMap,
EditableTileMapLayer,
TileDefinition,
TiledMap,
TiledTileset,
TileMapManager,
TileTextureCache,
PixiTileMapHelper,
} from './dts/index';
declare global {
namespace TileMapHelper {
export { EditableTileMap };
export { EditableTileMapLayer };
export { TileDefinition };
export { TiledMap };
export { TiledTileset };
export { TileMapManager };
export { TileTextureCache };
export { PixiTileMapHelper };
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
/**
* @packageDocumentation
* @module TileMapHelper
*/
import { TiledMap, TiledTileset } from './tiled/TiledFormat';
import {
EditableTileMap,
EditableTileMapLayer,
TileDefinition,
} from './model/TileMapModel';
import { TileMapManager } from './render/TileMapManager';
import { TileTextureCache } from './render/TileTextureCache';
import { PixiTileMapHelper } from './render/TileMapPixiHelper';
export * from './model/CommonTypes';
export { EditableTileMap };
export { EditableTileMapLayer };
export { TileDefinition };
export { TiledMap };
export { TiledTileset };
export { TileMapManager };
export { TileTextureCache };
export { PixiTileMapHelper };
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,cAAc,qBAAqB,CAAC;AAEpC,OAAO,EAAE,eAAe,EAAE,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,CAAC"}

View File

@@ -0,0 +1,5 @@
export declare type integer = number;
export declare type float = number;
export declare type FloatPoint = [float, float];
export declare type PolygonVertices = FloatPoint[];
//# sourceMappingURL=CommonTypes.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CommonTypes.d.ts","sourceRoot":"","sources":["../../src/model/CommonTypes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,MAAM,OAAO,GAAG,MAAM,CAAC;AACrC,MAAM,CAAC,OAAO,MAAM,KAAK,GAAG,MAAM,CAAC;AACnC,oBAAY,UAAU,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAExC,oBAAY,eAAe,GAAG,UAAU,EAAE,CAAC"}

View File

@@ -0,0 +1,315 @@
import { PolygonVertices, integer, float } from './CommonTypes';
/**
* A tile map model.
*
* Tile map files are parsed into this model by {@link TiledTileMapLoader}.
* This model is used for rending ({@link TileMapRuntimeObjectPixiRenderer})
* and hitboxes handling ({@link TransformedCollisionTileMap}).
* This allows to support new file format with only a new parser.
*/
export declare class EditableTileMap {
private _tileSet;
private _layers;
/**
* The width of a tile.
*/
private readonly tileWidth;
/**
* The height of a tile.
*/
private readonly tileHeight;
/**
* The number of tile columns in the map.
*/
private readonly dimX;
/**
* The number of tile rows in the map.
*/
private readonly dimY;
/**
* @param tileWidth The width of a tile.
* @param tileHeight The height of a tile.
* @param dimX The number of tile columns in the map.
* @param dimY The number of tile rows in the map.
* @param tileSet The tile set.
*/
constructor(
tileWidth: integer,
tileHeight: integer,
dimX: integer,
dimY: integer,
tileSet: Map<integer, TileDefinition>
);
/**
* @returns The tile map width in pixels.
*/
getWidth(): integer;
/**
* @returns The tile map height in pixels.
*/
getHeight(): integer;
/**
* @returns The tile width in pixels.
*/
getTileHeight(): integer;
/**
* @returns The tile height in pixels.
*/
getTileWidth(): integer;
/**
* @returns The number of tile columns in the map.
*/
getDimensionX(): integer;
/**
* @returns The number of tile rows in the map.
*/
getDimensionY(): integer;
/**
* @param tileId The tile identifier
* @returns The tile definition form the tile set.
*/
getTileDefinition(tileId: integer): TileDefinition | undefined;
/**
* @returns All the tile definitions form the tile set.
*/
getTileDefinitions(): Iterable<TileDefinition>;
/**
* @param id The identifier of the new layer.
* @returns The new layer.
*/
addTileLayer(id: integer): EditableTileMapLayer;
/**
* @param id The identifier of the new layer.
* @returns The new layer.
*/
addObjectLayer(id: integer): EditableObjectLayer;
/**
* @returns All the layers of the tile map.
*/
getLayers(): Iterable<AbstractEditableLayer>;
/**
* Check if a point is inside a tile with a given tag.
*
* It doesn't use the tile hitboxes.
* It only check the point is inside the tile square.
*
* @param x The X coordinate of the point to check.
* @param y The Y coordinate of the point to check.
* @param tag The tile tag
* @returns true when the point is inside a tile with a given tag.
*/
pointIsInsideTile(x: float, y: float, tag: string): boolean;
}
/**
* A tile map layer.
*/
declare abstract class AbstractEditableLayer {
/**
* The layer tile map.
*/
readonly tileMap: EditableTileMap;
/**
* The layer identifier.
*/
readonly id: integer;
private visible;
/**
* @param tileMap The layer tile map.
* @param id The layer identifier.
*/
constructor(tileMap: EditableTileMap, id: integer);
setVisible(visible: boolean): void;
/**
* @returns true if the layer is visible.
*/
isVisible(): boolean;
}
/**
* A layer where tiles are placed with pixel coordinates.
*/
export declare class EditableObjectLayer extends AbstractEditableLayer {
readonly objects: TileObject[];
/**
* @param tileMap The layer tile map.
* @param id The layer identifier.
*/
constructor(tileMap: EditableTileMap, id: integer);
add(object: TileObject): void;
}
/**
* A tile that is placed with pixel coordinates.
*/
export declare class TileObject {
/**
* The tile identifier in the tile set.
*/
private tileId;
/**
* The coordinate of the tile left side.
*/
readonly x: float;
/**
* The coordinate of the tile top side.
*/
readonly y: float;
/**
* @param x The coordinate of the tile left side.
* @param y The coordinate of the tile top side.
* @param tileId The tile identifier in the tile set.
*/
constructor(x: float, y: float, tileId: integer);
/**
* @return The tile identifier in the tile set.
*/
getTileId(): integer;
setFlippedHorizontally(flippedHorizontally: boolean): void;
setFlippedVertically(flippedVertically: boolean): void;
setFlippedDiagonally(flippedDiagonally: boolean): void;
/**
* @returns true if the tile is flipped horizontally.
*/
isFlippedHorizontally(): boolean;
/**
* @returns true if the tile is flipped vertically.
*/
isFlippedVertically(): boolean;
/**
* @returns true if the tile is flipped diagonally.
*/
isFlippedDiagonally(): boolean;
}
/**
* A tile map layer with tile organized in grid.
*/
export declare class EditableTileMapLayer extends AbstractEditableLayer {
private readonly _tiles;
/**
* @param tileMap The layer tile map.
* @param id The layer identifier.
*/
constructor(tileMap: EditableTileMap, id: integer);
/**
* @param x The layer column.
* @param y The layer row.
* @param tileId The tile identifier in the tile set.
*/
setTile(x: integer, y: integer, tileId: integer): void;
/**
* @param x The layer column.
* @param y The layer row.
*/
removeTile(x: integer, y: integer): void;
/**
* @param x The layer column.
* @param y The layer row.
* @param flippedHorizontally true if the tile is flipped horizontally.
*/
setFlippedHorizontally(
x: integer,
y: integer,
flippedHorizontally: boolean
): void;
/**
* @param x The layer column.
* @param y The layer row.
* @param flippedVertically true if the tile is flipped vertically.
*/
setFlippedVertically(
x: integer,
y: integer,
flippedVertically: boolean
): void;
/**
* @param x The layer column.
* @param y The layer row.
* @param flippedDiagonally true if the tile is flipped diagonally.
*/
setFlippedDiagonally(
x: integer,
y: integer,
flippedDiagonally: boolean
): void;
/**
* @param x The layer column.
* @param y The layer row.
* @returns true if the tile is flipped horizontally.
*/
isFlippedHorizontally(x: integer, y: integer): boolean;
/**
* @param x The layer column.
* @param y The layer row.
* @returns true if the tile is flipped vertically.
*/
isFlippedVertically(x: integer, y: integer): boolean;
/**
* @param x The layer column.
* @param y The layer row.
* @returns true if the tile is flipped diagonally.
*/
isFlippedDiagonally(x: integer, y: integer): boolean;
/**
* @param x The layer column.
* @param y The layer row.
* @returns The tile identifier from the tile set.
*/
get(x: integer, y: integer): integer | undefined;
/**
* The number of tile columns in the layer.
*/
getDimensionX(): integer;
/**
* The number of tile rows in the layer.
*/
getDimensionY(): integer;
/**
* @returns The layer width in pixels.
*/
getWidth(): integer;
/**
* @returns The layer height in pixels.
*/
getHeight(): integer;
}
/**
* A tile definition from the tile set.
*/
export declare class TileDefinition {
/**
* There will probably be at most 4 tags on a tile.
* An array lookup should take less time than using a Map.
*/
private readonly taggedHitBoxes;
private readonly animationLength;
/**
* @param animationLength The number of frame in the tile animation.
*/
constructor(animationLength: integer);
/**
* Add a polygon for the collision layer
* @param tag The tag to allow collision layer filtering.
* @param polygon The polygon to use for collisions.
*/
add(tag: string, polygon: PolygonVertices): void;
/**
* This property is used by {@link TransformedCollisionTileMap}
* to make collision classes.
* @param tag The tag to allow collision layer filtering.
* @returns true if this tile contains any polygon with the given tag.
*/
hasTag(tag: string): boolean;
/**
* The hitboxes positioning is done by {@link TransformedCollisionTileMap}.
* @param tag The tag to allow collision layer filtering.
* @returns The hit boxes for this tile.
*/
getHitBoxes(tag: string): PolygonVertices[] | undefined;
/**
* Animated tiles have a limitation:
* they are only able to use frames arranged horizontally one next
* to each other on the atlas.
* @returns The number of frame in the tile animation.
*/
getAnimationLength(): integer;
}
export {};
//# sourceMappingURL=TileMapModel.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TileMapModel.d.ts","sourceRoot":"","sources":["../../src/model/TileMapModel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEhE;;;;;;;GAOG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAA+B;IAC9C;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;IAC/B;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;IAE/B;;;;;;OAMG;gBAED,SAAS,EAAE,OAAO,EAClB,UAAU,EAAE,OAAO,EACnB,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,OAAO,EAGb,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC;IAUvC;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;;OAGG;IACH,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS;IAI9D;;OAEG;IACH,kBAAkB,IAAI,QAAQ,CAAC,cAAc,CAAC;IAI9C;;;OAGG;IACH,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,oBAAoB;IAM/C;;;OAGG;IACH,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,mBAAmB;IAMhD;;OAEG;IACH,SAAS,IAAI,QAAQ,CAAC,qBAAqB,CAAC;IAI5C;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;CAmB5D;AAED;;GAEG;AACH,uBAAe,qBAAqB;IAClC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC;;OAEG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,OAAO,CAAiB;IAEhC;;;OAGG;gBACS,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,OAAO;IAKjD,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIlC;;OAEG;IACH,SAAS,IAAI,OAAO;CAGrB;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,qBAAqB;IAC5D,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;IAE/B;;;OAGG;gBACS,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,OAAO;IAKjD,GAAG,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;CAG9B;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAU;IACxB;;OAEG;IACH,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC;IAClB;;OAEG;IACH,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC;IAElB;;;;OAIG;gBACS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO;IAM/C;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB,sBAAsB,CAAC,mBAAmB,EAAE,OAAO,GAAG,IAAI;IAO1D,oBAAoB,CAAC,iBAAiB,EAAE,OAAO,GAAG,IAAI;IAOtD,oBAAoB,CAAC,iBAAiB,EAAE,OAAO,GAAG,IAAI;IAOtD;;OAEG;IACH,qBAAqB,IAAI,OAAO;IAIhC;;OAEG;IACH,mBAAmB,IAAI,OAAO;IAI9B;;OAEG;IACH,mBAAmB,IAAI,OAAO;CAG/B;AAiED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,qBAAqB;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAE3C;;;OAGG;gBACS,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,OAAO;IASjD;;;;OAIG;IACH,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAUtD;;;OAGG;IACH,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,IAAI;IAKxC;;;;OAIG;IACH,sBAAsB,CACpB,CAAC,EAAE,OAAO,EACV,CAAC,EAAE,OAAO,EACV,mBAAmB,EAAE,OAAO,GAC3B,IAAI;IAWP;;;;OAIG;IACH,oBAAoB,CAClB,CAAC,EAAE,OAAO,EACV,CAAC,EAAE,OAAO,EACV,iBAAiB,EAAE,OAAO,GACzB,IAAI;IAWP;;;;OAIG;IACH,oBAAoB,CAClB,CAAC,EAAE,OAAO,EACV,CAAC,EAAE,OAAO,EACV,iBAAiB,EAAE,OAAO,GACzB,IAAI;IAWP;;;;OAIG;IACH,qBAAqB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAItD;;;;OAIG;IACH,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIpD;;;;OAIG;IACH,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIpD;;;;OAIG;IACH,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS;IAUhD;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,SAAS,IAAI,OAAO;CAGrB;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG3B;IACJ,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAE1C;;OAEG;gBACS,eAAe,EAAE,OAAO;IAKpC;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAShD;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI5B;;;;OAIG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,GAAG,SAAS;IAOvD;;;;;OAKG;IACH,kBAAkB,IAAI,OAAO;CAG9B"}

View File

@@ -0,0 +1,27 @@
/**
* A cache of resources identified by a string.
*
* It ensures that a resource is never load twice.
*/
export declare class ResourceCache<T> {
private _cachedValues;
/**
* Several calls can happen before the resource is loaded.
* This allows to stack them.
*/
private _callbacks;
constructor();
/**
* Return a resource through a call back.
* @param key the resource identifier.
* @param load load the resource in case of cache default.
* Note that the load callback is used by `getOrLoad` and not by the caller.
* @param callback called when the resource is ready.
*/
getOrLoad(
key: string,
load: (callback: (value: T | null) => void) => void,
callback: (value: T | null) => void
): void;
}
//# sourceMappingURL=ResourceCache.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ResourceCache.d.ts","sourceRoot":"","sources":["../../src/render/ResourceCache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,aAAa,CAAC,CAAC;IAC1B,OAAO,CAAC,aAAa,CAAiB;IAEtC;;;OAGG;IACH,OAAO,CAAC,UAAU,CAAgD;;IAOlE;;;;;;OAMG;IACH,SAAS,CACP,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,KAAK,IAAI,EACnD,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,GAClC,IAAI;CA+BR"}

View File

@@ -0,0 +1,61 @@
import { TiledMap } from '../tiled/TiledFormat';
import { EditableTileMap } from '../model/TileMapModel';
import { TileTextureCache } from './TileTextureCache';
import PIXI = GlobalPIXIModule.PIXI;
/**
* A holder to share tile maps across the 2 extension objects.
*
* Every instance with the same files path in properties will
* share the same {@link EditableTileMap} and {@link TileTextureCache}.
*
* @see {@link TileMapRuntimeManager}
*/
export declare class TileMapManager {
private _tileMapCache;
private _textureCacheCaches;
constructor();
/**
* @param instanceHolder Where to set the manager instance.
* @returns The shared manager.
*/
static getManager(instanceHolder: Object): TileMapManager;
/**
* @param loadTiledMap The method that loads the Tiled JSON file in memory.
* @param tileMapJsonResourceName The resource name of the tile map.
* @param tileSetJsonResourceName The resource name of the tile set.
* @param pako The zlib library.
* @param callback A function called when the tile map is parsed.
*/
getOrLoadTileMap(
loadTiledMap: (
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
callback: (tiledMap: TiledMap | null) => void
) => void,
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
pako: any,
callback: (tileMap: EditableTileMap | null) => void
): void;
/**
* @param loadTiledMap The method that loads the Tiled JSON file in memory.
* @param getTexture The method that loads the atlas image file in memory.
* @param atlasImageResourceName The resource name of the atlas image.
* @param tileMapJsonResourceName The resource name of the tile map.
* @param tileSetJsonResourceName The resource name of the tile set.
* @param callback A function called when the tiles textures are split.
*/
getOrLoadTextureCache(
loadTiledMap: (
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
callback: (tiledMap: TiledMap | null) => void
) => void,
getTexture: (textureName: string) => PIXI.BaseTexture<PIXI.Resource>,
atlasImageResourceName: string,
tileMapJsonResourceName: string,
tileSetJsonResourceName: string,
callback: (textureCache: TileTextureCache | null) => void
): void;
}
//# sourceMappingURL=TileMapManager.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TileMapManager.d.ts","sourceRoot":"","sources":["../../src/render/TileMapManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAEpC;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,mBAAmB,CAAkC;;IAO7D;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,cAAc;IAWzD;;;;;;OAMG;IACH,gBAAgB,CACd,YAAY,EAAE,CACZ,uBAAuB,EAAE,MAAM,EAC/B,uBAAuB,EAAE,MAAM,EAC/B,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,KAC1C,IAAI,EACT,uBAAuB,EAAE,MAAM,EAC/B,uBAAuB,EAAE,MAAM,EAC/B,IAAI,EAAE,GAAG,EACT,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,GAClD,IAAI;IAwBP;;;;;;;OAOG;IACH,qBAAqB,CACnB,YAAY,EAAE,CACZ,uBAAuB,EAAE,MAAM,EAC/B,uBAAuB,EAAE,MAAM,EAC/B,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,KAC1C,IAAI,EACT,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,EACpE,sBAAsB,EAAE,MAAM,EAC9B,uBAAuB,EAAE,MAAM,EAC/B,uBAAuB,EAAE,MAAM,EAC/B,QAAQ,EAAE,CAAC,YAAY,EAAE,gBAAgB,GAAG,IAAI,KAAK,IAAI,GACxD,IAAI;CAoCR"}

View File

@@ -0,0 +1,54 @@
import { integer, float } from '../model/CommonTypes';
import { TiledMap } from '../tiled/TiledFormat';
import { EditableTileMap } from '../model/TileMapModel';
import { TileTextureCache } from './TileTextureCache';
import PIXI = GlobalPIXIModule.PIXI;
export declare class PixiTileMapHelper {
/**
* Split an atlas image into Pixi textures.
*
* @param tiledMap A tile map exported from Tiled.
* @param atlasTexture The texture containing the whole tile set.
* @param getTexture A getter to load a texture. Used if atlasTexture is not specified.
* @returns A textures cache.
*/
static parseAtlas(
tiledMap: TiledMap,
atlasTexture: PIXI.BaseTexture<PIXI.Resource> | null,
getTexture: (textureName: string) => PIXI.BaseTexture<PIXI.Resource>
): TileTextureCache | null;
/**
* Re-renders the tile map whenever its rendering settings have been changed
*
* @param pixiTileMap the tile map renderer
* @param tileMap the tile map model
* @param textureCache the tile set textures
* @param displayMode What to display:
* - only a single layer (`index`)
* - only visible layers (`visible`)
* - everything (`all`).
* @param layerIndex If `displayMode` is set to `index`, the layer index to be
* displayed.
*/
static updatePixiTileMap(
untypedPixiTileMap: any,
tileMap: EditableTileMap,
textureCache: TileTextureCache,
displayMode: 'index' | 'visible' | 'all',
layerIndex: number
): void;
/**
* Re-renders the collision mask
*/
static updatePixiCollisionMask(
pixiGraphics: PIXI.Graphics,
tileMap: EditableTileMap,
typeFilter: string,
outlineSize: integer,
outlineColor: integer,
outlineOpacity: float,
fillColor: integer,
fillOpacity: float
): void;
}
//# sourceMappingURL=TileMapPixiHelper.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TileMapPixiHelper.d.ts","sourceRoot":"","sources":["../../src/render/TileMapPixiHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAEL,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAGpC,qBAAa,iBAAiB;IAC5B;;;;;;;OAOG;IACH,MAAM,CAAC,UAAU,CACf,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,EACpD,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GACnE,gBAAgB,GAAG,IAAI;IA+E1B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,iBAAiB,CACtB,kBAAkB,EAAE,GAAG,EACvB,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,EACxC,UAAU,EAAE,MAAM,GACjB,IAAI;IA4EP;;OAEG;IACH,MAAM,CAAC,uBAAuB,CAC5B,YAAY,EAAE,IAAI,CAAC,QAAQ,EAC3B,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,OAAO,EACpB,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,KAAK,EACrB,SAAS,EAAE,OAAO,EAClB,WAAW,EAAE,KAAK,GACjB,IAAI;CA6DR"}

View File

@@ -0,0 +1,44 @@
import { integer } from '../model/CommonTypes';
import PIXI = GlobalPIXIModule.PIXI;
/**
* A cache to access the tile images.
*
* It's created by {@link PixiTileMapHelper.parseAtlas}
* and used by {@link PixiTileMapHelper.updatePixiTileMap}.
*/
export declare class TileTextureCache {
private static readonly flippedHorizontallyFlag;
private static readonly flippedVerticallyFlag;
private static readonly flippedDiagonallyFlag;
private readonly _textures;
constructor();
setTexture(
tileId: integer,
flippedHorizontally: boolean,
flippedVertically: boolean,
flippedDiagonally: boolean,
texture: PIXI.Texture
): void;
/**
* Return the texture to use for the tile with the specified uid, which can contains
* information about rotation in bits 32, 31 and 30
* (see https://doc.mapeditor.org/en/stable/reference/tmx-map-format/).
*
* @param tileId The tile identifier
* @param flippedHorizontally true if the tile is flipped horizontally.
* @param flippedVertically true if the tile is flipped vertically.
* @param flippedDiagonally true if the tile is flipped diagonally.
* @returns The texture for the given tile identifier and orientation.
*/
findTileTexture(
tileId: integer,
flippedHorizontally: boolean,
flippedVertically: boolean,
flippedDiagonally: boolean
): PIXI.Texture | undefined;
/**
* @return the Tiled tile global uniq identifier.
*/
private _getGlobalId;
}
//# sourceMappingURL=TileTextureCache.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TileTextureCache.d.ts","sourceRoot":"","sources":["../../src/render/TileTextureCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAEpC;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAc;IAC7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAc;IAC3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAc;IAE3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;;IAMvD,UAAU,CACR,MAAM,EAAE,OAAO,EACf,mBAAmB,EAAE,OAAO,EAC5B,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,EAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,GACpB,IAAI;IAUP;;;;;;;;;;OAUG;IACH,eAAe,CACb,MAAM,EAAE,OAAO,EACf,mBAAmB,EAAE,OAAO,EAC5B,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,GACzB,IAAI,CAAC,OAAO,GAAG,SAAS;IA8D3B;;OAEG;IACH,OAAO,CAAC,YAAY;CAkBrB"}

View File

@@ -0,0 +1,339 @@
import { float, integer } from '../model/CommonTypes';
/**
* Tiled JSON format.
*/
export declare type TiledMap = {
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
backgroundcolor?: string;
/** The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default) */
compressionlevel: integer;
/** Number of tile rows */
height: integer;
/** Length of the side of a hex tile in pixels (hexagonal maps only) */
hexsidelength?: integer;
/** Whether the map has infinite dimensions */
infinite: boolean;
/** Array of {@link TiledLayer} */
layers: Array<TiledLayer>;
/** Auto-increments for each layer */
nextlayerid: integer;
/** Auto-increments for each placed object */
nextobjectid: integer;
/** `orthogonal`, `isometric`, `staggered` or `hexagonal` */
orientation: string;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** `right-down` (the default), `right-up`, `left-down` or `left-up` (currently only supported for orthogonal maps) */
renderorder: string;
/** `x` or `y` (staggered / hexagonal maps only) */
staggeraxis?: string;
/** `odd` or `even` (staggered / hexagonal maps only) */
staggerindex?: string;
/** The Tiled version used to save the file */
tiledversion: string;
/** Map grid height */
tileheight: integer;
/** Array of {@link TiledTileset} */
tilesets: Array<TiledTileset>;
/** Map grid width */
tilewidth: integer;
/** `map` (since 1.0) */
type: string;
/** The JSON format version (previously a number, saved as string since 1.6) */
version: string;
/** Number of tile columns */
width: integer;
};
export declare type TiledLayer = {
/** Array of {@link TiledChunk} (optional). `tilelayer` only. */
chunks?: Array<TiledChunk>;
/** `zlib`, `gzip`, `zstd` (since Tiled 1.3) or empty (default). `tilelayer` only. */
compression?: string;
/** Array of `unsigned`, `integer` (GIDs) or base64-encoded data. `tilelayer` only.*/
data?: Array<integer> | string;
/** `topdown` (default) or `index`. `objectgroup` only. */
draworder?: string;
/** `csv` (default) or `base64`. `tilelayer` only. */
encoding?: string;
/** Row count. Same as map height for fixed-size maps. */
height?: integer;
/** Incremental ID - unique across all layers */
id?: integer;
/** Image used by this layer. `imagelayer` only. */
image?: string;
/** Array of {@link TiledLayer}. `group` only. */
layers?: Array<TiledLayer>;
/** Name assigned to this layer */
name: string;
/** Array of {@link TiledObject}. `objectgroup` only. */
objects?: Array<TiledObject>;
/** Horizontal layer offset in pixels (default: 0) */
offsetx?: float;
/** Vertical layer offset in pixels (default: 0) */
offsety?: float;
/** Value between 0 and 1 */
opacity: float;
/** Horizontal {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
parallaxx?: float;
/** Vertical {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
parallaxy?: float;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** X coordinate where layer content starts (for infinite maps) */
startx?: integer;
/** Y coordinate where layer content starts (for infinite maps) */
starty?: integer;
/** Hex-formatted {@link tint color} (#RRGGBB or #AARRGGBB) that is multiplied with any graphics drawn by this layer or any child layers (optional). */
tintcolor?: string;
/** Hex-formatted color (#RRGGBB) (optional). `imagelayer` only. */
transparentcolor?: string;
/** `tilelayer`, `objectgroup`, `imagelayer` or `group` */
type: string;
/** Whether layer is shown or hidden in editor */
visible: boolean;
/** Column count. Same as map width for fixed-size maps. */
width?: integer;
/** Horizontal layer offset in tiles. Always 0. */
x: integer;
/** Vertical layer offset in tiles. Always 0. */
y: integer;
};
export declare type TiledChunk = {
/** Array of `unsigned` `integer` (GIDs) or base64-encoded data */
data: Array<integer> | string;
/** Height in tiles */
height: integer;
/** Width in tiles */
width: integer;
/** X coordinate in tiles */
x: integer;
/** Y coordinate in tiles */
y: integer;
};
export declare type TiledObject = {
/** Used to mark an object as an ellipse */
ellipse?: boolean;
/** Global tile ID, only if object represents a tile */
gid?: integer;
/** Height in pixels. */
height: float;
/** Incremental ID, unique across all objects */
id: integer;
/** String assigned to name field in editor */
name: string;
/** Used to mark an object as a point */
point?: boolean;
/** Array of {@link TiledPoint}, in case the object is a polygon */
polygon?: Array<TiledPoint>;
/** Array of {@link TiledPoint}, in case the object is a polyline */
polyline?: Array<TiledPoint>;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** Angle in degrees clockwise */
rotation: float;
/** Reference to a template file, in case object is a {@link template instance} */
template?: string;
/** Only used for text objects */
text?: Text;
/** String assigned to type Tiledfield in editor */
type: string;
/** Whether object is shown in editor. */
visible: boolean;
/** Width in pixels. */
width: float;
/** X coordinate in pixels */
x: float;
/** Y coordinate in pixels */
y: float;
};
export declare type TiledText = {
/** Whether to use a bold font (default: `false`) */
bold: boolean;
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (default: `#000000`) */
color: string;
/** Font family (default: `sans-serif`) */
fontfamily: string;
/** Horizontal alignment (`center`, `right`, `justify` or `left` (default)) */
halign: string;
/** Whether to use an italic font (default: `false`) */
italic: boolean;
/** Whether to use kerning when placing characters (default: `true`) */
kerning: boolean;
/** Pixel size of font (default: 16) */
pixelsize: integer;
/** Whether to strike out the text (default: `false`) */
strikeout: boolean;
/** Text */
text: string;
/** Whether to underline the text (default: `false`) */
underline: boolean;
/** Vertical alignment (`center`, `bottom` or `top` (default)) */
valign: string;
/** Whether the text is wrapped within the object bounds (default: `false`) */
wrap: boolean;
};
export declare type TiledTileset = {
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
backgroundcolor?: string;
/** The number of tile columns in the tileset */
columns: integer;
/** GID corresponding to the first tile in the set */
firstgid: integer;
/** (optional) */
grid?: TiledGrid;
/** Image used for tiles in this set */
image: string;
/** Height of source image in pixels */
imageheight: integer;
/** Width of source image in pixels */
imagewidth: integer;
/** Buffer between image edge and first tile (pixels) */
margin: integer;
/** Name given to this tileset */
name: string;
/** Alignment to use for tile objects (`unspecified` (default), `topleft`, `top`, `topright`, `left`, `center`, `right`, `bottomleft`, `bottom` or `bottomright`) (since 1.4) */
objectalignment?: string;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** The external file that contains this tilesets data */
source?: string;
/** Spacing between adjacent tiles in image (pixels) */
spacing: integer;
/** Array of {@link TiledTerrain} (optional) */
terrains?: Array<TiledTerrain>;
/** The number of tiles in this tileset */
tilecount: integer;
/** The Tiled version used to save the file */
tiledversion: string;
/** Maximum height of tiles in this set */
tileheight: integer;
/** (optional) */
tileoffset?: TileOffset;
/** Array of {@link TiledTileDefinition} (optional) */
tiles?: Array<TiledTileDefinition>;
/** Maximum width of tiles in this set */
tilewidth: integer;
/** Allowed transformations (optional) */
transformations?: TiledTransformations;
/** Hex-formatted color (#RRGGBB) (optional) */
transparentcolor?: string;
/** `tileset` (for tileset files, since 1.0) */
type: string;
/** The JSON format version (previously a number, saved as string since 1.6) */
version: string;
/** Array of {@link TiledWangSet} (since 1.1.5) */
wangsets?: Array<TiledWangSet>;
};
export declare type TiledGrid = {
/** Cell height of tile grid */
height: integer;
/** `orthogonal` (default) or `isometric` */
orientation: string;
/** Cell width of tile grid */
width: integer;
};
export declare type TileOffset = {
/** Horizontal offset in pixels */
x: integer;
/** Vertical offset in pixels (positive is down) */
y: integer;
};
export declare type TiledTransformations = {
/** Tiles can be flipped horizontally */
hflip: boolean;
/** Tiles can be flipped vertically */
vflip: boolean;
/** Tiles can be rotated in 90-degree increments */
rotate: boolean;
/** Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations */
preferuntransformed: boolean;
};
export declare type TiledTileDefinition = {
/** Array of {@link TiledTiles} */
animation?: Array<TiledTileDefinition>;
/** Local ID of the tile */
id: integer;
/** Image representing this tile (optional) */
image?: string;
/** Height of the tile image in pixels */
imageheight?: integer;
/** Width of the tile image in pixels */
imagewidth?: integer;
/** Layer with type Tiled`objectgroup`, when collision shapes are specified (optional) */
objectgroup?: TiledLayer;
/** Percentage chance this tile is chosen when competing with others in the editor (optional) */
probability?: float;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** Index of terrain for each corner of tile (optional) */
terrain?: Array<integer>;
/** The type of the tile (optional) */
type?: string;
};
export declare type TiledFrame = {
/** Frame duration in milliseconds */
duration: integer;
/** Local tile ID representing this frame */
tileid: integer;
};
export declare type TiledTerrain = {
/** Name of terrain */
name: string;
/** Array of {@link TiledProperty} */
properties: Array<TiledProperty>;
/** Local ID of tile representing terrain */
tile: integer;
};
export declare type TiledWangSet = {
/** Array of {@link TiledWangColor} */
colors: Array<TiledWangColor>;
/** Name of the Wang set */
name: string;
/** Array of {@link TiledProperty} */
properties: Array<TiledProperty>;
/** Local ID of tile representing the Wang set */
tile: integer;
/** Array of {@link TiledWangTile} */
wangtiles: Array<TiledWangTile>;
};
export declare type TiledWangColor = {
/** Hex-formatted color (#RRGGBB or #AARRGGBB) */
color: string;
/** Name of the Wang color */
name: string;
/** Probability used when randomizing */
probability: float;
/** Array of {@link TiledProperty} */
properties: Array<TiledProperty>;
/** Local ID of tile representing the Wang color */
tile: integer;
};
export declare type TiledWangTile = {
/** Local ID of tile */
tileid: integer;
/** Array of Wang color indexes (`uchar[8]`) */
wangid: Array<integer>;
};
export declare type TiledObjectTemplate = {
/** `template` */
type: string;
/** External tileset used by the template (optional) */
tileset?: TiledTileset;
/** The object instantiated by this template */
object: Object;
};
export declare type TiledProperty = {
/** Name of the property */
name: string;
/** type of the property (`string` (default), `integer`, `float`, `boolean`, `color` or `file` (since 0.16, with `color` and `file` added in 0.17)) */
type: string;
/** Value of the property */
value: string | number;
};
export declare type TiledPoint = {
/** X coordinate in pixels */
x: float;
/** Y coordinate in pixels */
y: float;
};
//# sourceMappingURL=Tiled.d.ts.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,339 @@
import { float, integer } from '../model/CommonTypes';
/**
* Tiled JSON format (https://www.mapeditor.org/).
*/
export declare type TiledMap = {
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
backgroundcolor?: string;
/** The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default) */
compressionlevel: integer;
/** Number of tile rows */
height: integer;
/** Length of the side of a hex tile in pixels (hexagonal maps only) */
hexsidelength?: integer;
/** Whether the map has infinite dimensions */
infinite: boolean;
/** Array of {@link TiledLayer} */
layers: Array<TiledLayer>;
/** Auto-increments for each layer */
nextlayerid: integer;
/** Auto-increments for each placed object */
nextobjectid: integer;
/** `orthogonal`, `isometric`, `staggered` or `hexagonal` */
orientation: string;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** `right-down` (the default), `right-up`, `left-down` or `left-up` (currently only supported for orthogonal maps) */
renderorder: string;
/** `x` or `y` (staggered / hexagonal maps only) */
staggeraxis?: string;
/** `odd` or `even` (staggered / hexagonal maps only) */
staggerindex?: string;
/** The Tiled version used to save the file */
tiledversion: string;
/** Map grid height */
tileheight: integer;
/** Array of {@link TiledTileset} */
tilesets: Array<TiledTileset>;
/** Map grid width */
tilewidth: integer;
/** `map` (since 1.0) */
type: string;
/** The JSON format version (previously a number, saved as string since 1.6) */
version: string;
/** Number of tile columns */
width: integer;
};
export declare type TiledLayer = {
/** Array of {@link TiledChunk} (optional). `tilelayer` only. */
chunks?: Array<TiledChunk>;
/** `zlib`, `gzip`, `zstd` (since Tiled 1.3) or empty (default). `tilelayer` only. */
compression?: string;
/** Array of `unsigned`, `integer` (GIDs) or base64-encoded data. `tilelayer` only.*/
data?: Array<integer> | string;
/** `topdown` (default) or `index`. `objectgroup` only. */
draworder?: string;
/** `csv` (default) or `base64`. `tilelayer` only. */
encoding?: string;
/** Row count. Same as map height for fixed-size maps. */
height?: integer;
/** Incremental ID - unique across all layers */
id?: integer;
/** Image used by this layer. `imagelayer` only. */
image?: string;
/** Array of {@link TiledLayer}. `group` only. */
layers?: Array<TiledLayer>;
/** Name assigned to this layer */
name: string;
/** Array of {@link TiledObject}. `objectgroup` only. */
objects?: Array<TiledObject>;
/** Horizontal layer offset in pixels (default: 0) */
offsetx?: float;
/** Vertical layer offset in pixels (default: 0) */
offsety?: float;
/** Value between 0 and 1 */
opacity: float;
/** Horizontal {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
parallaxx?: float;
/** Vertical {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
parallaxy?: float;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** X coordinate where layer content starts (for infinite maps) */
startx?: integer;
/** Y coordinate where layer content starts (for infinite maps) */
starty?: integer;
/** Hex-formatted {@link tint color} (#RRGGBB or #AARRGGBB) that is multiplied with any graphics drawn by this layer or any child layers (optional). */
tintcolor?: string;
/** Hex-formatted color (#RRGGBB) (optional). `imagelayer` only. */
transparentcolor?: string;
/** `tilelayer`, `objectgroup`, `imagelayer` or `group` */
type: string;
/** Whether layer is shown or hidden in editor */
visible: boolean;
/** Column count. Same as map width for fixed-size maps. */
width?: integer;
/** Horizontal layer offset in tiles. Always 0. */
x: integer;
/** Vertical layer offset in tiles. Always 0. */
y: integer;
};
export declare type TiledChunk = {
/** Array of `unsigned` `integer` (GIDs) or base64-encoded data */
data: Array<integer> | string;
/** Height in tiles */
height: integer;
/** Width in tiles */
width: integer;
/** X coordinate in tiles */
x: integer;
/** Y coordinate in tiles */
y: integer;
};
export declare type TiledObject = {
/** The class of the object (renamed from type since 1.9, optional) */
class?: string;
/** Used to mark an object as an ellipse */
ellipse?: boolean;
/** Global tile ID, only if object represents a tile */
gid?: integer;
/** Height in pixels. */
height: float;
/** Incremental ID, unique across all objects */
id: integer;
/** String assigned to name field in editor */
name: string;
/** Used to mark an object as a point */
point?: boolean;
/** Array of {@link TiledPoint}, in case the object is a polygon */
polygon?: Array<TiledPoint>;
/** Array of {@link TiledPoint}, in case the object is a polyline */
polyline?: Array<TiledPoint>;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** Angle in degrees clockwise */
rotation: float;
/** Reference to a template file, in case object is a {@link template instance} */
template?: string;
/** Only used for text objects */
text?: Text;
/** Whether object is shown in editor. */
visible: boolean;
/** Width in pixels. */
width: float;
/** X coordinate in pixels */
x: float;
/** Y coordinate in pixels */
y: float;
};
export declare type TiledText = {
/** Whether to use a bold font (default: `false`) */
bold: boolean;
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (default: `#000000`) */
color: string;
/** Font family (default: `sans-serif`) */
fontfamily: string;
/** Horizontal alignment (`center`, `right`, `justify` or `left` (default)) */
halign: string;
/** Whether to use an italic font (default: `false`) */
italic: boolean;
/** Whether to use kerning when placing characters (default: `true`) */
kerning: boolean;
/** Pixel size of font (default: 16) */
pixelsize: integer;
/** Whether to strike out the text (default: `false`) */
strikeout: boolean;
/** Text */
text: string;
/** Whether to underline the text (default: `false`) */
underline: boolean;
/** Vertical alignment (`center`, `bottom` or `top` (default)) */
valign: string;
/** Whether the text is wrapped within the object bounds (default: `false`) */
wrap: boolean;
};
export declare type TiledTileset = {
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
backgroundcolor?: string;
/** The number of tile columns in the tileset */
columns: integer;
/** GID corresponding to the first tile in the set */
firstgid: integer;
/** (optional) */
grid?: TiledGrid;
/** Image used for tiles in this set */
image: string;
/** Height of source image in pixels */
imageheight: integer;
/** Width of source image in pixels */
imagewidth: integer;
/** Buffer between image edge and first tile (pixels) */
margin: integer;
/** Name given to this tileset */
name: string;
/** Alignment to use for tile objects (`unspecified` (default), `topleft`, `top`, `topright`, `left`, `center`, `right`, `bottomleft`, `bottom` or `bottomright`) (since 1.4) */
objectalignment?: string;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** The external file that contains this tilesets data */
source?: string;
/** Spacing between adjacent tiles in image (pixels) */
spacing: integer;
/** Array of {@link TiledTerrain} (optional) */
terrains?: Array<TiledTerrain>;
/** The number of tiles in this tileset */
tilecount: integer;
/** The Tiled version used to save the file */
tiledversion: string;
/** Maximum height of tiles in this set */
tileheight: integer;
/** (optional) */
tileoffset?: TileOffset;
/** Array of {@link TiledTileDefinition} (optional) */
tiles?: Array<TiledTileDefinition>;
/** Maximum width of tiles in this set */
tilewidth: integer;
/** Allowed transformations (optional) */
transformations?: TiledTransformations;
/** Hex-formatted color (#RRGGBB) (optional) */
transparentcolor?: string;
/** `tileset` (for tileset files, since 1.0) */
type: string;
/** The JSON format version (previously a number, saved as string since 1.6) */
version: string;
/** Array of {@link TiledWangSet} (since 1.1.5) */
wangsets?: Array<TiledWangSet>;
};
export declare type TiledGrid = {
/** Cell height of tile grid */
height: integer;
/** `orthogonal` (default) or `isometric` */
orientation: string;
/** Cell width of tile grid */
width: integer;
};
export declare type TileOffset = {
/** Horizontal offset in pixels */
x: integer;
/** Vertical offset in pixels (positive is down) */
y: integer;
};
export declare type TiledTransformations = {
/** Tiles can be flipped horizontally */
hflip: boolean;
/** Tiles can be flipped vertically */
vflip: boolean;
/** Tiles can be rotated in 90-degree increments */
rotate: boolean;
/** Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations */
preferuntransformed: boolean;
};
export declare type TiledTileDefinition = {
/** Array of {@link TiledTiles} */
animation?: Array<TiledTileDefinition>;
/** The class of the tile (renamed from type since 1.9, optional) */
class?: string;
/** Local ID of the tile */
id: integer;
/** Image representing this tile (optional) */
image?: string;
/** Height of the tile image in pixels */
imageheight?: integer;
/** Width of the tile image in pixels */
imagewidth?: integer;
/** Layer with type Tiled`objectgroup`, when collision shapes are specified (optional) */
objectgroup?: TiledLayer;
/** Percentage chance this tile is chosen when competing with others in the editor (optional) */
probability?: float;
/** Array of {@link TiledProperty} */
properties?: Array<TiledProperty>;
/** Index of terrain for each corner of tile (optional) */
terrain?: Array<integer>;
};
export declare type TiledFrame = {
/** Frame duration in milliseconds */
duration: integer;
/** Local tile ID representing this frame */
tileid: integer;
};
export declare type TiledTerrain = {
/** Name of terrain */
name: string;
/** Array of {@link TiledProperty} */
properties: Array<TiledProperty>;
/** Local ID of tile representing terrain */
tile: integer;
};
export declare type TiledWangSet = {
/** Array of {@link TiledWangColor} */
colors: Array<TiledWangColor>;
/** Name of the Wang set */
name: string;
/** Array of {@link TiledProperty} */
properties: Array<TiledProperty>;
/** Local ID of tile representing the Wang set */
tile: integer;
/** Array of {@link TiledWangTile} */
wangtiles: Array<TiledWangTile>;
};
export declare type TiledWangColor = {
/** Hex-formatted color (#RRGGBB or #AARRGGBB) */
color: string;
/** Name of the Wang color */
name: string;
/** Probability used when randomizing */
probability: float;
/** Array of {@link TiledProperty} */
properties: Array<TiledProperty>;
/** Local ID of tile representing the Wang color */
tile: integer;
};
export declare type TiledWangTile = {
/** Local ID of tile */
tileid: integer;
/** Array of Wang color indexes (`uchar[8]`) */
wangid: Array<integer>;
};
export declare type TiledObjectTemplate = {
/** `template` */
type: string;
/** External tileset used by the template (optional) */
tileset?: TiledTileset;
/** The object instantiated by this template */
object: Object;
};
export declare type TiledProperty = {
/** Name of the property */
name: string;
/** type of the property (`string` (default), `integer`, `float`, `boolean`, `color` or `file` (since 0.16, with `color` and `file` added in 0.17)) */
type: string;
/** Value of the property */
value: string | number;
};
export declare type TiledPoint = {
/** X coordinate in pixels */
x: float;
/** Y coordinate in pixels */
y: float;
};
//# sourceMappingURL=TiledFormat.d.ts.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,37 @@
import { integer } from '../model/CommonTypes';
import { TiledLayer } from './TiledFormat';
/**
* Decodes a layer data, which can sometimes be store as a compressed base64 string
* by Tiled.
* See https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#data.
* @param pako The zlib library.
* @param layer The layer data from a Tiled JSON.
* @returns The decoded layer data.
*/
export declare const decodeBase64LayerData: (
pako: any,
layer: TiledLayer
) => number[];
export declare type TiledGID = {
id: integer;
flippedHorizontally: boolean;
flippedVertically: boolean;
flippedDiagonally: boolean;
};
/**
* Extract information about the rotation of a tile from the tile id.
* @param globalTileUid The Tiled tile global uniq identifier.
* @returns The tile identifier and orientation.
*/
export declare const extractTileUidFlippedStates: (
globalTileUid: integer
) => TiledGID;
/**
* Tiled use 0 as null, we do too but it's black boxed.
* This is why the id needs to be decremented.
* @return the tile identifier used in {@link TilMapModel}.
*/
export declare const getTileIdFromTiledGUI: (
tiledGUI: number | undefined
) => number | undefined;
//# sourceMappingURL=TiledLoaderHelper.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TiledLoaderHelper.d.ts","sourceRoot":"","sources":["../../src/tiled/TiledLoaderHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,SAAU,GAAG,SAAS,UAAU,aAgDjE,CAAC;AAEF,oBAAY,QAAQ,GAAG;IACrB,EAAE,EAAE,OAAO,CAAC;IACZ,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,kBACvB,OAAO,KACrB,QAuBF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,aACtB,MAAM,GAAG,SAAS,KAC3B,MAAM,GAAG,SAAwD,CAAC"}

View File

@@ -0,0 +1,9 @@
import { EditableTileMap } from '../model/TileMapModel';
import { TiledMap } from './TiledFormat';
/**
* It creates a {@link EditableTileMap} from a Tiled JSON.
*/
export declare class TiledTileMapLoader {
static load(pako: any, tiledMap: TiledMap): EditableTileMap | null;
}
//# sourceMappingURL=TiledTileMapLoader.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TiledTileMapLoader.d.ts","sourceRoot":"","sources":["../../src/tiled/TiledTileMapLoader.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAOzC;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,GAAG,eAAe,GAAG,IAAI;CA2KnE"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=TiledTileMapLoader.spec.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TiledTileMapLoader.spec.d.ts","sourceRoot":"","sources":["../../src/tiled/TiledTileMapLoader.spec.ts"],"names":[],"mappings":""}

166
Extensions/TileMap/pako/dist/pako.d.ts vendored Normal file
View File

@@ -0,0 +1,166 @@
// Type definitions for pako 1.0
// Project: https://github.com/nodeca/pako
// Definitions by: Denis Cappellin <https://github.com/cappellin>
// Caleb Eggensperger <https://github.com/calebegg>
// Muhammet Öztürk <https://github.com/hlthi>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
export = Pako;
export as namespace pako;
declare namespace Pako {
enum FlushValues {
Z_NO_FLUSH = 0,
Z_PARTIAL_FLUSH = 1,
Z_SYNC_FLUSH = 2,
Z_FULL_FLUSH = 3,
Z_FINISH = 4,
Z_BLOCK = 5,
Z_TREES = 6,
}
enum StrategyValues {
Z_FILTERED = 1,
Z_HUFFMAN_ONLY = 2,
Z_RLE = 3,
Z_FIXED = 4,
Z_DEFAULT_STRATEGY = 0,
}
enum ReturnCodes {
Z_OK = 0,
Z_STREAM_END = 1,
Z_NEED_DICT = 2,
Z_ERRNO = -1,
Z_STREAM_ERROR = -2,
Z_DATA_ERROR = -3,
Z_BUF_ERROR = -5,
}
interface DeflateOptions {
level?: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined;
windowBits?: number | undefined;
memLevel?: number | undefined;
strategy?: StrategyValues | undefined;
dictionary?: any;
raw?: boolean | undefined;
to?: 'string' | undefined;
chunkSize?: number | undefined;
gzip?: boolean | undefined;
header?: Header | undefined;
}
interface DeflateFunctionOptions {
level?: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined;
windowBits?: number | undefined;
memLevel?: number | undefined;
strategy?: StrategyValues | undefined;
dictionary?: any;
raw?: boolean | undefined;
to?: 'string' | undefined;
}
interface InflateOptions {
windowBits?: number | undefined;
dictionary?: any;
raw?: boolean | undefined;
to?: 'string' | undefined;
chunkSize?: number | undefined;
}
interface InflateFunctionOptions {
windowBits?: number | undefined;
raw?: boolean | undefined;
to?: 'string' | undefined;
}
interface Header {
text?: boolean | undefined;
time?: number | undefined;
os?: number | undefined;
extra?: number[] | undefined;
name?: string | undefined;
comment?: string | undefined;
hcrc?: boolean | undefined;
}
type Data = Uint8Array | number[] | string;
/**
* Compress data with deflate algorithm and options.
*/
function deflate(
data: Data,
options: DeflateFunctionOptions & { to: 'string' }
): string;
function deflate(data: Data, options?: DeflateFunctionOptions): Uint8Array;
/**
* The same as deflate, but creates raw data, without wrapper (header and adler32 crc).
*/
function deflateRaw(
data: Data,
options: DeflateFunctionOptions & { to: 'string' }
): string;
function deflateRaw(data: Data, options?: DeflateFunctionOptions): Uint8Array;
/**
* The same as deflate, but create gzip wrapper instead of deflate one.
*/
function gzip(
data: Data,
options: DeflateFunctionOptions & { to: 'string' }
): string;
function gzip(data: Data, options?: DeflateFunctionOptions): Uint8Array;
/**
* Decompress data with inflate/ungzip and options. Autodetect format via wrapper header
* by default. That's why we don't provide separate ungzip method.
*/
function inflate(
data: Data,
options: InflateFunctionOptions & { to: 'string' }
): string;
function inflate(data: Data, options?: InflateFunctionOptions): Uint8Array;
/**
* The same as inflate, but creates raw data, without wrapper (header and adler32 crc).
*/
function inflateRaw(
data: Data,
options: InflateFunctionOptions & { to: 'string' }
): string;
function inflateRaw(data: Data, options?: InflateFunctionOptions): Uint8Array;
/**
* Just shortcut to inflate, because it autodetects format by header.content. Done for convenience.
*/
function ungzip(
data: Data,
options: InflateFunctionOptions & { to: 'string' }
): string;
function ungzip(data: Data, options?: InflateFunctionOptions): Uint8Array;
// https://github.com/nodeca/pako/blob/893381abcafa10fa2081ce60dae7d4d8e873a658/lib/deflate.js
class Deflate {
constructor(options?: DeflateOptions);
err: ReturnCodes;
msg: string;
result: Uint8Array | number[];
onData(chunk: Data): void;
onEnd(status: number): void;
push(data: Data | ArrayBuffer, mode?: FlushValues | boolean): boolean;
}
// https://github.com/nodeca/pako/blob/893381abcafa10fa2081ce60dae7d4d8e873a658/lib/inflate.js
class Inflate {
constructor(options?: InflateOptions);
header?: Header | undefined;
err: ReturnCodes;
msg: string;
result: Data;
onData(chunk: Data): void;
onEnd(status: number): void;
push(data: Data | ArrayBuffer, mode?: FlushValues | boolean): boolean;
}
}

View File

@@ -1,470 +0,0 @@
// @ts-check
(function (root, factory) {
// @ts-ignore
if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
// CommonJS
factory(exports);
} else {
// Browser globals
// @ts-ignore
factory((root.PixiTileMapHelper = {}));
}
})(typeof self !== 'undefined' ? self : this, function (exports) {
/** @typedef {GlobalPIXIModule.PIXI.Texture} PIXI.Texture */
/** @typedef {GlobalPIXIModule.PIXI.BaseTexture} PIXI.BaseTexture */
/** @typedef {GlobalPIXIModule.PIXI.Rectangle} PIXI.Rectangle */
const PIXI = GlobalPIXIModule.PIXI;
/**
* Information about one or more tiles. Loosely based on
* https://doc.mapeditor.org/en/stable/reference/json-map-format/#tile-definition.
*
* @typedef {{
"id": number,
"terrain"?: Array<number>,
"animation"?: Array<{duration: number, tileid: number}>
}} TiledDataTile
*/
/**
* Information about a layer. Loosely based on
* https://doc.mapeditor.org/en/stable/reference/json-map-format/#layer.
*
* @typedef {{
"compression"?:"zlib" | "gzip" | "zstd",
"data":Array<number> | string,
"encoding"?:"base64",
"height":number,
"id":number,
"name": string,
"opacity": number,
"type": string,
"visible":boolean,
"width":number,
"objects": Array<{ gid: number, x: number, y: number, visible: boolean }>
}} TiledDataLayer
*/
/**
* Data to render a tile map. Loosely based on the merge of a Tiled
* map and tileset.
*
* @typedef {{
width: number,
height: number,
tileWidth: number,
tileHeight: number,
atlasTexture: PIXI.BaseTexture,
textureCache: Object<number, PIXI.Texture | null>,
layers: Array<TiledDataLayer>,
tiles: Array<TiledDataTile>,
}} GenericPixiTileMapData
*/
/**
* The Tilesets that are ready to be used
* with Pixi Tilemap, indexed by their id.
* @type {Object<string, GenericPixiTileMapData>}
*/
const loadedGenericPixiTileMapData = {};
/**
* Parse a Tiled map JSON file,
* exported from Tiled (https://www.mapeditor.org/)
* into a generic tile map data (`GenericPixiTileMapData`).
*
* @param {Object} tiledData A JS object representing a map exported from Tiled.
* @param {?PIXI.BaseTexture} atlasTexture
* @param {(textureName: string) => PIXI.BaseTexture} getTexture A getter to load a texture. Used if atlasTexture is not specified.
* @returns {?GenericPixiTileMapData}
*/
const parseTiledData = (tiledData, atlasTexture, getTexture) => {
if (!tiledData.tiledversion) {
console.warn(
"The loaded Tiled map does not contain a 'tiledversion' key. Are you sure this file has been exported from Tiled (mapeditor.org)?"
);
return null;
}
// We only handle tileset embedded in the tilemap. Warn if it's not the case.
if (!tiledData.tilesets.length || 'source' in tiledData.tilesets[0]) {
console.warn(
"The loaded Tiled map seems not to contain any tileset data (nothing in 'tilesets' key)."
);
return null;
}
const {
tilewidth,
tileheight,
tilecount,
tiles,
image,
columns,
spacing,
margin,
} = tiledData.tilesets[0];
if (!atlasTexture) atlasTexture = getTexture(image);
// We try to detect what size Tiled is expecting.
const rows = tilecount / columns;
const expectedAtlasWidth =
tilewidth * columns + spacing * (columns - 1) + margin * 2;
const expectedAtlasHeight =
tileheight * rows + spacing * (rows - 1) + margin * 2;
if (
(atlasTexture.width !== 1 && expectedAtlasWidth !== atlasTexture.width) ||
(atlasTexture.height !== 1 && expectedAtlasHeight !== atlasTexture.height)
) {
const expectedSize = expectedAtlasWidth + 'x' + expectedAtlasHeight;
const actualSize = atlasTexture.width + 'x' + atlasTexture.height;
console.warn(
'It seems the atlas file was resized, which is not supported. It should be ' +
expectedSize +
"px, but it's " +
actualSize +
' px.'
);
return null;
}
// Prepare the textures pointing to the base "Atlas" Texture for each tile.
// Note that this cache can be augmented later with rotated/flipped
// versions of the tile textures.
/** @type {Object<number, PIXI.Texture | null>} */
const textureCache = {};
for (let frame = 0; frame <= tilecount; frame++) {
const columnMultiplier = Math.floor((frame - 1) % columns);
const rowMultiplier = Math.floor((frame - 1) / columns);
const x = margin + columnMultiplier * (tilewidth + spacing);
const y = margin + rowMultiplier * (tileheight + spacing);
try {
const rect = new PIXI.Rectangle(x, y, tilewidth, tileheight);
// @ts-ignore - atlasTexture is never null here.
const texture = new PIXI.Texture(atlasTexture, rect);
textureCache[frame] = texture;
} catch (error) {
console.error(
'An error occurred while creating a PIXI.Texture to be used in a TileMap:',
error
);
textureCache[frame] = null;
}
}
/** @type {GenericPixiTileMapData} */
const tileMapData = {
width: atlasTexture.width,
height: atlasTexture.height,
tileWidth: tilewidth,
tileHeight: tileheight,
atlasTexture: atlasTexture,
textureCache: textureCache,
layers: tiledData.layers,
tiles: tiles,
};
return tileMapData;
};
/**
* Decodes a layer data, which can sometimes be store as a compressed base64 string
* by Tiled.
* See https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#data.
*/
const decodeBase64LayerData = (layer, pako) => {
const { data, compression } = layer;
let index = 4;
const decodedData = [];
let step1 = atob(data)
.split('')
.map(function (x) {
return x.charCodeAt(0);
});
try {
const decodeString = (str, index) =>
(str.charCodeAt(index) +
(str.charCodeAt(index + 1) << 8) +
(str.charCodeAt(index + 2) << 16) +
(str.charCodeAt(index + 3) << 24)) >>>
0;
const decodeArray = (arr, index) =>
(arr[index] +
(arr[index + 1] << 8) +
(arr[index + 2] << 16) +
(arr[index + 3] << 24)) >>>
0;
if (compression === 'zlib') {
const binData = new Uint8Array(step1);
step1 = pako.inflate(binData);
while (index <= step1.length) {
decodedData.push(decodeArray(step1, index - 4));
index += 4;
}
} else if (compression === 'zstd') {
console.error(
'Zstandard compression is not supported for layers in a Tilemap. Use instead zlib compression or no compression.'
);
return null;
} else {
while (index <= step1.length) {
decodedData.push(decodeString(step1, index - 4));
index += 4;
}
}
return decodedData;
} catch (error) {
console.error(
'Failed to decompress and unzip base64 layer.data string',
error
);
return null;
}
};
/**
* Extract information about the rotation of a tile from the tile id.
* @param {number} globalTileUid
* @returns {[number, boolean, boolean, boolean]}
*/
const extractTileUidFlippedStates = (globalTileUid) => {
const FLIPPED_HORIZONTALLY_FLAG = 0x80000000;
const FLIPPED_VERTICALLY_FLAG = 0x40000000;
const FLIPPED_DIAGONALLY_FLAG = 0x20000000;
const flippedHorizontally = globalTileUid & FLIPPED_HORIZONTALLY_FLAG;
const flippedVertically = globalTileUid & FLIPPED_VERTICALLY_FLAG;
const flippedDiagonally = globalTileUid & FLIPPED_DIAGONALLY_FLAG;
const tileUid =
globalTileUid &
~(
FLIPPED_HORIZONTALLY_FLAG |
FLIPPED_VERTICALLY_FLAG |
FLIPPED_DIAGONALLY_FLAG
);
return [tileUid, !!flippedHorizontally, !!flippedVertically, !!flippedDiagonally];
};
/**
* Return the texture to use for the tile with the specified uid, which can contains
* information about rotation in bits 32, 31 and 30
* (see https://doc.mapeditor.org/en/stable/reference/tmx-map-format/).
*
* @param {Object<number, PIXI.Texture | null>} textureCache
* @param {number} globalTileUid
* @returns {?PIXI.Texture}
*/
const findTileTextureInCache = (textureCache, globalTileUid) => {
if (globalTileUid === 0) return null;
if (textureCache[globalTileUid]) {
return textureCache[globalTileUid];
}
// If the texture is not in the cache, it's potentially because its ID
// is a flipped/rotated version of another ID.
const flippedStates = extractTileUidFlippedStates(globalTileUid);
const tileUid = flippedStates[0];
const flippedHorizontally = flippedStates[1];
const flippedVertically = flippedStates[2];
const flippedDiagonally = flippedStates[3];
if (tileUid === 0) return null;
// If the tile still can't be found in the cache, it means the ID we got
// is invalid.
const unflippedTexture = textureCache[tileUid];
if (!unflippedTexture) return null;
// Clone the unflipped texture and save it in the cache
const frame = unflippedTexture.frame.clone();
const orig = unflippedTexture.orig.clone();
if (flippedDiagonally) {
const width = orig.width;
orig.width = orig.height;
orig.height = width;
}
const trim = orig.clone();
// Get the rotation "D8" number.
// See https://pixijs.io/examples/#/textures/texture-rotate.js
let rotate = 0;
if (flippedDiagonally) {
rotate = 10;
if (!flippedHorizontally && flippedVertically) {
rotate = 2;
} else if (flippedHorizontally && !flippedVertically) {
rotate = 6;
} else if (flippedHorizontally && flippedVertically) {
rotate = 14;
}
} else {
rotate = 0;
if (!flippedHorizontally && flippedVertically) {
rotate = 8;
} else if (flippedHorizontally && !flippedVertically) {
rotate = 12;
} else if (flippedHorizontally && flippedVertically) {
rotate = 4;
}
}
const flippedTexture = new PIXI.Texture(
unflippedTexture.baseTexture,
frame,
orig,
trim,
rotate
);
return (textureCache[globalTileUid] = flippedTexture);
};
/**
* Re-renders the tilemap whenever its rendering settings have been changed
*
* @param {any} pixiTileMap
* @param {GenericPixiTileMapData} genericTileMapData
* @param {'index' | 'visible' | 'all'} displayMode What to display: only a single layer (`index`), only visible layers (`visible`) or everyhing (`all`).
* @param {number} layerIndex If `displayMode` is set to `index`, the layer index to be displayed.
* @param {any} pako The Pako library object, to decompress the layer data.
*/
exports.updatePixiTileMap = (
pixiTileMap,
genericTileMapData,
displayMode,
layerIndex,
pako
) => {
if (!pixiTileMap || !genericTileMapData) return;
pixiTileMap.clear();
genericTileMapData.layers.forEach(function (layer, index) {
if (displayMode === 'index' && layerIndex !== index) return;
else if (displayMode === 'visible' && !layer.visible) return;
if (layer.type === 'objectgroup') {
layer.objects.forEach(function (object) {
const { gid, x, y, visible } = object;
if (displayMode === 'visible' && !visible) return;
if (genericTileMapData.textureCache[gid]) {
pixiTileMap.addFrame(
genericTileMapData.textureCache[gid],
x,
y - genericTileMapData.tileHeight
);
}
});
} else if (layer.type === 'tilelayer') {
let tileSlotIndex = 0;
let layerData = layer.data;
if (layer.encoding === 'base64') {
// @ts-ignore
layerData = decodeBase64LayerData(layer, pako);
if (!layerData) {
console.warn('Failed to uncompress layer.data');
return;
}
}
for (let i = 0; i < layer.height; i++) {
for (let j = 0; j < layer.width; j++) {
const xPos = genericTileMapData.tileWidth * j;
const yPos = genericTileMapData.tileHeight * i;
// The "globalTileUid" is the tile UID with encoded
// bits about the flipping/rotation of the tile.
/** @type {number} */
// @ts-ignore
const globalTileUid = layerData[tileSlotIndex];
// Extract the tile UID and the texture.
const tileUid = extractTileUidFlippedStates(globalTileUid)[0];
const tileTexture = findTileTextureInCache(
genericTileMapData.textureCache,
globalTileUid
);
if (tileTexture) {
const tileData =
genericTileMapData.tiles &&
genericTileMapData.tiles.find(function (tile) {
return tile.id === tileUid - 1;
});
const pixiTilemapFrame = pixiTileMap.addFrame(
tileTexture,
xPos,
yPos
);
// Animated tiles have a limitation:
// they are only able to use frames arranged horizontally one next
// to each other on the atlas.
if (tileData && tileData.animation) {
pixiTilemapFrame.tileAnimX(
genericTileMapData.tileWidth,
tileData.animation.length
);
}
}
tileSlotIndex += 1;
}
}
}
});
};
/**
* Load the given data, exported from Tiled, into a generic tilemap data (`GenericPixiTileMapData`),
* which can then be used to update a PIXI.Tilemap (see `updatePixiTileMap`).
*
* Later on, this could potentially be refactored to support other data structures
* (LDtk, for example: https://github.com/deepnight/ldtk).
*
* @param {(textureName: string) => PIXI.BaseTexture} getTexture A getter to load a texture. Used if atlasTexture is not specified.
* @param {Object} tiledData A JS object representing a map exported from Tiled.
* @param {string} atlasImageResourceName The name of the resource to pass to `getTexture` to load the atlas.
* @param {string} tilemapResourceName The name of the tilemap resource - used to index internally the loaded tilemap data.
* @param {string} tilesetResourceName The name of the tileset resource - used to index internally the loaded tilemap data.
* @returns {?GenericPixiTileMapData}
*/
exports.loadPixiTileMapData = (
getTexture,
tiledData,
atlasImageResourceName,
tilemapResourceName,
tilesetResourceName
) => {
const requestedTileMapDataId =
tilemapResourceName +
'@' +
tilesetResourceName +
'@' +
atlasImageResourceName;
// If the tilemap data is already in the cache, use it directly.
if (loadedGenericPixiTileMapData[requestedTileMapDataId]) {
return loadedGenericPixiTileMapData[requestedTileMapDataId];
}
const atlasTexture = atlasImageResourceName
? getTexture(atlasImageResourceName)
: null;
const genericPixiTileMapData = parseTiledData(
tiledData,
atlasTexture,
getTexture
);
if (genericPixiTileMapData)
loadedGenericPixiTileMapData[
requestedTileMapDataId
] = genericPixiTileMapData;
return genericPixiTileMapData;
};
});

View File

@@ -0,0 +1,29 @@
import {
CanvasTileRenderer,
CompositeRectTileLayer,
GraphicsLayer,
IMultiTextureOptions,
MultiTextureResource,
RectTileGeom,
RectTileLayer,
RectTileShader,
TileRenderer,
ZLayer,
} from './pixi-tilemap';
declare global {
namespace PIXI {
export namespace tilemap {
export { CanvasTileRenderer };
export { CompositeRectTileLayer };
export { GraphicsLayer };
export { IMultiTextureOptions };
export { MultiTextureResource };
export { RectTileGeom };
export { RectTileLayer };
export { RectTileShader };
export { TileRenderer };
export { ZLayer };
}
}
}

View File

@@ -0,0 +1,262 @@
import PIXI = GlobalPIXIModule.PIXI;
export declare class CanvasTileRenderer {
renderer: PIXI.Renderer;
tileAnim: number[];
dontUseTransform: boolean;
constructor(renderer: PIXI.Renderer);
}
export declare class CompositeRectTileLayer extends PIXI.Container {
constructor(
zIndex?: number,
bitmaps?: Array<PIXI.Texture>,
texPerChild?: number
);
z: number;
// @ts-ignore Maybe it's a compatibility issue with the PIXI version we are using
zIndex: number;
modificationMarker: number;
shadowColor: Float32Array;
_globalMat: PIXI.Matrix;
_lastLayer: RectTileLayer;
texPerChild: number;
tileAnim: number[];
initialize(
zIndex?: number,
bitmaps?: Array<PIXI.Texture>,
texPerChild?: number
): void;
setBitmaps(bitmaps: Array<PIXI.Texture>): void;
clear(): void;
addRect(
textureIndex: number,
u: number,
v: number,
x: number,
y: number,
tileWidth: number,
tileHeight: number,
animX?: number,
animY?: number,
rotate?: number,
animWidth?: number,
animHeight?: number
): this;
tileRotate(rotate: number): this;
tileAnimX(offset: number, count: number): this;
tileAnimY(offset: number, count: number): this;
addFrame(
texture_: PIXI.Texture | String | number,
x: number,
y: number,
animX?: number,
animY?: number,
animWidth?: number,
animHeight?: number
): this;
renderCanvas(renderer: any): void;
render(renderer: PIXI.Renderer): void;
isModified(anim: boolean): boolean;
clearModify(): void;
}
export declare const Constant: {
maxTextures: number;
bufferSize: number;
boundSize: number;
boundCountPerBuffer: number;
use32bitIndex: boolean;
SCALE_MODE: PIXI.SCALE_MODES;
DO_CLEAR: boolean;
};
export declare function fillSamplers(
shader: TilemapShader,
maxTextures: number
): void;
export declare function generateFragmentSrc(
maxTextures: number,
fragmentSrc: string
): string;
export declare function generateSampleSrc(maxTextures: number): string;
export declare class GraphicsLayer extends PIXI.Graphics {
constructor(zIndex: number);
renderCanvas(renderer: any): void;
isModified(anim: boolean): boolean;
clearModify(): void;
}
export declare interface IMultiTextureOptions {
boundCountPerBuffer: number;
boundSize: number;
bufferSize: number;
DO_CLEAR?: boolean;
}
export declare class MultiTextureResource extends PIXI.Resource {
constructor(options: IMultiTextureOptions);
DO_CLEAR: boolean;
boundSize: number;
_clearBuffer: Uint8Array;
bind(baseTexture: PIXI.BaseTexture): void;
baseTex: PIXI.BaseTexture;
boundSprites: Array<PIXI.Sprite>;
dirties: Array<number>;
setTexture(ind: number, texture: PIXI.Texture): void;
upload(
renderer: PIXI.Renderer,
texture: PIXI.BaseTexture,
glTexture: PIXI.GLTexture
): boolean;
}
export declare const pixi_tilemap: {
CanvasTileRenderer: typeof CanvasTileRenderer;
CompositeRectTileLayer: typeof CompositeRectTileLayer;
Constant: {
maxTextures: number;
bufferSize: number;
boundSize: number;
boundCountPerBuffer: number;
use32bitIndex: boolean;
SCALE_MODE: PIXI.SCALE_MODES;
DO_CLEAR: boolean;
};
GraphicsLayer: typeof GraphicsLayer;
MultiTextureResource: typeof MultiTextureResource;
RectTileLayer: typeof RectTileLayer;
TilemapShader: typeof TilemapShader;
RectTileShader: typeof RectTileShader;
RectTileGeom: typeof RectTileGeom;
TileRenderer: typeof TileRenderer;
ZLayer: typeof ZLayer;
};
export declare const POINT_STRUCT_SIZE = 12;
export declare class RectTileGeom extends PIXI.Geometry {
vertSize: number;
vertPerQuad: number;
stride: number;
lastTimeAccess: number;
constructor();
buf: PIXI.Buffer;
}
export declare class RectTileLayer extends PIXI.Container {
constructor(zIndex: number, texture: PIXI.Texture | Array<PIXI.Texture>);
// @ts-ignore Maybe it's a compatibility issue with the PIXI version we are using
zIndex: number;
modificationMarker: number;
_$_localBounds: PIXI.Bounds;
shadowColor: Float32Array;
_globalMat: PIXI.Matrix;
pointsBuf: Array<number>;
hasAnim: boolean;
textures: Array<PIXI.Texture>;
offsetX: number;
offsetY: number;
compositeParent: boolean;
initialize(
zIndex: number,
textures: PIXI.Texture | Array<PIXI.Texture>
): void;
clear(): void;
addFrame(
texture_: PIXI.Texture | String | number,
x: number,
y: number,
animX: number,
animY: number
): boolean;
addRect(
textureIndex: number,
u: number,
v: number,
x: number,
y: number,
tileWidth: number,
tileHeight: number,
animX?: number,
animY?: number,
rotate?: number,
animCountX?: number,
animCountY?: number
): this;
tileRotate(rotate: number): void;
tileAnimX(offset: number, count: number): void;
tileAnimY(offset: number, count: number): void;
renderCanvas(renderer: any): void;
renderCanvasCore(renderer: any): void;
vbId: number;
vb: RectTileGeom;
vbBuffer: ArrayBuffer;
vbArray: Float32Array;
vbInts: Uint32Array;
destroyVb(): void;
render(renderer: PIXI.Renderer): void;
renderWebGLCore(renderer: PIXI.Renderer, plugin: TileRenderer): void;
isModified(anim: boolean): boolean;
clearModify(): void;
protected _calculateBounds(): void;
getLocalBounds(rect?: PIXI.Rectangle): PIXI.Rectangle;
destroy(options?: any): void;
}
export declare class RectTileShader extends TilemapShader {
constructor(maxTextures: number);
}
export declare abstract class TilemapShader extends PIXI.Shader {
maxTextures: number;
constructor(maxTextures: number, shaderVert: string, shaderFrag: string);
}
export declare class TileRenderer extends PIXI.ObjectRenderer {
renderer: PIXI.Renderer;
gl: WebGLRenderingContext;
sn: number;
indexBuffer: PIXI.Buffer;
ibLen: number;
tileAnim: number[];
texLoc: Array<number>;
rectShader: RectTileShader;
texResources: Array<MultiTextureResource>;
constructor(renderer: PIXI.Renderer);
initBounds(): void;
bindTexturesWithoutRT(
renderer: PIXI.Renderer,
shader: TilemapShader,
textures: Array<PIXI.Texture>
): void;
bindTextures(
renderer: PIXI.Renderer,
shader: TilemapShader,
textures: Array<PIXI.Texture>
): void;
start(): void;
createVb(): RectTileGeom;
checkIndexBuffer(size: number, vb?: RectTileGeom): void;
getShader(): TilemapShader;
destroy(): void;
}
export declare class ZLayer extends PIXI.Container {
constructor(tilemap: PIXI.Container, zIndex: number);
tilemap: any;
z: number;
// @ts-ignore Maybe it's a compatibility issue with the PIXI version we are using
zIndex: number;
_previousLayers: number;
canvasBuffer: HTMLCanvasElement;
_tempRender: any;
_lastAnimationFrame: number;
layerTransform: PIXI.Matrix;
clear(): void;
cacheIfDirty(): void;
renderCanvas(renderer: any): void;
}

View File

@@ -0,0 +1,238 @@
// @ts-check
describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
const createScene = (framePerSecond = 60) => {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
// @ts-ignore - missing properties.
properties: { windowWidth: 800, windowHeight: 600 },
resources: {
resources: [
{
file: 'base/tests-utils/simple-tiled-map/SmallTiledMap.json',
kind: 'json',
metadata: '',
name: 'SmallTiledMap.json',
userAdded: true,
alwaysLoaded: true,
},
{
file: 'base/tests-utils/simple-tiled-map/MiniTiledSet.json',
kind: 'json',
metadata: '',
name: 'MiniTiledSet.json',
userAdded: true,
alwaysLoaded: true,
},
],
},
});
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [
{
name: '',
visibility: true,
effects: [],
cameras: [],
ambientLightColorR: 0,
ambientLightColorG: 0,
ambientLightColorB: 0,
isLightingLayer: false,
followBaseLayerCamera: true,
},
],
variables: [],
r: 0,
v: 0,
b: 0,
mangledName: 'Scene1',
name: 'Scene1',
stopSoundsOnStartup: false,
title: '',
behaviorsSharedData: [],
objects: [],
instances: [],
});
setFramesPerSecond(runtimeScene, framePerSecond);
return runtimeScene;
};
const setFramesPerSecond = (runtimeScene, framePerSecond) => {
runtimeScene._timeManager.getElapsedTime = function () {
return 1000 / framePerSecond;
};
};
const addTileMapCollisionMask = (runtimeScene) => {
const tileMap = new gdjs.TileMapCollisionMaskRuntimeObject(runtimeScene, {
name: 'tilemap',
type: 'TileMap::CollisionMask',
behaviors: [],
effects: [],
content: {
tilemapJsonFile: 'SmallTiledMap.json',
tilesetJsonFile: 'MiniTiledSet.json',
layerIndex: 0,
collisionMaskTag: 'obstacle',
debugMode: false,
fillColor: '#ffffff',
outlineColor: '#ffffff',
fillOpacity: 1,
outlineOpacity: 1,
outlineSize: 1,
},
});
runtimeScene.addObject(tileMap);
return tileMap;
};
const addObject = (runtimeScene) => {
const object = new gdjs.TestRuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [],
effects: [],
variables: [],
});
object.setCustomWidthAndHeight(8, 8);
runtimeScene.addObject(object);
return object;
};
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
let runtimeScene;
/**
* @type {gdjs.TileMapCollisionMaskRuntimeObject}
*/
let tileMap;
beforeEach(async function () {
runtimeScene = createScene();
tileMap = addTileMapCollisionMask(runtimeScene);
// TODO find a clean way to wait for the json to be read.
for (
let index = 0;
index < 200 && tileMap._collisionTileMap.getDimensionX() === 0;
index++
) {
await delay(5);
}
if (tileMap._collisionTileMap.getDimensionX() === 0) {
throw new Error('Timeout reading the tile map JSON file.');
}
});
it('can be measured', function () {
tileMap.setPosition(100, 200);
expect(tileMap.getWidth()).to.be(32);
expect(tileMap.getHeight()).to.be(16);
expect(tileMap.getCenterX()).to.be(16);
expect(tileMap.getCenterY()).to.be(8);
});
/**
* insideObject usually use the AABB of the object.
* But, in case of a tile map, it makes more sense to look each tile individually.
* It returns true when there is an hitbox in the tile.
*/
it('can detect a point inside the collision mask', function () {
tileMap.setPosition(100, 200);
// The point is in the black square with an hitbox.
expect(tileMap.insideObject(104, 204)).to.be(true);
expect(tileMap.isCollidingWithPoint(104, 204)).to.be(true);
// The point is in wite square without any hitbox.
expect(tileMap.insideObject(112, 212)).to.be(false);
expect(tileMap.isCollidingWithPoint(112, 212)).to.be(false);
// The point is in black triangle part of the square that has an hitbox.
expect(tileMap.insideObject(102, 210)).to.be(true);
expect(tileMap.isCollidingWithPoint(102, 210)).to.be(true);
// The point is in white triangle part of the square that has no hitbox.
expect(tileMap.insideObject(106, 214)).to.be(true);
expect(tileMap.isCollidingWithPoint(106, 214)).to.be(false);
});
it('can detect collisions with an object', function () {
tileMap.setPosition(100, 200);
const object = addObject(runtimeScene);
object.setPosition(96, 196);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
object.setPosition(90, 190);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
object.setPosition(115, 207);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
object.setPosition(116, 208);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
it('can check collisions with an object on empty tiles without any issue', function () {
tileMap.setPosition(100, 200);
const object = addObject(runtimeScene);
object.setPosition(116, 208);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
it('can detect collisions with an object on flipped tiles', function () {
tileMap.setPosition(100, 200);
const object = addObject(runtimeScene);
// The object is over the black triangle.
object.setPosition(118, 214);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
// The object is over the red triangle without touching a black polygon.
object.setPosition(130, 204);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
it("can detect collisions with an object when it's rotated", function () {
tileMap.setPosition(100, 200);
tileMap.setAngle(90);
const object = addObject(runtimeScene);
object.setPosition(123, 185);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
object.setPosition(124, 184);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
it('can detect collisions with an object when it has a custom size', function () {
tileMap.setPosition(100, 200);
tileMap.setWidth(2 * tileMap.getWidth());
tileMap.setHeight(2 * tileMap.getHeight());
const object = addObject(runtimeScene);
object.setPosition(163, 231);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
object.setPosition(164, 232);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
});

View File

@@ -0,0 +1,472 @@
/// <reference path="helper/TileMapHelper.d.ts" />
namespace gdjs {
const logger = new gdjs.Logger('Tilemap object');
/**
* An object that handle hitboxes for a tile map.
* @extends gdjs.RuntimeObject
*/
export class TileMapCollisionMaskRuntimeObject extends gdjs.RuntimeObject {
private _tilemapJsonFile: string;
private _tilesetJsonFile: string;
private _renderer: gdjs.TileMap.TileMapCollisionMaskRenderer;
_collisionTileMap: gdjs.TileMap.TransformedCollisionTileMap;
/**
* The tiles are filtered according to this tag.
*
* This allows have multiple objects with different usage
* for the same tile map.
* For instance, platforms, jumpthru, ladder, spike, water...
*/
private _collisionMaskTag: string;
private _tileMapManager: gdjs.TileMap.TileMapRuntimeManager;
/**
* When set to true, the hitboxes will be shown.
*/
_debugMode: boolean;
_fillColor: integer;
_outlineColor: integer;
_fillOpacity: float;
_outlineOpacity: float;
_outlineSize: float;
/**
* If the owner moves, the hitboxes vertices
* will have to be transformed again.
*/
private _transformationIsUpToDate: boolean = false;
constructor(runtimeScene: gdjs.RuntimeScene, objectData) {
super(runtimeScene, objectData);
this._tilemapJsonFile = objectData.content.tilemapJsonFile;
this._tilesetJsonFile = objectData.content.tilesetJsonFile;
this._collisionMaskTag = objectData.content.collisionMaskTag;
this._debugMode = objectData.content.debugMode;
this._fillColor = gdjs.rgbOrHexStringToNumber(
objectData.content.fillColor
);
this._outlineColor = gdjs.rgbOrHexStringToNumber(
objectData.content.outlineColor
);
this._fillOpacity = objectData.content.fillOpacity;
this._outlineOpacity = objectData.content.outlineOpacity;
this._outlineSize = objectData.content.outlineSize;
this._tileMapManager = gdjs.TileMap.TileMapRuntimeManager.getManager(
runtimeScene
);
const editableTileMap = new TileMapHelper.EditableTileMap(
1,
1,
0,
0,
new Map()
);
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
editableTileMap,
this._collisionMaskTag
);
this._renderer = new gdjs.TileMap.TileMapCollisionMaskRenderer(
this,
runtimeScene
);
this._updateTileMap();
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
}
getRendererObject() {
return this._renderer.getRendererObject();
}
getVisibilityAABB() {
return null;
}
updateFromObjectData(oldObjectData: any, newObjectData: any): boolean {
if (
oldObjectData.content.tilemapJsonFile !==
newObjectData.content.tilemapJsonFile
) {
this.setTilemapJsonFile(newObjectData.content.tilemapJsonFile);
}
if (
oldObjectData.content.tilesetJsonFile !==
newObjectData.content.tilesetJsonFile
) {
this.setTilesetJsonFile(newObjectData.content.tilesetJsonFile);
}
if (oldObjectData.content.debugMode !== newObjectData.content.debugMode) {
this.setDebugMode(newObjectData.content.debugMode);
}
if (oldObjectData.content.fillColor !== newObjectData.content.fillColor) {
this.setFillColor(
gdjs.rgbOrHexStringToNumber(newObjectData.content.fillColor)
);
}
if (
oldObjectData.content.outlineColor !==
newObjectData.content.outlineColor
) {
this.setOutlineColor(
gdjs.rgbOrHexStringToNumber(newObjectData.content.outlineColor)
);
}
if (oldObjectData.fillOpacity !== newObjectData.fillOpacity) {
this.setFillOpacity(newObjectData.fillOpacity);
}
if (oldObjectData.outlineOpacity !== newObjectData.outlineOpacity) {
this.setOutlineOpacity(newObjectData.outlineOpacity);
}
if (oldObjectData.outlineSize !== newObjectData.outlineSize) {
this.setOutlineSize(newObjectData.outlineSize);
}
return true;
}
extraInitializationFromInitialInstance(initialInstanceData): void {
if (initialInstanceData.customSize) {
this.setWidth(initialInstanceData.width);
this.setHeight(initialInstanceData.height);
}
}
private _updateTileMap(): void {
this._tileMapManager.getOrLoadTileMap(
this._tilemapJsonFile,
this._tilesetJsonFile,
(tileMap: TileMapHelper.EditableTileMap | null) => {
if (!tileMap) {
// getOrLoadTileMap already log errors.
return;
}
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
tileMap,
this._collisionMaskTag
);
// The tile map polygons always keep the same references.
// It works because the tilemap is never modified.
this.hitBoxes = Array.from(
this._collisionTileMap.getAllHitboxes(this._collisionMaskTag)
);
this._renderer.redrawCollisionMask();
}
);
}
updateHitBoxes(): void {
this.updateTransformation();
// Update the RuntimeObject hitboxes attribute.
for (const hitboxes of this._collisionTileMap.getAllHitboxes(
this._collisionMaskTag
)) {
// RuntimeObject.hitBoxes contains the same polygons instances as the
// hitboxes from the tiles.
//
// When hitboxes for a tile is asked to the model, they are updated
// according to the new object location if needed.
// Iterating over all the tiles forces them to update their hitboxes.
//
// The hitboxes array is built by _updateTileMap().
}
this.hitBoxesDirty = false;
this._renderer.redrawCollisionMask();
this.updateAABB();
}
/**
* Update the affine transformation according to the object position, size
* and angle.
*/
updateTransformation(): void {
if (this._transformationIsUpToDate) {
return;
}
const transformation = this._collisionTileMap.getTransformation();
const absScaleX = Math.abs(this._renderer.getScaleX());
const absScaleY = Math.abs(this._renderer.getScaleY());
transformation.setToIdentity();
// Translation
transformation.translate(this.x, this.y);
// Rotation
const angleInRadians = (this.angle * Math.PI) / 180;
transformation.rotateAround(
angleInRadians,
this.getCenterX() * absScaleX,
this.getCenterY() * absScaleY
);
// Scale
transformation.scale(absScaleX, absScaleY);
this._collisionTileMap.setTransformation(transformation);
this._transformationIsUpToDate = true;
}
/**
* This method is expensive and should not be called.
* Prefer using {@link getHitBoxesAround} rather than getHitBoxes.
*/
getHitBoxes(): gdjs.Polygon[] {
if (this.hitBoxesDirty) {
this.updateHitBoxes();
this.updateAABB();
this.hitBoxesDirty = false;
}
return this.hitBoxes;
}
getHitBoxesAround(left: float, top: float, right: float, bottom: float) {
// This implementation doesn't call updateHitBoxes.
// It's important for good performances because there is no need to
// update the whole collision mask where only a few hitboxes must be
// checked.
this.updateTransformation();
return this._collisionTileMap.getHitboxesAround(
this._collisionMaskTag,
left,
top,
right,
bottom
);
}
/**
* insideObject usually use the AABB of the object.
* But, in case of a tile map, it makes more sense to look each tile individually.
* It returns true when there is an hitbox in the tile.
*/
insideObject(x: float, y: float): boolean {
this.updateTransformation();
// This is more precise than the default implementation.
return this._collisionTileMap.pointIsInsideTile(
x,
y,
this._collisionMaskTag
);
}
// This implementation doesn't use updateHitBoxes.
// It's important for good performances.
updateAABB(): void {
if (this.getAngle() === 0) {
// Fast computation of AABB for non rotated object
this.aabb.min[0] = this.x;
this.aabb.min[1] = this.y;
this.aabb.max[0] = this.aabb.min[0] + this.getWidth();
this.aabb.max[1] = this.aabb.min[1] + this.getHeight();
} else {
const affineTransformation = this._collisionTileMap.getTransformation();
const left = 0;
const right = this._collisionTileMap.getWidth();
const top = 0;
const bottom = this._collisionTileMap.getHeight();
const workingPoint = this.aabb.min;
workingPoint[0] = left;
workingPoint[1] = top;
affineTransformation.transform(workingPoint, workingPoint);
const topLeftX = workingPoint[0];
const topLeftY = workingPoint[1];
workingPoint[0] = right;
workingPoint[1] = top;
affineTransformation.transform(workingPoint, workingPoint);
const topRightX = workingPoint[0];
const topRightY = workingPoint[1];
workingPoint[0] = right;
workingPoint[1] = bottom;
affineTransformation.transform(workingPoint, workingPoint);
const bottomRightX = workingPoint[0];
const bottomRightY = workingPoint[1];
workingPoint[0] = left;
workingPoint[1] = bottom;
affineTransformation.transform(workingPoint, workingPoint);
const bottomLeftX = workingPoint[0];
const bottomLeftY = workingPoint[1];
this.aabb.min[0] = Math.min(
topLeftX,
topRightX,
bottomRightX,
bottomLeftX
);
this.aabb.max[0] = Math.max(
topLeftX,
topRightX,
bottomRightX,
bottomLeftX
);
this.aabb.min[1] = Math.min(
topLeftY,
topRightY,
bottomRightY,
bottomLeftY
);
this.aabb.max[1] = Math.max(
topLeftY,
topRightY,
bottomRightY,
bottomLeftY
);
}
}
/**
* Set the Tilemap json file to display.
*/
setTilemapJsonFile(tilemapJsonFile: string): void {
this._tilemapJsonFile = tilemapJsonFile;
this._updateTileMap();
}
getTilemapJsonFile(): string {
return this._tilemapJsonFile;
}
isTilemapJsonFile(selectedTilemapJsonFile: string): boolean {
return this._tilemapJsonFile === selectedTilemapJsonFile;
}
setTilesetJsonFile(tilesetJsonFile: string) {
this._tilesetJsonFile = tilesetJsonFile;
this._updateTileMap();
}
getTilesetJsonFile(): string {
return this._tilesetJsonFile;
}
isTilesetJsonFile(selectedTilesetJsonFile: string): boolean {
return this._tilesetJsonFile === selectedTilesetJsonFile;
}
/**
* @returns true if the hitboxes are shown.
*/
getDebugMode(): boolean {
return this._debugMode;
}
/**
* @returns true if the hitboxes are shown.
*/
setDebugMode(debugMode: boolean): void {
this._debugMode = debugMode;
this._renderer.redrawCollisionMask();
}
getFillColor(): integer {
return this._fillColor;
}
getOutlineColor(): integer {
return this._outlineColor;
}
setFillColor(fillColor: integer): void {
this._fillColor = fillColor;
}
setOutlineColor(outlineColor: integer): void {
this._outlineColor = outlineColor;
}
setOutlineSize(size: float): void {
this._outlineSize = size;
}
getOutlineSize(): float {
return this._outlineSize;
}
/**
*
* @param opacity from 0 to 255
*/
setFillOpacity(opacity: float): void {
this._fillOpacity = opacity;
}
/**
*
* @returns an opacity value from 0 to 255.
*/
getFillOpacity(): float {
return this._fillOpacity;
}
/**
*
* @param opacity from 0 to 255
*/
setOutlineOpacity(opacity: float): void {
this._outlineOpacity = opacity;
}
/**
*
* @returns an opacity value from 0 to 255.
*/
getOutlineOpacity(): float {
return this._outlineOpacity;
}
setX(x: float): void {
super.setX(x);
this._transformationIsUpToDate = false;
}
setY(y: float): void {
super.setY(y);
this._transformationIsUpToDate = false;
}
setAngle(angle: float): void {
super.setAngle(angle);
this._transformationIsUpToDate = false;
}
// TODO allow size changes from events?
setWidth(width: float): void {
if (this._renderer.getWidth() === width) return;
this._renderer.setWidth(width);
this.hitBoxesDirty = true;
this._transformationIsUpToDate = false;
}
setHeight(height: float): void {
if (this._renderer.getHeight() === height) return;
this._renderer.setHeight(height);
this.hitBoxesDirty = true;
this._transformationIsUpToDate = false;
}
getWidth(): float {
return this._renderer.getWidth();
}
getHeight(): float {
return this._renderer.getHeight();
}
}
gdjs.registerObject(
'TileMap::CollisionMask',
gdjs.TileMapCollisionMaskRuntimeObject
);
TileMapCollisionMaskRuntimeObject.supportsReinitialization = false;
}

View File

@@ -1,3 +1,5 @@
/// <reference path="helper/TileMapHelper.d.ts" />
/// <reference path="pixi-tilemap/dist/pixi-tilemap.d.ts" />
namespace gdjs {
const logger = new gdjs.Logger('Tilemap object');
@@ -10,8 +12,7 @@ namespace gdjs {
_object: any;
_runtimeScene: gdjs.RuntimeScene;
// @ts-ignore - pixi-tilemap types to be added.
_pixiObject: any;
_pixiObject: PIXI.tilemap.CompositeRectTileLayer;
/**
* @param runtimeObject The object to render
@@ -25,10 +26,7 @@ namespace gdjs {
this._runtimeScene = runtimeScene;
// Load (or reset)
if (this._pixiObject === undefined) {
// @ts-ignore - pixi-tilemap types to be added.
this._pixiObject = new PIXI.tilemap.CompositeRectTileLayer(0);
}
this._pixiObject = new PIXI.tilemap.CompositeRectTileLayer(0);
this._pixiObject.tileAnim = [0, 0];
runtimeScene
@@ -37,7 +35,6 @@ namespace gdjs {
.addRendererObject(this._pixiObject, runtimeObject.getZOrder());
this.updateAngle();
this.updateOpacity();
this.updateTileMap();
this.updatePosition();
}
@@ -45,74 +42,21 @@ namespace gdjs {
return this._pixiObject;
}
incrementAnimationFrameX(runtimeScene) {
incrementAnimationFrameX(runtimeScene: gdjs.RuntimeScene) {
this._pixiObject.tileAnim[0] += 1;
}
_loadTileMapWithTileset(tileMapJsonData, tilesetJsonData) {
// @ts-ignore - TODO: Add typings for pixi-tilemap-helper.
const pixiTileMapData = PixiTileMapHelper.loadPixiTileMapData(
(textureName) =>
this._runtimeScene
.getGame()
.getImageManager()
.getPIXITexture(textureName),
tilesetJsonData
? { ...tileMapJsonData, tilesets: [tilesetJsonData] }
: tileMapJsonData,
this._object._tilemapAtlasImage,
this._object._tilemapJsonFile,
this._object._tilesetJsonFile
updatePixiTileMap(
tileMap: TileMapHelper.EditableTileMap,
textureCache: TileMapHelper.TileTextureCache
) {
TileMapHelper.PixiTileMapHelper.updatePixiTileMap(
this._pixiObject,
tileMap,
textureCache,
this._object._displayMode,
this._object._layerIndex
);
if (pixiTileMapData) {
// @ts-ignore - TODO: Add typings for pixi-tilemap-helper.
PixiTileMapHelper.updatePixiTileMap(
this._pixiObject,
pixiTileMapData,
this._object._displayMode,
this._object._layerIndex,
// @ts-ignore - TODO: Add typings for pako.
pako
);
}
}
updateTileMap(): void {
this._runtimeScene
.getGame()
.getJsonManager()
.loadJson(this._object._tilemapJsonFile, (error, tileMapJsonData) => {
if (error) {
logger.error(
'An error happened while loading a Tilemap JSON data:',
error
);
return;
}
if (this._object._tilesetJsonFile) {
this._runtimeScene
.getGame()
.getJsonManager()
.loadJson(
this._object._tilesetJsonFile,
(error, tilesetJsonData) => {
if (error) {
logger.error(
'An error happened while loading Tileset JSON data:',
error
);
return;
}
this._loadTileMapWithTileset(
tileMapJsonData,
tilesetJsonData
);
}
);
} else {
this._loadTileMapWithTileset(tileMapJsonData, null);
}
});
}
updatePosition(): void {
@@ -135,13 +79,13 @@ namespace gdjs {
this._pixiObject.alpha = this._object._opacity / 255;
}
setWidth(width): void {
setWidth(width: float): void {
this._pixiObject.width = width / this._pixiObject.scale.x;
this._pixiObject.pivot.x = width / 2;
this.updatePosition();
}
setHeight(height): void {
setHeight(height: float): void {
this._pixiObject.height = height / this._pixiObject.scale.y;
this._pixiObject.pivot.y = height / 2;
this.updatePosition();

View File

@@ -1,9 +1,10 @@
/// <reference path="helper/TileMapHelper.d.ts" />
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
const logger = new gdjs.Logger('Tilemap object');
/**
* Displays a Tilemap object (mapeditor.org supported).
* @memberof gdjs
* @class TileMapRuntimeObject
* @extends gdjs.RuntimeObject
*/
export class TileMapRuntimeObject extends gdjs.RuntimeObject {
_frameElapsedTime: float = 0;
@@ -15,9 +16,10 @@ namespace gdjs {
_layerIndex: integer;
_animationSpeedScale: number;
_animationFps: number;
_renderer: any;
_tileMapManager: gdjs.TileMap.TileMapRuntimeManager;
_renderer: gdjs.TileMapRuntimeObjectPixiRenderer;
constructor(runtimeScene, objectData) {
constructor(runtimeScene: gdjs.RuntimeScene, objectData) {
super(runtimeScene, objectData);
this._opacity = objectData.content.opacity;
this._tilemapJsonFile = objectData.content.tilemapJsonFile;
@@ -27,18 +29,14 @@ namespace gdjs {
this._layerIndex = objectData.content.layerIndex;
this._animationSpeedScale = objectData.content.animationSpeedScale;
this._animationFps = objectData.content.animationFps;
if (this._renderer) {
gdjs.TileMapRuntimeObjectRenderer.call(
this._renderer,
this,
runtimeScene
);
} else {
this._renderer = new gdjs.TileMapRuntimeObjectRenderer(
this,
runtimeScene
);
}
this._tileMapManager = gdjs.TileMap.TileMapRuntimeManager.getManager(
runtimeScene
);
this._renderer = new gdjs.TileMapRuntimeObjectRenderer(
this,
runtimeScene
);
this._updateTileMap();
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
@@ -48,14 +46,14 @@ namespace gdjs {
return this._renderer.getRendererObject();
}
update(runtimeScene): void {
update(runtimeScene: gdjs.RuntimeScene): void {
if (this._animationSpeedScale <= 0 || this._animationFps === 0) {
return;
}
const elapsedTime = this.getElapsedTime(runtimeScene) / 1000;
this._frameElapsedTime += elapsedTime * this._animationSpeedScale;
while (this._frameElapsedTime > 1 / this._animationFps) {
this._renderer.incrementAnimationFrameX();
this._renderer.incrementAnimationFrameX(runtimeScene);
this._frameElapsedTime -= 1 / this._animationFps;
}
}
@@ -108,67 +106,101 @@ namespace gdjs {
return true;
}
/**
* Initialize the extra parameters that could be set for an instance.
*/
extraInitializationFromInitialInstance(initialInstanceData) {
extraInitializationFromInitialInstance(initialInstanceData): void {
if (initialInstanceData.customSize) {
this.setWidth(initialInstanceData.width);
this.setHeight(initialInstanceData.height);
}
}
private _updateTileMap(): void {
this._tileMapManager.getOrLoadTileMap(
this._tilemapJsonFile,
this._tilesetJsonFile,
(tileMap: TileMapHelper.EditableTileMap | null) => {
if (!tileMap) {
// getOrLoadTileMap already warn.
return;
}
this._tileMapManager.getOrLoadTextureCache(
(textureName) =>
(this._runtimeScene
.getGame()
.getImageManager()
.getPIXITexture(textureName) as unknown) as PIXI.BaseTexture<
PIXI.Resource
>,
this._tilemapAtlasImage,
this._tilemapJsonFile,
this._tilesetJsonFile,
(textureCache: TileMapHelper.TileTextureCache | null) => {
if (!textureCache) {
// getOrLoadTextureCache already log warns and errors.
return;
}
this._renderer.updatePixiTileMap(tileMap, textureCache);
}
);
}
);
}
/**
* Set the Tilemap json file to display.
*/
setTilemapJsonFile(tilemapJsonFile): void {
setTilemapJsonFile(tilemapJsonFile: string): void {
this._tilemapJsonFile = tilemapJsonFile;
this._renderer.updateTileMap();
this._updateTileMap();
}
getTilemapJsonFile() {
getTilemapJsonFile(): string {
return this._tilemapJsonFile;
}
isTilemapJsonFile(selectedTilemapJsonFile): boolean {
isTilemapJsonFile(selectedTilemapJsonFile: string): boolean {
return this._tilemapJsonFile === selectedTilemapJsonFile;
}
setTilesetJsonFile(tilesetJsonFile) {
setTilesetJsonFile(tilesetJsonFile: string): void {
this._tilesetJsonFile = tilesetJsonFile;
this._renderer.updateTileMap();
this._updateTileMap();
}
getTilesetJsonFile() {
getTilesetJsonFile(): string {
return this._tilesetJsonFile;
}
setAnimationFps(animationFps) {
setAnimationFps(animationFps: float) {
this._animationFps = animationFps;
}
getAnimationFps() {
getAnimationFps(): float {
return this._animationFps;
}
isTilesetJsonFile(selectedTilesetJsonFile) {
isTilesetJsonFile(selectedTilesetJsonFile: string): boolean {
return this._tilesetJsonFile === selectedTilesetJsonFile;
}
isDisplayMode(selectedDisplayMode) {
isDisplayMode(selectedDisplayMode: string): boolean {
return this._displayMode === selectedDisplayMode;
}
setDisplayMode(displayMode): void {
setDisplayMode(displayMode: string): void {
this._displayMode = displayMode;
this._renderer.updateTileMap();
this._updateTileMap();
}
getDisplayMode() {
getDisplayMode(): string {
return this._displayMode;
}
setLayerIndex(layerIndex): void {
this._layerIndex = layerIndex;
this._renderer.updateTileMap();
this._updateTileMap();
}
getLayerIndex() {
getLayerIndex(): integer {
return this._layerIndex;
}
@@ -176,14 +208,10 @@ namespace gdjs {
this._animationSpeedScale = animationSpeedScale;
}
getAnimationSpeedScale() {
getAnimationSpeedScale(): float {
return this._animationSpeedScale;
}
/**
* Set the width of the object.
* @param width The new width.
*/
setWidth(width: float): void {
if (this._renderer.getWidth() === width) return;
@@ -191,10 +219,6 @@ namespace gdjs {
this.hitBoxesDirty = true;
}
/**
* Set the height of the object.
* @param height The new height.
*/
setHeight(height: float): void {
if (this._renderer.getHeight() === height) return;
@@ -202,28 +226,16 @@ namespace gdjs {
this.hitBoxesDirty = true;
}
/**
* Set object position on X axis.
* @param x The new position X of the object.
*/
setX(x: float): void {
super.setX(x);
this._renderer.updatePosition();
}
/**
* Set object position on Y axis.
* @param y The new position Y of the object.
*/
setY(y: float): void {
super.setY(y);
this._renderer.updatePosition();
}
/**
* Set the angle of the object.
* @param angle The new angle of the object.
*/
setAngle(angle: float): void {
super.setAngle(angle);
this._renderer.updateAngle();
@@ -241,20 +253,14 @@ namespace gdjs {
/**
* Get object opacity.
*/
getOpacity() {
getOpacity(): float {
return this._opacity;
}
/**
* Get the width of the object.
*/
getWidth(): float {
return this._renderer.getWidth();
}
/**
* Get the height of the object.
*/
getHeight(): float {
return this._renderer.getHeight();
}

View File

@@ -34,9 +34,6 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/TiledSpriteIcon.png")
.SetCategoryFullName(_("General"));
#if defined(GD_IDE_ONLY)
obj.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
obj.AddCondition("Opacity",
_("Opacity"),
_("Compare the opacity of a Tiled Sprite, between 0 (fully "
@@ -92,8 +89,7 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TiledSprite")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetWidth")
.SetGetter("GetWidth")
.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
.SetGetter("GetWidth");
obj.AddCondition("Width",
_("Width"),
@@ -105,8 +101,7 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TiledSprite")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetWidth")
.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
.SetFunctionName("GetWidth");
obj.AddAction("Height",
_("Height"),
@@ -119,8 +114,7 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TiledSprite")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetHeight")
.SetGetter("GetHeight")
.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
.SetGetter("GetHeight");
obj.AddCondition("Height",
_("Height"),
@@ -132,8 +126,7 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TiledSprite")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetHeight")
.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
.SetFunctionName("GetHeight");
obj.AddAction("SetSize",
_("Size"),
@@ -146,8 +139,7 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TiledSprite")
.AddParameter("expression", _("Width"))
.AddParameter("expression", _("Height"))
.SetFunctionName("SetSize")
.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
.SetFunctionName("SetSize");
// Deprecated: now available for all objects.
obj.AddAction("Angle",
@@ -187,8 +179,7 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
.UseStandardOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("SetXOffset")
.SetGetter("GetXOffset")
.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
.SetGetter("GetXOffset");
obj.AddCondition(
"XOffset",
@@ -201,8 +192,7 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TiledSprite")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetXOffset")
.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
.SetFunctionName("GetXOffset");
obj.AddAction(
"YOffset",
@@ -216,8 +206,7 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
.UseStandardOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("SetYOffset")
.SetGetter("GetYOffset")
.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
.SetGetter("GetYOffset");
obj.AddCondition(
"YOffset",
@@ -230,7 +219,5 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "TiledSprite")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetYOffset")
.SetIncludeFile("TiledSpriteObject/TiledSpriteObject.h");
#endif
.SetFunctionName("GetYOffset");
}

View File

@@ -35,10 +35,6 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
std::make_shared<gd::BehaviorsSharedData>());
#if defined(GD_IDE_ONLY)
aut.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
aut.AddAction("SimulateLeftKey",
_("Simulate left key press"),
_("Simulate a press of left key."),
@@ -49,9 +45,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.MarkAsAdvanced()
.SetFunctionName("SimulateLeftKey")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("SimulateLeftKey");
aut.AddAction("SimulateRightKey",
_("Simulate right key press"),
@@ -63,9 +57,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.MarkAsAdvanced()
.SetFunctionName("SimulateRightKey")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("SimulateRightKey");
aut.AddAction("SimulateUpKey",
_("Simulate up key press"),
@@ -77,9 +69,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.MarkAsAdvanced()
.SetFunctionName("SimulateUpKey")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("SimulateUpKey");
aut.AddAction("SimulateDownKey",
_("Simulate down key press"),
@@ -91,9 +81,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.MarkAsAdvanced()
.SetFunctionName("SimulateDownKey")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("SimulateDownKey");
aut.AddAction(
"SimulateControl",
@@ -109,9 +97,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
_("Key"),
"[\"Left\", \"Right\", \"Up\", \"Down\"]")
.MarkAsAdvanced()
.SetFunctionName("SimulateControl")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("SimulateControl");
aut.AddAction("IgnoreDefaultControls",
_("Ignore default controls"),
@@ -125,9 +111,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.AddParameter("yesorno", _("Ignore controls"))
.MarkAsAdvanced()
.SetFunctionName("IgnoreDefaultControls")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("IgnoreDefaultControls");
aut.AddAction("SimulateStick",
_("Simulate stick control"),
@@ -141,9 +125,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("expression", _("Stick angle (in degrees)"))
.AddParameter("expression", _("Stick force (between 0 and 1)"))
.MarkAsAdvanced()
.SetFunctionName("SimulateStick")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("SimulateStick");
aut.AddScopedCondition("IsUsingControl",
_("Control pressed or simulated"),
@@ -176,9 +158,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("IsMoving")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("IsMoving");
aut.AddAction("Acceleration",
_("Acceleration"),
@@ -192,9 +172,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.UseStandardOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("SetAcceleration")
.SetGetter("GetAcceleration")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetGetter("GetAcceleration");
aut.AddCondition("Acceleration",
_("Acceleration"),
@@ -207,9 +185,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetAcceleration")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetAcceleration");
aut.AddAction("Deceleration",
_("Deceleration"),
@@ -223,9 +199,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.UseStandardOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("SetDeceleration")
.SetGetter("GetDeceleration")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetGetter("GetDeceleration");
aut.AddCondition("Deceleration",
_("Deceleration"),
@@ -238,9 +212,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetDeceleration")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetDeceleration");
aut.AddAction("MaxSpeed",
_("Maximum speed"),
@@ -253,9 +225,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardOperatorParameters("number")
.SetFunctionName("SetMaxSpeed")
.SetGetter("GetMaxSpeed")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetGetter("GetMaxSpeed");
aut.AddCondition("MaxSpeed",
_("Maximum speed"),
@@ -268,9 +238,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetMaxSpeed")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetMaxSpeed");
aut.AddCondition("Speed",
_("Speed"),
@@ -282,9 +250,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardRelationalOperatorParameters("number")
.SetFunctionName("GetSpeed")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetSpeed");
aut.AddAction("AngularMaxSpeed",
_("Angular maximum speed"),
@@ -298,9 +264,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.UseStandardOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("SetAngularMaxSpeed")
.SetGetter("GetAngularMaxSpeed")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetGetter("GetAngularMaxSpeed");
aut.AddCondition("AngularMaxSpeed",
_("Angular maximum speed"),
@@ -313,9 +277,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetAngularMaxSpeed")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetAngularMaxSpeed");
aut.AddAction("AngleOffset",
_("Rotation offset"),
@@ -329,9 +291,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.UseStandardOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("SetAngleOffset")
.SetGetter("GetAngleOffset")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetGetter("GetAngleOffset");
aut.AddCondition(
"AngleOffset",
@@ -345,9 +305,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetAngleOffset")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetAngleOffset");
aut.AddCondition(
"Angle",
@@ -361,9 +319,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetAngle")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetAngle");
aut.AddCondition("XVelocity",
_("Speed on X axis"),
@@ -377,9 +333,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetXVelocity")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetXVelocity");
aut.AddCondition("YVelocity",
_("Speed on Y axis"),
@@ -393,9 +347,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.UseStandardRelationalOperatorParameters("number")
.MarkAsAdvanced()
.SetFunctionName("GetYVelocity")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetYVelocity");
aut.AddAction("AllowDiagonals",
_("Diagonal movement"),
@@ -407,9 +359,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.AddParameter("yesorno", _("Allow?"))
.SetFunctionName("SetAllowDiagonals")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("SetAllowDiagonals");
aut.AddCondition("DiagonalsAllowed",
_("Diagonal movement"),
@@ -421,9 +371,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.MarkAsAdvanced()
.SetFunctionName("DiagonalsAllowed")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("DiagonalsAllowed");
aut.AddAction("RotateObject",
_("Rotate the object"),
@@ -436,9 +384,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.AddParameter("yesorno", _("Rotate object?"))
.MarkAsAdvanced()
.SetFunctionName("SetRotateObject")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("SetRotateObject");
aut.AddCondition(
"ObjectRotated",
@@ -451,9 +397,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.MarkAsAdvanced()
.SetFunctionName("IsObjectRotated")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("IsObjectRotated");
aut.AddExpression("Acceleration",
_("Acceleration"),
@@ -462,9 +406,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("GetAcceleration")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetAcceleration");
aut.AddExpression("Deceleration",
_("Deceleration"),
@@ -473,9 +415,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("GetDeceleration")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetDeceleration");
aut.AddExpression("MaxSpeed",
_("Maximum speed"),
@@ -484,9 +424,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("GetMaxSpeed")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetMaxSpeed");
aut.AddExpression("Speed",
_("Speed"),
@@ -495,9 +433,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("GetSpeed")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetSpeed");
aut.AddExpression("AngularMaxSpeed",
_("Angular maximum speed"),
@@ -506,9 +442,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("GetAngularMaxSpeed")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetAngularMaxSpeed");
aut.AddExpression("AngleOffset",
_("Rotation offset"),
@@ -517,9 +451,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("GetAngleOffset")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetAngleOffset");
aut.AddExpression("Angle",
_("Angle of the movement"),
@@ -528,9 +460,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("GetAngle")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetAngle");
aut.AddExpression("XVelocity",
_("Speed on the X axis"),
@@ -539,9 +469,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("GetXVelocity")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetXVelocity");
aut.AddExpression("YVelocity",
_("Speed on the Y axis"),
@@ -550,9 +478,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
"CppPlatform/Extensions/topdownmovementicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
.SetFunctionName("GetYVelocity")
.SetIncludeFile(
"TopDownMovementBehavior/TopDownMovementRuntimeBehavior.h");
.SetFunctionName("GetYVelocity");
aut.AddScopedAction("SetVelocityX",
_("Speed on the X axis"),

View File

@@ -19,6 +19,44 @@
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
*/
const easingChoices = JSON.stringify([
'linear',
'easeInQuad',
'easeOutQuad',
'easeInOutQuad',
'easeInCubic',
'easeOutCubic',
'easeInOutCubic',
'easeInQuart',
'easeOutQuart',
'easeInOutQuart',
'easeInQuint',
'easeOutQuint',
'easeInOutQuint',
'easeInSine',
'easeOutSine',
'easeInOutSine',
'easeInExpo',
'easeOutExpo',
'easeInOutExpo',
'easeInCirc',
'easeOutCirc',
'easeInOutCirc',
'easeOutBounce',
'easeInBack',
'easeOutBack',
'easeInOutBack',
'elastic',
'swingFromTo',
'swingFrom',
'swingTo',
'bounce',
'bouncePast',
'easeFromTo',
'easeFrom',
'easeTo',
]);
module.exports = {
createExtension: function (
_ /*: (string) => string */,
@@ -37,45 +75,7 @@ module.exports = {
)
.setExtensionHelpPath('/behaviors/tween');
const easingChoices = JSON.stringify([
'linear',
'easeInQuad',
'easeOutQuad',
'easeInOutQuad',
'easeInCubic',
'easeOutCubic',
'easeInOutCubic',
'easeInQuart',
'easeOutQuart',
'easeInOutQuart',
'easeInQuint',
'easeOutQuint',
'easeInOutQuint',
'easeInSine',
'easeOutSine',
'easeInOutSine',
'easeInExpo',
'easeOutExpo',
'easeInOutExpo',
'easeInCirc',
'easeOutCirc',
'easeInOutCirc',
'easeOutBounce',
'easeInBack',
'easeOutBack',
'easeInOutBack',
'elastic',
'swingFromTo',
'swingFrom',
'swingTo',
'bounce',
'bouncePast',
'easeFromTo',
'easeFrom',
'easeTo',
]);
extension
extension
.addExpression(
'Ease',
_('Ease'),
@@ -88,15 +88,194 @@ module.exports = {
.addParameter('expression', _('From value'))
.addParameter('expression', _('To value'))
.addParameter('expression', _('Weighting'))
.setParameterLongDescription(
_('From 0 to 1.')
)
.setParameterLongDescription(_('From 0 to 1.'))
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.ease');
var tweenBehavior = new gd.BehaviorJsImplementation();
extension
.addAction(
'TweenSceneVariableNumber',
_('Tween a number in a scene variable'),
_(
"Tweens a scene variable's numeric value from one number to another."
),
_(
'Tween variable _PARAM2_ from _PARAM3_ to _PARAM4_ for _PARAM5_ms with easing _PARAM6_ as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', 'Tween identifier', '', false)
.addParameter('scenevar', 'The variable to tween', '', false)
.addParameter('expression', 'Initial value', '', false)
.addParameter('expression', 'Final value', '', false)
.addParameter('expression', 'Duration', '', false)
.addParameter('stringWithSelector', 'Easing', easingChoices, false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.tweenNumber');
extension
.addAction(
'TweenCameraPosition',
_('Tween the camera position'),
_(
'Tweens tweens the camera position from the current one to a new one.'
),
_(
'Tween camera on layer _PARAM4_ to _PARAM2_;_PARAM3_ for _PARAM5_ms with easing _PARAM6_ as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', 'Tween identifier', '', false)
.addParameter('expression', 'Target X position', '', false)
.addParameter('expression', 'Target Y position', '', false)
.addParameter('layer', 'Layer', '', true)
.addParameter('expression', 'Duration', '', false)
.addParameter('stringWithSelector', 'Easing', easingChoices, false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.tweenCamera');
extension
.addCondition(
'SceneTweenExists',
_('Scene tween exists'),
_('Check if the scene tween exists.'),
_('Scene tween _PARAM1_ exists'),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.sceneTweenExists');
extension
.addCondition(
'SceneTweenIsPlaying',
_('Scene tween is playing'),
_('Check if the scene tween is currently playing.'),
_('Scene tween _PARAM1_ is playing'),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.sceneTweenIsPlaying');
extension
.addCondition(
'SceneTweenHasFinished',
_('Scene tween finished playing'),
_('Check if the scene tween has finished playing.'),
_('Scene tween _PARAM1_ has finished playing'),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.sceneTweenHasFinished');
extension
.addAction(
'PauseSceneTween',
_('Pause a scene tween'),
_('Pause the running scene tween.'),
_('Pause the scene tween _PARAM1_'),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.pauseSceneTween');
extension
.addAction(
'StopSceneTween',
_('Stop a scene tween'),
_('Stop the running scene tween.'),
_('Stop the scene tween _PARAM1_ (jump to the end: _PARAM2_)'),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('yesorno', _('Jump to the end'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.stopSceneTween');
extension
.addAction(
'ResumeSceneTween',
_('Resume a scene tween'),
_('Resume the scene tween.'),
_('Resume the scene tween _PARAM1_'),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.resumeSceneTween');
extension
.addAction(
'RemoveSceneTween',
_('Remove a scene tween'),
_('Remove the scene tween. Call this when the tween is no longer needed to free memory.'),
_('Remove the scene tween _PARAM1_'),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.removeSceneTween');
const tweenBehavior = new gd.BehaviorJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
tweenBehavior.updateProperty = function (
@@ -131,6 +310,7 @@ module.exports = {
new gd.BehaviorsSharedData()
)
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweenruntimebehavior.js');
// Behavior related

View File

@@ -1,10 +1,9 @@
// Shifty.js 2.16.0 type definitions by arthuro555
declare namespace shifty {
// index.js
// index.ts
type easingFunction = (position: number) => number;
type startFunction = (state: any, data?: any) => any;
type finishFunction = (promisedData: shifty.promisedData) => any;
type startFunction = (state: any, data?: any | undefined) => void;
type finishFunction = (promisedData: shifty.promisedData) => void;
/**
* Gets called for every tick of the tween. This function is not called on the
* final tick of the animation.
@@ -13,11 +12,11 @@ declare namespace shifty {
state: any,
data: any | undefined,
timeElapsed: number
) => any;
type scheduleFunction = (callback: Function, timeout: number) => any;
interface tweenConfig {
) => void;
type scheduleFunction = (callback: Function, timeout: number) => void;
type tweenConfig = {
/**
* Starting position. If omitted, {@link * shifty.Tweenable#get} is used.
* Starting position. If omitted, {@link * Tweenable#get} is used.
*/
from?: any;
/**
@@ -40,8 +39,8 @@ declare namespace shifty {
start?: shifty.startFunction;
/**
* Executes when the tween
* completes. This will get overridden by {@link shifty.Tweenablethen } if that
* is called, and it will not fire if {@link shifty.Tweenablecancel } is
* completes. This will get overridden by {@link Tweenablethen } if that
* is called, and it will not fire if {@link Tweenablecancel } is
* called.
*/
finish?: shifty.finishFunction;
@@ -64,7 +63,7 @@ declare namespace shifty {
* correspond to `to`/`from`. You can learn more about this in the {@tutorial
* easing-function-in-depth} tutorial.
*/
easing?: Record<string, easingFunction> | string | easingFunction;
easing?: any | string | shifty.easingFunction;
/**
* Data that is passed to {@link * shifty.startFunction}, {@link shifty.renderFunction }, and {@link * shifty.promisedData}. Legacy property name: `attachment`.
*/
@@ -74,7 +73,7 @@ declare namespace shifty {
* to use Promise library or polyfill Promises in unsupported environments.
*/
promise?: Function;
}
};
type promisedData = {
/**
* The current state of the tween.
@@ -85,7 +84,7 @@ declare namespace shifty {
*/
data: any;
/**
* The {@link shifty.Tweenable } instance to
* The {@link Tweenable } instance to
* which the tween belonged.
*/
tweenable: Tweenable;
@@ -95,20 +94,20 @@ declare namespace shifty {
* Filters are only added to a tween when it is created so that they are not
* unnecessarily processed if they don't apply during an update tick.
*/
type doesApplyFilter = (tweenable: any) => boolean;
type doesApplyFilter = (tweenable: Tweenable) => boolean;
/**
* Is called when a tween is created. This should perform any setup needed by
* subsequent per-tick calls to {@link shifty.beforeTween } and {@link * shifty.afterTween}.
*/
type tweenCreatedFilter = (tweenable: any) => any;
type tweenCreatedFilter = (tweenable: Tweenable) => void;
/**
* Is called right before a tween is processed in a tick.
*/
type beforeTweenFilter = (tweenable: any) => any;
type beforeTweenFilter = (tweenable: Tweenable) => void;
/**
* Is called right after a tween is processed in a tick.
*/
type afterTweenFilter = (tweenable: any) => any;
type afterTweenFilter = (tweenable: Tweenable) => void;
/**
* An Object that contains functions that are called at key points in a tween's
* lifecycle. Shifty can only process `Number`s internally, but filters can
@@ -138,34 +137,321 @@ declare namespace shifty {
afterTween: shifty.afterTweenFilter;
};
// bezier.js
// bezier.ts
export function setBezierFunction(
name: string,
x1: number,
y1: number,
x2: number,
y2: number
): any;
): shifty.easingFunction;
export function unsetBezierFunction(name: string): boolean;
// interpolate.js
// easing-function.ts
export namespace Tweenable {
/*!
* All equations are adapted from Thomas Fuchs'
* [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/penner.js).
*
* Based on Easing Equations (c) 2003 [Robert
* Penner](http://www.robertpenner.com/), all rights reserved. This work is
* [subject to terms](http://www.robertpenner.com/easing_terms_of_use.html).
*/
/*!
* TERMS OF USE - EASING EQUATIONS
* Open source under the BSD License.
* Easing Equations (c) 2003 Robert Penner, all rights reserved.
*/
/**
* @member Tweenable.formulas
* @description A static Object of {@link shifty.easingFunction}s that can by
* used by Shifty. The default values are defined in
* [`easing-functions.js`](easing-functions.js.html), but you can add your own
* {@link shifty.easingFunction}s by defining them as keys to this Object.
*
* Shifty ships with an implementation of [Robert Penner's easing
* equations](http://robertpenner.com/easing/), as adapted from
* [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/penner.js)'s
* implementation.
* <p data-height="934" data-theme-id="0" data-slug-hash="wqObdO"
* data-default-tab="js,result" data-user="jeremyckahn" data-embed-version="2"
* data-pen-title="Shifty - Easing formula names" class="codepen">See the Pen <a
* href="https://codepen.io/jeremyckahn/pen/wqObdO/">Shifty - Easing formula
* names</a> by Jeremy Kahn (<a
* href="https://codepen.io/jeremyckahn">@jeremyckahn</a>) on <a
* href="https://codepen.io">CodePen</a>.</p>
* <script async
* src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
* @type {Object.<shifty.easingFunction>}
* @static
*/
export namespace formulas {
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const linear: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInQuad: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeOutQuad: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInOutQuad: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInCubic: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeOutCubic: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInOutCubic: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInQuart: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeOutQuart: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInOutQuart: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInQuint: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeOutQuint: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInOutQuint: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInSine: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeOutSine: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInOutSine: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInExpo: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeOutExpo: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInOutExpo: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInCirc: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeOutCirc: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInOutCirc: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeOutBounce: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInBack: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeOutBack: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeInOutBack: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const elastic: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const swingFromTo: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const swingFrom: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const swingTo: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const bounce: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const bouncePast: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeFromTo: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeFrom: shifty.easingFunction;
/**
* @memberof Tweenable.formulas
* @type {shifty.easingFunction}
* @param {number} pos
* @returns {number}
*/
export const easeTo: shifty.easingFunction;
}
}
export function interpolate<T extends Object>(
// interpolate.ts
export function interpolate<T>(
from: T,
to: T,
position: number,
easing: Record<string, easingFunction> | string | easingFunction,
easing:
| Record<string, string | shifty.easingFunction>
| string
| shifty.easingFunction,
delay?: number
): T;
// scene.js
// scene.ts
export class Scene {
/**
* The {@link shifty.Scene} class provides a way to control groups of {@link
* shifty.Tweenable}s. It is lightweight, minimalistic, and meant to provide
* performant {@link shifty.Tweenable} batch control that users of Shifty
* The {@link Scene} class provides a way to control groups of {@link
* Tweenable}s. It is lightweight, minimalistic, and meant to provide
* performant {@link Tweenable} batch control that users of Shifty
* might otherwise have to implement themselves. It is **not** a robust
* timeline solution, and it does **not** provide utilities for sophisticated
* animation sequencing or orchestration. If that is what you need for your
@@ -173,10 +459,10 @@ declare namespace shifty {
* [Rekapi](http://jeremyckahn.github.io/rekapi/doc/) (a timeline layer built
* on top of Shifty).
*
* Please be aware that {@link shifty.Scene} does **not** perform any
* automatic cleanup. If you want to remove a {@link shifty.Tweenable} from a
* {@link shifty.Scene}, you must do so explicitly with either {@link
* shifty.Scene#remove} or {@link shifty.Scene#empty}.
* Please be aware that {@link Scene} does **not** perform any
* automatic cleanup. If you want to remove a {@link Tweenable} from a
* {@link Scene}, you must do so explicitly with either {@link
* Scene#remove} or {@link Scene#empty}.
*
* <p class="codepen" data-height="677" data-theme-id="0" data-default-tab="js,result" data-user="jeremyckahn" data-slug-hash="qvZKbe" style="height: 677px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1em 0; padding: 1em;" data-pen-title="Shifty Scene Demo">
* <span>See the Pen <a href="https://codepen.io/jeremyckahn/pen/qvZKbe/">
@@ -184,25 +470,23 @@ declare namespace shifty {
* on <a href="https://codepen.io">CodePen</a>.</span>
* </p>
* <script async src="https://static.codepen.io/assets/embed/ei.js"></script>
* @param {...shifty.Tweenable} tweenables
* @param {...Tweenable} tweenables
* @see https://codepen.io/jeremyckahn/pen/qvZKbe
* @constructs shifty.Scene
* @constructs Scene
* @memberof shifty
*/
constructor(...tweenables: Tweenable[]);
/**
* A copy of the internal {@link shifty.Tweenable}s array.
* @member shifty.Scene#tweenables
* @type {Array.<shifty.Tweenable>}
* @readonly
* A copy of the internal {@link Tweenable}s array.
* @member Scene#tweenables
* @type {Array.<Tweenable>}
*/
get tweenables(): Tweenable[];
/**
* The {@link external:Promise}s for all {@link shifty.Tweenable}s in this
* {@link shifty.Scene} that have been configured with {@link
* shifty.Tweenable#setConfig}. Note that each call of {@link
* shifty.Scene#play} or {@link shifty.Scene#pause} creates new {@link
* The {@link external:Promise}s for all {@link Tweenable}s in this
* {@link Scene} that have been configured with {@link
* Tweenable#setConfig}. Note that each call of {@link
* Scene#play} or {@link Scene#pause} creates new {@link
* external:Promise}s:
*
* const scene = new Scene(new Tweenable());
@@ -214,87 +498,95 @@ declare namespace shifty {
* scene.play()
* );
*
* @member shifty.Scene#promises
* @type {Array.<external:Promise>}
* @readonly
* @member Scene#promises
* @type {Array.<Promise<any>>}
*/
get promises(): Promise<Object>[];
get promises(): Promise<any>[];
/**
* Add a {@link shifty.Tweenable} to be controlled by this {@link
* shifty.Scene}.
* @method shifty.Scene#add
* @param {shifty.Tweenable} tweenable
* @return {shifty.Tweenable} The {@link shifty.Tweenable} that was added.
* Add a {@link Tweenable} to be controlled by this {@link
* Scene}.
* @method Scene#add
* @param {Tweenable} tweenable
* @return {Tweenable} The {@link Tweenable} that was added.
*/
add(tweenable: Tweenable): Tweenable;
/**
* Remove a {@link shifty.Tweenable} that is controlled by this {@link
* shifty.Scene}.
* @method shifty.Scene#remove
* @param {shifty.Tweenable} tweenable
* @return {shifty.Tweenable} The {@link shifty.Tweenable} that was removed.
* Remove a {@link Tweenable} that is controlled by this {@link
* Scene}.
* @method Scene#remove
* @param {Tweenable} tweenable
* @return {Tweenable} The {@link Tweenable} that was removed.
*/
remove(tweenable: Tweenable): Tweenable;
/**
* [Remove]{@link shifty.Scene#remove} all {@link shifty.Tweenable}s in this {@link
* shifty.Scene}.
* @method shifty.Scene#empty
* @return {Array.<shifty.Tweenable>} The {@link shifty.Tweenable}s that were
* [Remove]{@link Scene#remove} all {@link Tweenable}s in this {@link
* Scene}.
* @method Scene#empty
* @return {Array.<Tweenable>} The {@link Tweenable}s that were
* removed.
*/
empty(): Array<Tweenable>;
/**
* Is `true` if any {@link shifty.Tweenable} in this {@link shifty.Scene} is
* Is `true` if any {@link Tweenable} in this {@link Scene} is
* playing.
* @method shifty.Scene#isPlaying
* @method Scene#isPlaying
* @return {boolean}
*/
isPlaying(): boolean;
/**
* Play all {@link shifty.Tweenable}s from their beginning.
* @method shifty.Scene#play
* @return {shifty.Scene}
* Play all {@link Tweenable}s from their beginning.
* @method Scene#play
* @return {Scene}
*/
play(): Scene;
/**
* {@link shifty.Tweenable#pause} all {@link shifty.Tweenable}s in this
* {@link shifty.Scene}.
* @method shifty.Scene#pause
* @return {shifty.Scene}
* {@link Tweenable#pause} all {@link Tweenable}s in this
* {@link Scene}.
* @method Scene#pause
* @return {Scene}
*/
pause(): Scene;
/**
* {@link shifty.Tweenable#resume} all paused {@link shifty.Tweenable}s.
* @method shifty.Scene#resume
* @return {shifty.Scene}
* {@link Tweenable#resume} all paused {@link Tweenable}s.
* @method Scene#resume
* @return {Scene}
*/
resume(): Scene;
/**
* {@link shifty.Tweenable#stop} all {@link shifty.Tweenable}s in this {@link
* shifty.Scene}.
* @method shifty.Scene#stop
* {@link Tweenable#stop} all {@link Tweenable}s in this {@link
* Scene}.
* @method Scene#stop
* @param {boolean} [gotoEnd]
* @return {shifty.Scene}
* @return {Scene}
*/
stop(gotoEnd?: boolean): Scene;
}
// tweenable.js
// token.ts
/**
* @memberof Tweenable.filters.token
* @param {Tweenable} tweenable
*/
export function tweenCreated(tweenable: Tweenable): void;
/**
* @memberof Tweenable.filters.token
* @param {Tweenable} tweenable
*/
export function beforeTween(tweenable: Tweenable): void;
/**
* @memberof Tweenable.filters.token
* @param {Tweenable} tweenable
*/
export function afterTween(tweenable: Tweenable): void;
export function doesApply(tweenable: Tweenable): boolean;
// tweenable.ts
/**
* @method shifty.tween
* @param {shifty.tweenConfig} [config={}]
* @description Standalone convenience method that functions identically to
* {@link shifty.Tweenable#tween}. You can use this to create tweens without
* needing to set up a {@link shifty.Tweenable} instance.
* {@link Tweenable#tween}. You can use this to create tweens without
* needing to set up a {@link Tweenable} instance.
*
* ```
* import { tween } from 'shifty';
@@ -304,9 +596,12 @@ declare namespace shifty {
* );
* ```
*
* @returns {shifty.Tweenable} A new {@link shifty.Tweenable} instance.
* @returns {Tweenable} A new {@link Tweenable} instance.
*/
export function tween(config?: tweenConfig): Tweenable;
export function tween(config?: shifty.tweenConfig): Tweenable;
export function resetList(): void;
export function getListHead(): Tweenable;
export function getListTail(): Tweenable;
export function tweenProps(
forPosition: number,
currentState: any,
@@ -314,18 +609,18 @@ declare namespace shifty {
targetState: any,
duration: number,
timestamp: number,
easing: Record<any, string | Function>
): Object;
easing: Record<string, string | Function>
): any;
export function processTweens(): void;
export function scheduleUpdate(): void;
export function composeEasingObject(
fromTweenParams: any,
fromTweenParams: Record<string, string | Function>,
easing?: any | string | Function,
composedEasing?: any
): any | Function;
): Record<string, string | Function> | Function;
export class Tweenable {
/**
* @method shifty.Tweenable.now
* @method Tweenable.now
* @static
* @returns {number} The current timestamp.
*/
@@ -333,180 +628,197 @@ declare namespace shifty {
/**
* @param {Object} [initialState={}] The values that the initial tween should
* start at if a `from` value is not provided to {@link
* shifty.Tweenable#tween} or {@link shifty.Tweenable#setConfig}.
* Tweenable#tween} or {@link Tweenable#setConfig}.
* @param {shifty.tweenConfig} [config] Configuration object to be passed to
* {@link shifty.Tweenable#setConfig}.
* @constructs shifty.Tweenable
* {@link Tweenable#setConfig}.
* @constructs Tweenable
* @memberof shifty
*/
constructor(initialState?: Object, config?: tweenConfig);
private _config: tweenConfig;
private _data: Object;
private _delay: number;
private _filters: filter[];
private _next: any;
private _previous: any;
private _timestamp: number;
private _resolve: any;
private _reject: (reason?: any) => void;
private _currentState: any;
private _originalState: Object;
private _targetState: Object;
private _start: () => void;
private _render: () => void;
private _promiseCtor: PromiseConstructor;
constructor(initialState?: any, config?: shifty.tweenConfig);
/** @private */
private _config;
/** @private */
private _data;
/** @private */
private _delay;
/** @private */
private _filters;
/** @private */
private _next;
/** @private */
private _previous;
/** @private */
private _timestamp;
/** @private */
private _hasEnded;
/** @private */
private _resolve;
/** @private */
private _reject;
/** @private */
private _currentState;
/** @private */
private _originalState;
/** @private */
private _targetState;
/** @private */
private _start;
/** @private */
private _render;
/** @private */
private _promiseCtor;
/**
* Applies a filter to Tweenable instance.
* @param {string} filterName The name of the filter to apply.
* @private
*/
private _applyFilter;
private _isPlaying: boolean;
private _pausedAtTime: number;
private _duration: any;
private _scheduleId: any;
private _easing: any;
/**
* Configure and start a tween. If this {@link shifty.Tweenable}'s instance
* Configure and start a tween. If this {@link Tweenable}'s instance
* is already running, then it will stop playing the old tween and
* immediately play the new one.
* @method shifty.Tweenable#tween
* @method Tweenable#tween
* @param {shifty.tweenConfig} [config] Gets passed to {@link
* shifty.Tweenable#setConfig}.
* @return {shifty.Tweenable}
* Tweenable#setConfig}.
* @return {Tweenable}
*/
tween(config?: tweenConfig): this;
tween(config?: shifty.tweenConfig): Tweenable;
/** @private */
private _pausedAtTime;
/**
* Configure a tween that will start at some point in the future. Aside from
* `delay`, `from`, and `to`, each configuration option will automatically
* default to the same option used in the preceding tween of this {@link
* shifty.Tweenable} instance.
* @method shifty.Tweenable#setConfig
* Tweenable} instance.
* @method Tweenable#setConfig
* @param {shifty.tweenConfig} [config={}]
* @return {shifty.Tweenable}
* @return {Tweenable}
*/
setConfig(config?: tweenConfig): this;
setConfig(config?: shifty.tweenConfig): Tweenable;
/** @private */
private _isPlaying;
/** @private */
private _scheduleId;
/** @private */
private _duration;
/** @private */
private _easing;
/**
* Overrides any `finish` function passed via a {@link shifty.tweenConfig}.
* @method shifty.Tweenable#then
* @method Tweenable#then
* @param {function} onFulfilled Receives {@link shifty.promisedData} as the
* first parameter.
* @param {function} onRejected Receives {@link shifty.promisedData} as the
* first parameter.
* @return {external:Promise}
* @return {Promise<Object>}
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
*/
then(onFulfilled: Function, onRejected?: Function): Promise<any>;
private _promise: Promise<any>;
/** @private */
private _promise;
/**
* @method shifty.Tweenable#catch
* @method Tweenable#catch
* @param {function} onRejected Receives {@link shifty.promisedData} as the
* first parameter.
* @return {external:Promise}
* @return {Promise<Object>}
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
*/
catch(onRejected: Function): Promise<any>;
/**
* @method shifty.Tweenable#get
* @method Tweenable#get
* @return {Object} The current state.
*/
get(): Object;
get(): any;
/**
* Set the current state.
* @method shifty.Tweenable#set
* @method Tweenable#set
* @param {Object} state The state to set.
*/
set(state: Object): void;
set(state: any): void;
/**
* Pause a tween. Paused tweens can be resumed from the point at which they
* were paused. If a tween is not running, this is a no-op.
* @method shifty.Tweenable#pause
* @return {shifty.Tweenable}
* @method Tweenable#pause
* @return {Tweenable}
*/
pause(): this;
pause(): Tweenable;
/**
* Resume a paused tween.
* @method shifty.Tweenable#resume
* @return {shifty.Tweenable}
* @method Tweenable#resume
* @return {Tweenable}
*/
resume(): this;
private _resume(currentTime?: number): any;
resume(): Tweenable;
/**
* @private
* @param {number} currentTime
* @returns {Tweenable}
*/
private _resume;
/**
* Move the state of the animation to a specific point in the tween's
* timeline. If the animation is not running, this will cause {@link
* shifty.renderFunction} handlers to be called.
* @method shifty.Tweenable#seek
* @param {millisecond} millisecond The millisecond of the animation to seek
* @method Tweenable#seek
* @param {number} millisecond The millisecond of the animation to seek
* to. This must not be less than `0`.
* @return {shifty.Tweenable}
* @return {Tweenable}
*/
seek(millisecond: number): this;
seek(millisecond: number): Tweenable;
/**
* Stops a tween. If a tween is not running, this is a no-op. This method
* does not cancel the tween {@link external:Promise}. For that, use {@link
* shifty.Tweenable#cancel}.
* Tweenable#cancel}.
* @param {boolean} [gotoEnd] If `false`, the tween just stops at its current
* state. If `true`, the tweened object's values are instantly set to the
* target values.
* @method shifty.Tweenable#stop
* @return {shifty.Tweenable}
* @method Tweenable#stop
* @return {Tweenable}
*/
stop(gotoEnd?: boolean): this;
stop(gotoEnd?: boolean): Tweenable;
/**
* {@link shifty.Tweenable#stop}s a tween and also `reject`s its {@link
* {@link Tweenable#stop}s a tween and also `reject`s its {@link
* external:Promise}. If a tween is not running, this is a no-op. Prevents
* calling any provided `finish` function.
* @param {boolean} [gotoEnd] Is propagated to {@link shifty.Tweenable#stop}.
* @method shifty.Tweenable#cancel
* @return {shifty.Tweenable}
* @param {boolean} [gotoEnd] Is propagated to {@link Tweenable#stop}.
* @method Tweenable#cancel
* @return {Tweenable}
* @see https://github.com/jeremyckahn/shifty/issues/122
*/
cancel(gotoEnd?: boolean): this;
cancel(gotoEnd?: boolean): Tweenable;
/**
* Whether or not a tween is running.
* @method shifty.Tweenable#isPlaying
* @method Tweenable#isPlaying
* @return {boolean}
*/
isPlaying(): boolean;
/**
* @method shifty.Tweenable#setScheduleFunction
* @param {shifty.scheduleFunction} scheduleFunction
* @deprecated Will be removed in favor of {@link shifty.Tweenable.setScheduleFunction} in 3.0.
* Whether or not a tween has finished running.
* @method Tweenable#hasEnded
* @return {boolean}
*/
setScheduleFunction(scheduleFunction: scheduleFunction): void;
hasEnded(): boolean;
/**
* @method Tweenable#setScheduleFunction
* @param {shifty.scheduleFunction} scheduleFunction
* @deprecated Will be removed in favor of {@link Tweenable.setScheduleFunction} in 3.0.
*/
setScheduleFunction(scheduleFunction: shifty.scheduleFunction): void;
/**
* Get and optionally set the data that gets passed as `data` to {@link
* shifty.promisedData}, {@link shifty.startFunction} and {@link
* shifty.renderFunction}.
* @param {Object} [data]
* @method shifty.Tweenable#data
* @method Tweenable#data
* @return {Object} The internally stored `data`.
*/
data(data?: any): any;
/**
* `delete` all "own" properties. Call this when the {@link
* shifty.Tweenable} instance is no longer needed to free memory.
* @method shifty.Tweenable#dispose
* Tweenable} instance is no longer needed to free memory.
* @method Tweenable#dispose
*/
dispose(): void;
}
export namespace Tweenable {
/**
* Set a custom schedule function.
@@ -516,58 +828,12 @@ declare namespace shifty {
* is used if available, otherwise
* [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout)
* is used.
* @method shifty.Tweenable.setScheduleFunction
* @method Tweenable.setScheduleFunction
* @param {shifty.scheduleFunction} fn The function to be
* used to schedule the next frame to be rendered.
* @return {shifty.scheduleFunction} The function that was set.
*/
export function setScheduleFunction(fn: scheduleFunction): scheduleFunction;
export const filters: any;
// easing-functions.js
export namespace formulas {
export function linear(pos: number): number;
export function easeInQuad(pos: any): number;
export function easeOutQuad(pos: any): number;
export function easeInOutQuad(pos: any): number;
export function easeInCubic(pos: any): number;
export function easeOutCubic(pos: any): number;
export function easeInOutCubic(pos: any): number;
export function easeInQuart(pos: any): number;
export function easeOutQuart(pos: any): number;
export function easeInOutQuart(pos: any): number;
export function easeInQuint(pos: any): number;
export function easeOutQuint(pos: any): number;
export function easeInOutQuint(pos: any): number;
export function easeInSine(pos: any): number;
export function easeOutSine(pos: any): number;
export function easeInOutSine(pos: any): number;
export function easeInExpo(pos: any): number;
export function easeOutExpo(pos: any): number;
export function easeInOutExpo(pos: any): number;
export function easeInCirc(pos: any): number;
export function easeOutCirc(pos: any): number;
export function easeInOutCirc(pos: any): number;
export function easeOutBounce(pos: any): number;
export function easeInBack(pos: any): number;
export function easeOutBack(pos: any): number;
export function easeInOutBack(pos: any): number;
export function elastic(pos: any): number;
export function swingFromTo(pos: any): number;
export function swingFrom(pos: any): number;
export function swingTo(pos: any): number;
export function bounce(pos: any): number;
export function bouncePast(pos: any): number;
export function easeFromTo(pos: any): number;
export function easeFrom(pos: any): number;
export function easeTo(pos: any): number;
}
export const filters: Record<string, shifty.filter>;
}
// token.js
export function tweenCreated(tweenable: any): void;
export function beforeTween(tweenable: any): void;
export function afterTween(tweenable: any): void;
export function doesApply(tweenable: any): boolean;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,68 @@
// Callbacks called to pause/resume Shifty scene when a gdjs.RuntimeScene
// is paused/resumed
namespace gdjs {
export interface RuntimeScene {
shiftyJsScene: shifty.Scene;
}
}
let currentTweenTime: number = 0;
/**
* Stop and "destroy" all the tweens when a scene is unloaded.
*/
gdjs.registerRuntimeSceneUnloadedCallback(function (runtimeScene) {
const shiftyJsScene = runtimeScene.shiftyJsScene;
if (!shiftyJsScene) return;
// Stop and explicitly remove all tweenables to be sure to drop
// all references to the tweenables of the scene.
shiftyJsScene.stop(false);
shiftyJsScene.empty();
});
/**
* When a scene is paused, pause all the tweens of this scene.
*/
gdjs.registerRuntimeScenePausedCallback(function (runtimeScene) {
const shiftyJsScene = runtimeScene.shiftyJsScene;
if (shiftyJsScene) shiftyJsScene.pause();
});
/**
* When a scene is paused, resume all the tweens of this scene.
*/
gdjs.registerRuntimeSceneResumedCallback(function (runtimeScene) {
const shiftyJsScene = runtimeScene.shiftyJsScene;
if (!shiftyJsScene) return;
// It is important to set immediately the current Shifty time back to the
// time of the scene, as the call `resume` will process the tweens.
// (If not done, tweens will be resumed with the time of the previous
// scene, that could create weird result/make tweens act as if not paused).
currentTweenTime = runtimeScene.getTimeManager().getTimeFromStart();
// Note that per the invariant of shiftyJsScene, shiftyJsScene will only
// contains tweenables that should be playing (so calling resume is safe).
shiftyJsScene.resume();
});
// Handle Shifty.js updates (the time and the "tick" of tweens
// is controlled by the behavior)
gdjs.registerRuntimeScenePreEventsCallback(function (runtimeScene) {
currentTweenTime = runtimeScene.getTimeManager().getTimeFromStart();
shifty.processTweens();
});
// Set up Shifty.js so that the processing ("tick"/updates) is handled
// by the behavior, once per frame. See above.
shifty.Tweenable.setScheduleFunction(function () {
/* Do nothing, we'll call processTweens manually. */
});
// Set up Shifty.js so that the time is handled by the behavior.
// It will be set to be the time of the current scene, and should be updated
// before any tween processing (processTweens, resume).
shifty.Tweenable.now = function () {
return currentTweenTime;
};

View File

@@ -1,9 +1,5 @@
/// <reference path="shifty.d.ts" />
namespace gdjs {
export interface RuntimeScene {
shiftyJsScene: shifty.Scene;
}
interface IScaleable extends RuntimeObject {
setScaleX(x: number): void;
setScaleY(y: number): void;
@@ -1048,70 +1044,4 @@ namespace gdjs {
}
}
}
// Callbacks called to pause/resume Shifty scene when a gdjs.RuntimeScene
// is paused/resumed
/**
* Stop and "destroy" all the tweens when a scene is unloaded.
*/
gdjs.registerRuntimeSceneUnloadedCallback(function (runtimeScene) {
const shiftyJsScene = runtimeScene.shiftyJsScene;
if (!shiftyJsScene) return;
// Stop and explicitly remove all tweenables to be sure to drop
// all references to the tweenables of the scene.
shiftyJsScene.stop(false);
shiftyJsScene.empty();
});
/**
* When a scene is paused, pause all the tweens of this scene.
*/
gdjs.registerRuntimeScenePausedCallback(function (runtimeScene) {
const shiftyJsScene = runtimeScene.shiftyJsScene;
if (shiftyJsScene) shiftyJsScene.pause();
});
/**
* When a scene is paused, resume all the tweens of this scene.
*/
gdjs.registerRuntimeSceneResumedCallback(function (runtimeScene) {
const shiftyJsScene = runtimeScene.shiftyJsScene;
if (!shiftyJsScene) return;
// It is important to set immediately the current Shifty time back to the
// time of the scene, as the call `resume` will process the tweens.
// (If not done, tweens will be resumed with the time of the previous
// scene, that could create weird result/make tweens act as if not paused).
TweenRuntimeBehavior._currentTweenTime = runtimeScene
.getTimeManager()
.getTimeFromStart();
// Note that per the invariant of shiftyJsScene, shiftyJsScene will only
// contains tweenables that should be playing (so calling resume is safe).
shiftyJsScene.resume();
});
// Handle Shifty.js updates (the time and the "tick" of tweens
// is controlled by the behavior)
gdjs.registerRuntimeScenePreEventsCallback(function (runtimeScene) {
TweenRuntimeBehavior._currentTweenTime = runtimeScene
.getTimeManager()
.getTimeFromStart();
shifty.processTweens();
});
// Set up Shifty.js so that the processing ("tick"/updates) is handled
// by the behavior, once per frame. See above.
shifty.Tweenable.setScheduleFunction(function () {
/* Do nothing, we'll call processTweens manually. */
});
// Set up Shifty.js so that the time is handled by the behavior.
// It will be set to be the time of the current scene, and should be updated
// before any tween processing (processTweens, resume).
shifty.Tweenable.now = function () {
return TweenRuntimeBehavior._currentTweenTime;
};
}

View File

@@ -1,9 +1,11 @@
/// <reference path="shifty.d.ts" />
namespace gdjs {
export interface RuntimeScene {
_tweens: Map<string, shifty.Tweenable>;
}
export namespace evtTools {
export namespace tween {
const easingFunctions: { [key: string]: shifty.easingFunction } = {
const easingFunctions: Record<string, shifty.easingFunction> = {
linear: shifty.Tweenable.formulas.linear,
easeInQuad: shifty.Tweenable.formulas.easeInQuad,
easeOutQuad: shifty.Tweenable.formulas.easeOutQuad,
@@ -59,6 +61,127 @@ namespace gdjs {
: shifty.Tweenable.formulas.linear;
return fromValue + (toValue - fromValue) * easingFunction(weighting);
};
const getTweensMap = (runtimeScene: RuntimeScene) =>
runtimeScene._tweens || (runtimeScene._tweens = new Map());
const getShiftyScene = (runtimeScene: RuntimeScene) =>
runtimeScene.shiftyJsScene ||
(runtimeScene.shiftyJsScene = new shifty.Scene());
export const sceneTweenExists = (
runtimeScene: RuntimeScene,
id: string
) => getTweensMap(runtimeScene).has(id);
export const sceneTweenIsPlaying = (
runtimeScene: RuntimeScene,
id: string
) => {
const tweenMap = getTweensMap(runtimeScene);
const tween = tweenMap.get(id);
return !!tween && tween.isPlaying();
};
export const sceneTweenHasFinished = (
runtimeScene: RuntimeScene,
id: string
) => {
const tweenMap = getTweensMap(runtimeScene);
const tween = tweenMap.get(id);
return !!tween && tween.hasEnded();
};
export const resumeSceneTween = (
runtimeScene: RuntimeScene,
id: string
) => {
const tweenMap = getTweensMap(runtimeScene);
const tween = tweenMap.get(id);
if (!tween) return;
tween.resume();
getShiftyScene(runtimeScene).add(tween);
};
export const pauseSceneTween = (
runtimeScene: RuntimeScene,
id: string
) => {
const tweenMap = getTweensMap(runtimeScene);
const tween = tweenMap.get(id);
if (!tween) return;
tween.pause();
getShiftyScene(runtimeScene).remove(tween);
};
export const stopSceneTween = (
runtimeScene: RuntimeScene,
id: string,
shouldGoToEnd: boolean
) => {
const tweenMap = getTweensMap(runtimeScene);
const tween = tweenMap.get(id);
if (!tween) return;
tween.stop(shouldGoToEnd);
getShiftyScene(runtimeScene).remove(tween);
};
export const removeSceneTween = (
runtimeScene: RuntimeScene,
id: string
) => {
const tweenMap = getTweensMap(runtimeScene);
const tween = tweenMap.get(id);
if (!tween) return;
tweenMap.delete(id);
getShiftyScene(runtimeScene).remove(tween);
tween.stop().dispose();
};
export const tweenVariableNumber = (
runtimeScene: RuntimeScene,
identifier: string,
variable: Variable,
from: number,
to: number,
duration: number,
easing: shifty.easingFunction
) => {
const tween = shifty.tween({
from: { value: from },
to: { value: to },
easing,
duration,
render: ({ value }) => variable.setNumber(value),
});
getTweensMap(runtimeScene).set(identifier, tween);
getShiftyScene(runtimeScene).add(tween);
};
export const tweenCamera = (
runtimeScene: RuntimeScene,
identifier: string,
toX: number,
toY: number,
layerName: string,
duration: number,
easing: shifty.easingFunction
) => {
const layer = runtimeScene.getLayer(layerName);
const tween = shifty.tween({
from: { x: layer.getCameraX(), y: layer.getCameraY() },
to: { x: toX, y: toY },
easing,
duration,
render: ({ x, y }) => {
layer.setCameraX(x);
layer.setCameraY(y);
},
});
getTweensMap(runtimeScene).set(identifier, tween);
getShiftyScene(runtimeScene).add(tween);
};
}
}
}

View File

@@ -45,8 +45,6 @@ set(source_files ${source_files} ${f3})
ENDIF()
file(GLOB_RECURSE formatted_source_files GDJS/Events/* GDJS/Extensions/* GDJS/IDE/*)
list(REMOVE_ITEM formatted_source_files "${CMAKE_CURRENT_SOURCE_DIR}/GDJS/IDE/Dialogs/GDJSDialogs.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GDJS/IDE/Dialogs/GDJSDialogs.h" "${CMAKE_CURRENT_SOURCE_DIR}/GDJS/IDE/Dialogs/GDJSDialogsBitmaps.cpp")
list(REMOVE_ITEM formatted_source_files "${CMAKE_CURRENT_SOURCE_DIR}/GDJS/IDE/mongoose/mongoose.c" "${CMAKE_CURRENT_SOURCE_DIR}/GDJS/IDE/mongoose/mongoose.h")
gd_add_clang_utils(GDJS "${formatted_source_files}")

View File

@@ -408,7 +408,8 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
" return null;\n" +
" },\n"
// Function to count instances on the scene. We need it here because
// it needs the objects map to get the object names of the parent context.
// it needs the objects map to get the object names of the parent
// context.
" getInstancesCountOnScene: function(objectName) {\n"
" const objectsList = "
"eventsFunctionContext._objectsMap[objectName];\n" +
@@ -679,6 +680,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
gd::String EventsCodeGenerator::GenerateObjectAction(
const gd::String& objectName,
const gd::ObjectMetadata& objInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -697,25 +699,19 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
call = GenerateOperatorCall(
instrInfos,
arguments,
objectPart + instrInfos.codeExtraInformation.functionCallName,
objectPart + functionCallName,
objectPart +
instrInfos.codeExtraInformation.optionalAssociatedInstruction,
1);
else if (instrInfos.codeExtraInformation.accessType ==
gd::InstructionMetadata::ExtraInformation::Mutators)
call = GenerateMutatorCall(
instrInfos,
arguments,
objectPart + instrInfos.codeExtraInformation.functionCallName,
1);
instrInfos, arguments, objectPart + functionCallName, 1);
else
call = GenerateCompoundOperatorCall(
instrInfos,
arguments,
objectPart + instrInfos.codeExtraInformation.functionCallName,
1);
instrInfos, arguments, objectPart + functionCallName, 1);
} else {
call = objectPart + instrInfos.codeExtraInformation.functionCallName + "(" +
call = objectPart + functionCallName + "(" +
GenerateArgumentsList(arguments, 1) + ")";
}
@@ -743,6 +739,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
const gd::String& objectName,
const gd::String& behaviorName,
const gd::BehaviorMetadata& autoInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -763,25 +760,19 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
call = GenerateOperatorCall(
instrInfos,
arguments,
objectPart + instrInfos.codeExtraInformation.functionCallName,
objectPart + functionCallName,
objectPart +
instrInfos.codeExtraInformation.optionalAssociatedInstruction,
2);
else if (instrInfos.codeExtraInformation.accessType ==
gd::InstructionMetadata::ExtraInformation::Mutators)
call = GenerateMutatorCall(
instrInfos,
arguments,
objectPart + instrInfos.codeExtraInformation.functionCallName,
2);
instrInfos, arguments, objectPart + functionCallName, 2);
else
call = GenerateCompoundOperatorCall(
instrInfos,
arguments,
objectPart + instrInfos.codeExtraInformation.functionCallName,
2);
instrInfos, arguments, objectPart + functionCallName, 2);
} else {
call = objectPart + instrInfos.codeExtraInformation.functionCallName + "(" +
call = objectPart + functionCallName + "(" +
GenerateArgumentsList(arguments, 2) + ")";
}
@@ -833,30 +824,34 @@ gd::String EventsCodeGenerator::GenerateGetBehaviorNameCode(
gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
gd::EventsCodeGenerationContext& context) {
auto declareObjectListFromParent = [this](gd::String object,
gd::EventsCodeGenerationContext& context) {
gd::String objectListName = GetObjectListName(object, context);
if (!context.GetParentContext()) {
std::cout << "ERROR: During code generation, a context tried to use an "
"already declared object list without having a parent"
<< std::endl;
return "/* Could not declare " + objectListName + " */";
}
auto declareObjectListFromParent =
[this](gd::String object, gd::EventsCodeGenerationContext& context) {
gd::String objectListName = GetObjectListName(object, context);
if (!context.GetParentContext()) {
std::cout
<< "ERROR: During code generation, a context tried to use an "
"already declared object list without having a parent"
<< std::endl;
return "/* Could not declare " + objectListName + " */";
}
if (context.ShouldUseAsyncObjectsList(object)) {
gd::String copiedListName = "asyncObjectsList.getObjects(" + ConvertToStringExplicit(object) + ")";
return "gdjs.copyArray(" + copiedListName + ", " + objectListName + ");\n";
}
if (context.ShouldUseAsyncObjectsList(object)) {
gd::String copiedListName = "asyncObjectsList.getObjects(" +
ConvertToStringExplicit(object) + ")";
return "gdjs.copyArray(" + copiedListName + ", " + objectListName +
");\n";
}
//*Optimization*: Avoid expensive copy of the object list if we're using
// the same list as the one from the parent context.
if (context.IsSameObjectsList(object, *context.GetParentContext()))
return "/* Reuse " + objectListName + " */";
//*Optimization*: Avoid expensive copy of the object list if we're using
// the same list as the one from the parent context.
if (context.IsSameObjectsList(object, *context.GetParentContext()))
return "/* Reuse " + objectListName + " */";
gd::String copiedListName =
GetObjectListName(object, *context.GetParentContext());
return "gdjs.copyArray(" + copiedListName + ", " + objectListName + ");\n";
};
gd::String copiedListName =
GetObjectListName(object, *context.GetParentContext());
return "gdjs.copyArray(" + copiedListName + ", " + objectListName +
");\n";
};
gd::String declarationsCode;
for (auto object : context.GetObjectsListsToBeDeclared()) {
@@ -1045,8 +1040,7 @@ gd::String EventsCodeGenerator::GenerateObject(
objectsMapName += "Empty" + ManObjListName(objectName);
if (!mapDeclaration.empty()) mapDeclaration += ", ";
mapDeclaration += "\"" + ConvertToString(objectName) +
"\": []";
mapDeclaration += "\"" + ConvertToString(objectName) + "\": []";
}
// TODO: this should be de-duplicated.
@@ -1072,7 +1066,8 @@ gd::String EventsCodeGenerator::GenerateObject(
gd::String objectsMapName = declareMapOfObjects(realObjects, context);
output = objectsMapName;
} else if (type == "objectListOrEmptyWithoutPicking") {
std::vector<gd::String> realObjects = ExpandObjectsName(objectName, context);
std::vector<gd::String> realObjects =
ExpandObjectsName(objectName, context);
// Find the objects not yet declared, and handle them separately so they are
// passed as empty object lists.
@@ -1087,7 +1082,8 @@ gd::String EventsCodeGenerator::GenerateObject(
}
}
gd::String objectsMapName = declareMapOfObjects(objectToBeDeclaredNames, context, objectNotYetDeclaredNames);
gd::String objectsMapName = declareMapOfObjects(
objectToBeDeclaredNames, context, objectNotYetDeclaredNames);
output = objectsMapName;
} else if (type == "objectPtr") {
std::vector<gd::String> realObjects =

View File

@@ -227,6 +227,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
virtual gd::String GenerateObjectAction(
const gd::String& objectName,
const gd::ObjectMetadata& objInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,
@@ -236,6 +237,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
const gd::String& objectName,
const gd::String& behaviorName,
const gd::BehaviorMetadata& autoInfo,
const gd::String& functionCallName,
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
gd::EventsCodeGenerationContext& context,

View File

@@ -18,8 +18,9 @@ NetworkExtension::NetworkExtension() {
GetAllActions()["SendRequest"].SetFunctionName(
"gdjs.evtTools.network.sendDeprecatedSynchronousRequest");
GetAllActions()["SendAsyncRequest"].SetFunctionName(
"gdjs.evtTools.network.sendAsyncRequest");
GetAllActions()["SendAsyncRequest"]
.SetFunctionName("gdjs.evtTools.network.sendAsyncRequest")
.SetAsyncFunctionName("gdjs.evtTools.network.sendAwaitableAsyncRequest");
GetAllActions()["EnableMetrics"].SetFunctionName(
"gdjs.evtTools.network.enableMetrics");
GetAllActions()["LaunchFile"].SetFunctionName("gdjs.evtTools.window.openURL");

View File

@@ -4,6 +4,7 @@
* reserved. This project is released under the MIT License.
*/
#include "TimeExtension.h"
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Tools/Localization.h"
@@ -16,7 +17,7 @@ TimeExtension::TimeExtension() {
GetAllConditions()["Timer"].SetFunctionName(
"gdjs.evtTools.runtimeScene.timerElapsedTime"); // Deprecated
GetAllConditions()["CompareTimer"].SetFunctionName(
"gdjs.evtTools.runtimeScene.getTimerElapsedTimeInSecondsOrNaN");
"gdjs.evtTools.runtimeScene.getTimerElapsedTimeInSecondsOrNaN");
GetAllConditions()["TimerPaused"].SetFunctionName(
"gdjs.evtTools.runtimeScene.timerPaused");
GetAllActions()["ResetTimer"].SetFunctionName(
@@ -27,7 +28,7 @@ TimeExtension::TimeExtension() {
"gdjs.evtTools.runtimeScene.unpauseTimer");
GetAllActions()["RemoveTimer"].SetFunctionName(
"gdjs.evtTools.runtimeScene.removeTimer");
GetAllActions()["Wait"].SetFunctionName(
GetAllActions()["Wait"].SetAsyncFunctionName(
"gdjs.evtTools.runtimeScene.wait");
GetAllConditions()["TimeScale"].SetFunctionName(
"gdjs.evtTools.runtimeScene.getTimeScale");

View File

@@ -121,6 +121,11 @@ bool Exporter::ExportWholePixiProject(
fs, exportedProject, codeOutputDir + "/data.js", noRuntimeGameOptions);
includesFiles.push_back(codeOutputDir + "/data.js");
// Export a WebManifest with project metadata
if (!fs.WriteToFile(exportDir + "/manifest.webmanifest",
helper.GenerateWebManifest(exportedProject)))
gd::LogError("Unable to export WebManifest.");
helper.ExportIncludesAndLibs(includesFiles, exportDir, false);
gd::String source = gdjsRoot + "/Runtime/index.html";

View File

@@ -9,6 +9,7 @@
#include <emscripten.h>
#endif
#include <algorithm>
#include <array>
#include <fstream>
#include <functional>
#include <sstream>
@@ -552,6 +553,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
InsertUnique(includesFiles, "oncetriggers.js");
InsertUnique(includesFiles, "runtimebehavior.js");
InsertUnique(includesFiles, "spriteruntimeobject.js");
InsertUnique(includesFiles, "affinetransformation.js");
// Common includes for events only.
InsertUnique(includesFiles, "events-tools/commontools.js");
@@ -844,4 +846,74 @@ void ExporterHelper::AddDeprecatedFontFilesToFontResources(
// end of compatibility code
}
const std::array<int, 20> IOS_ICONS_SIZES = {
180, 60, 120, 76, 152, 40, 80, 57, 114, 72,
144, 167, 29, 58, 87, 50, 20, 100, 167, 1024,
};
const std::array<int, 6> ANDROID_ICONS_SIZES = {36, 48, 72, 96, 144, 192};
const gd::String ExporterHelper::GenerateWebManifest(
const gd::Project &project) {
const gd::String &orientation = project.GetOrientation();
gd::String icons = "[";
{
std::map<int, gd::String> resourcesForSizes;
const auto getFileNameForIcon = [&project](const gd::String &platform,
const int size) {
const gd::String iconName = "icon-" + gd::String::From(size);
return project.GetPlatformSpecificAssets().Has(platform, iconName)
? project.GetResourcesManager()
.GetResource(project.GetPlatformSpecificAssets().Get(
platform, iconName))
.GetFile()
: "";
};
for (const int size : IOS_ICONS_SIZES) {
const auto iconFile = getFileNameForIcon("ios", size);
if (!iconFile.empty()) resourcesForSizes[size] = iconFile;
};
for (const int size : ANDROID_ICONS_SIZES) {
const auto iconFile = getFileNameForIcon("android", size);
if (!iconFile.empty()) resourcesForSizes[size] = iconFile;
};
const auto desktopIconFile = getFileNameForIcon("desktop", 512);
if (!desktopIconFile.empty()) resourcesForSizes[512] = desktopIconFile;
for (const auto &sizeAndFile : resourcesForSizes) {
icons +=
gd::String(R"({
"src": "{FILE}",
"sizes": "{SIZE}x{SIZE}"
},)")
.FindAndReplace("{SIZE}", gd::String::From(sizeAndFile.first))
.FindAndReplace("{FILE}", sizeAndFile.second);
}
}
icons = icons.RightTrim(",") + "]";
return gd::String(R"webmanifest({
"name": "{NAME}",
"short_name": "{NAME}",
"id": "{PACKAGE_ID}",
"description": "{DESCRIPTION}",
"orientation": "{ORIENTATION}",
"start_url": "./index.html",
"display": "standalone",
"background_color": "black",
"categories": ["games", "entertainment"],
"icons": {ICONS}
})webmanifest")
.FindAndReplace("{NAME}", project.GetName())
.FindAndReplace("{PACKAGE_ID}", project.GetPackageName())
.FindAndReplace("{DESCRIPTION}", project.GetDescription())
.FindAndReplace("{ORIENTATION}",
orientation == "default" ? "any" : orientation)
.FindAndReplace("{ICONS}", icons);
};
} // namespace gdjs

View File

@@ -117,11 +117,12 @@ struct PreviewExportOptions {
/**
* Set the path to use for the game engine to require "@electron/remote".
* This is because the preview is run in a folder without any node_module, but this
* is still required for now for some features.
* This should be removed once the dependency to "@electron/remote" is removed.
* This is because the preview is run in a folder without any node_module, but
* this is still required for now for some features. This should be removed
* once the dependency to "@electron/remote" is removed.
*/
PreviewExportOptions &SetElectronRemoteRequirePath(const gd::String& electronRemoteRequirePath_) {
PreviewExportOptions &SetElectronRemoteRequirePath(
const gd::String &electronRemoteRequirePath_) {
electronRemoteRequirePath = electronRemoteRequirePath_;
return *this;
}
@@ -303,6 +304,15 @@ class ExporterHelper {
unsigned int nonRuntimeScriptsCacheBurst,
gd::String additionalSpec);
/**
* \brief Generates a WebManifest, a metadata file that allow to make the
* exported game a working PWA.
*
* \param project The project containing the game properties to generate the
* manifest from.
*/
const gd::String GenerateWebManifest(const gd::Project &project);
/**
* \brief Generate the Cordova configuration file and save it to the export
* directory.

View File

@@ -0,0 +1,493 @@
namespace gdjs {
/**
* An affine transformation that can transform points.
*/
export class AffineTransformation {
private matrix: Float32Array;
/**
* Initialize to the identity.
*/
constructor() {
// | 1 0 0 |
// | 0 1 0 |
// | 0 0 1 |
this.matrix = new Float32Array([1, 0, 0, 1, 0, 0]);
}
/**
* Reset to the identity.
*/
setToIdentity() {
const matrix = this.matrix;
// | 1 0 0 |
// | 0 1 0 |
// | 0 0 1 |
matrix[0] = 1;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 1;
matrix[4] = 0;
matrix[5] = 0;
}
/**
* Check if this transformation is the identity.
*/
isIdentity(): boolean {
const matrix = this.matrix;
return (
matrix[0] === 1 &&
matrix[1] === 0 &&
matrix[2] === 0 &&
matrix[3] === 1 &&
matrix[4] === 0 &&
matrix[5] === 0
);
}
/**
* Check if this is equals to another transformation.
* @param other The transformation to check.
*/
equals(other: AffineTransformation): boolean {
const matrix = this.matrix;
const otherMatrix = other.matrix;
return (
this === other ||
(matrix[0] === otherMatrix[0] &&
matrix[1] === otherMatrix[1] &&
matrix[2] === otherMatrix[2] &&
matrix[3] === otherMatrix[3] &&
matrix[4] === otherMatrix[4] &&
matrix[5] === otherMatrix[5])
);
}
/**
* Check if this is almost equals to another transformation.
* @param other The transformation to check.
* @param epsilon The relative margin error.
*/
nearlyEquals(other: AffineTransformation, epsilon: float): boolean {
const matrix = this.matrix;
const otherMatrix = other.matrix;
return (
this === other ||
(gdjs.nearlyEqual(matrix[0], otherMatrix[0], epsilon) &&
gdjs.nearlyEqual(matrix[1], otherMatrix[1], epsilon) &&
gdjs.nearlyEqual(matrix[2], otherMatrix[2], epsilon) &&
gdjs.nearlyEqual(matrix[3], otherMatrix[3], epsilon) &&
gdjs.nearlyEqual(matrix[4], otherMatrix[4], epsilon) &&
gdjs.nearlyEqual(matrix[5], otherMatrix[5], epsilon))
);
}
/**
* Copy a transformation.
* @param other The transformation to copy.
*/
copyFrom(other: AffineTransformation) {
const matrix = this.matrix;
const otherMatrix = other.matrix;
matrix[0] = otherMatrix[0];
matrix[1] = otherMatrix[1];
matrix[2] = otherMatrix[2];
matrix[3] = otherMatrix[3];
matrix[4] = otherMatrix[4];
matrix[5] = otherMatrix[5];
return this;
}
/**
* Reset to a translation.
*
* @param x The horizontal translation value.
* @param y The vertical translation value.
*/
setToTranslation(tx: float, ty: float) {
const matrix = this.matrix;
// | m0 m2 m4 | | 1 0 tx |
// | m1 m3 m5 | = | 0 1 ty |
// | 0 0 1 | | 0 0 1 |
matrix[0] = 1;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 1;
matrix[4] = tx;
matrix[5] = ty;
}
/**
* Concatenate a translation.
*
* @param tx The horizontal translation value.
* @param ty The vertical translation value.
*/
translate(tx: float, ty: float) {
var matrix = this.matrix;
// 1 0 tx
// 0 1 ty
// 0 0 1
// m0 m2 m4
// m1 m3 m5
// 0 0 1
matrix[4] = matrix[0] * tx + matrix[2] * ty + matrix[4];
matrix[5] = matrix[1] * tx + matrix[3] * ty + matrix[5];
}
/**
* Reset to a scale.
*
* @param sx The horizontal scale value.
* @param sy The vertical scale value.
*/
setToScale(sx: float, sy: float) {
const matrix = this.matrix;
// | m0 m2 m4 | | sx 0 0 |
// | m1 m3 m5 | = | 0 sy 0 |
// | 0 0 1 | | 0 0 1 |
matrix[0] = sx;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = sy;
matrix[4] = 0;
matrix[5] = 0;
}
/**
* Concatenate a scale.
*
* @param sx The horizontal scale value.
* @param sy The vertical scale value.
*/
scale(sx: float, sy: float) {
const matrix = this.matrix;
// sx 0 0
// 0 sy 0
// 0 0 1
// m0 m2 m4
// m1 m3 m5
// 0 0 1
matrix[0] *= sx;
matrix[1] *= sx;
matrix[2] *= sy;
matrix[3] *= sy;
}
/**
* Reset to a rotation.
*
* @param angle The angle of rotation in radians.
*/
setToRotation(theta: float) {
const matrix = this.matrix;
let cost = Math.cos(theta);
let sint = Math.sin(theta);
// Avoid rounding errors around 0.
if (cost === -1 || cost === 1) {
sint = 0;
}
if (sint === -1 || sint === 1) {
cost = 0;
}
// | m0 m2 m4 | | cost -sint 0 |
// | m1 m3 m5 | = | sint cost 0 |
// | 0 0 1 | | 0 0 1 |
matrix[0] = cost;
matrix[1] = sint;
matrix[2] = -sint;
matrix[3] = cost;
matrix[4] = 0;
matrix[5] = 0;
}
/**
* Concatenate a rotation.
*
* @param angle The angle of rotation in radians.
*/
rotate(angle: float) {
const matrix = this.matrix;
let cost = Math.cos(angle);
let sint = Math.sin(angle);
// Avoid rounding errors around 0.
if (cost === -1 || cost === 1) {
sint = 0;
}
if (sint === -1 || sint === 1) {
cost = 0;
}
// cost -sint 0
// sint cost 0
// 0 0 1
// m0 m2 m4
// m1 m3 m5
// 0 0 1
const m0 = matrix[0];
const m1 = matrix[1];
const m2 = matrix[2];
const m3 = matrix[3];
matrix[0] = m0 * cost + m2 * sint;
matrix[1] = m1 * cost + m3 * sint;
matrix[2] = m0 * -sint + m2 * cost;
matrix[3] = m1 * -sint + m3 * cost;
}
/**
* Reset to a rotation.
*
* @param angle The angle of rotation in radians.
* @param anchorX The rotation anchor point X.
* @param anchorY The rotation anchor point Y.
*/
setToRotationAround(angle: float, anchorX: float, anchorY: float) {
const matrix = this.matrix;
let cost = Math.cos(angle);
let sint = Math.sin(angle);
// Avoid rounding errors around 0.
if (cost === -1 || cost === 1) {
sint = 0;
}
if (sint === -1 || sint === 1) {
cost = 0;
}
// | m0 m2 m4 | | cost -sint x-x*cost+y*sint |
// | m1 m3 m5 | = | sint cost y-x*sint-y*cost |
// | 0 0 1 | | 0 0 1 |
matrix[0] = cost;
matrix[1] = sint;
matrix[2] = -sint;
matrix[3] = cost;
matrix[4] = anchorX - anchorX * cost + anchorY * sint;
matrix[5] = anchorY - anchorX * sint + anchorY * cost;
}
/**
* Concatenate a rotation.
*
* @param angle The angle of rotation in radians.
* @param anchorX The rotation anchor point X.
* @param anchorY The rotation anchor point Y.
*/
rotateAround(angle: float, anchorX: float, anchorY: float) {
this.translate(anchorX, anchorY);
this.rotate(angle);
// First: translate anchor to origin
this.translate(-anchorX, -anchorY);
}
/**
* Reset to an horizontal flip.
*
* @param anchorX The flip anchor point X.
*/
setToFlipX(anchorX: float) {
const matrix = this.matrix;
// | m0 m2 m4 | | -1 0 2x |
// | m1 m3 m5 | = | 0 1 0 |
// | 0 0 1 | | 0 0 1 |
matrix[0] = -1;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 1;
matrix[4] = 2 * anchorX;
matrix[5] = 0;
}
/**
* Concatenate an horizontal flip.
*
* @param anchorX The flip anchor point X.
*/
flipX(anchorX: float) {
this.translate(anchorX, 0);
this.scale(-1, 1);
// First: translate anchor to origin
this.translate(-anchorX, 0);
}
/**
* Reset to an vertical flip.
*
* @param anchorY The flip anchor point Y.
*/
setToFlipY(anchorY: float) {
const matrix = this.matrix;
// | m0 m2 m4 | | 1 0 0 |
// | m1 m3 m5 | = | 0 -1 2x |
// | 0 0 1 | | 0 0 1 |
matrix[0] = -1;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 1;
matrix[4] = 0;
matrix[5] = 2 * anchorY;
}
/**
* Concatenate an vertical flip.
*
* @param anchorY The flip anchor point Y.
*/
flipY(anchorY: float) {
this.translate(0, anchorY);
this.scale(1, -1);
// First: translate anchor to origin
this.translate(0, -anchorY);
}
/**
* Concatenate a flip between X and Y.
*/
flipDiagonally() {
const matrix = this.matrix;
const m0 = matrix[0];
const m1 = matrix[1];
const m2 = matrix[2];
const m3 = matrix[3];
const m4 = matrix[4];
const m5 = matrix[5];
matrix[0] = m1;
matrix[1] = m0;
matrix[2] = m3;
matrix[3] = m2;
matrix[4] = m5;
matrix[5] = m4;
}
/**
* Concatenate a transformation after this one.
* @param other The transformation to concatenate.
*/
concatenate(other: AffineTransformation) {
const matrix = this.matrix;
const otherMatrix = other.matrix;
const m0 = matrix[0];
const m1 = matrix[1];
const m2 = matrix[2];
const m3 = matrix[3];
const m4 = matrix[4];
const m5 = matrix[5];
const o0 = otherMatrix[0];
const o1 = otherMatrix[1];
const o2 = otherMatrix[2];
const o3 = otherMatrix[3];
const o4 = otherMatrix[4];
const o5 = otherMatrix[5];
// o0 o2 o4
// o1 o3 o5
// 0 0 1
// m0 m2 m4
// m1 m3 m5
// 0 0 1
matrix[0] = o0 * m0 + o1 * m2;
matrix[1] = o0 * m1 + o1 * m3;
matrix[2] = o2 * m0 + o3 * m2;
matrix[3] = o2 * m1 + o3 * m3;
matrix[4] = o4 * m0 + o5 * m2 + m4;
matrix[5] = o4 * m1 + o5 * m3 + m5;
}
/**
* Concatenate a transformation before this one.
* @param other The transformation to concatenate.
*/
preConcatenate(other: AffineTransformation) {
const matrix = this.matrix;
const otherMatrix = other.matrix;
const m0 = matrix[0];
const m1 = matrix[1];
const m2 = matrix[2];
const m3 = matrix[3];
const m4 = matrix[4];
const m5 = matrix[5];
const o0 = otherMatrix[0];
const o1 = otherMatrix[1];
const o2 = otherMatrix[2];
const o3 = otherMatrix[3];
const o4 = otherMatrix[4];
const o5 = otherMatrix[5];
// m0 m2 m4
// m1 m3 m5
// 0 0 1
// o0 o2 o4
// o1 o3 o5
// 0 0 1
matrix[0] = m0 * o0 + m1 * o2;
matrix[1] = m0 * o1 + m1 * o3;
matrix[2] = m2 * o0 + m3 * o2;
matrix[3] = m2 * o1 + m3 * o3;
matrix[4] = m4 * o0 + m5 * o2 + o4;
matrix[5] = m4 * o1 + m5 * o3 + o5;
}
/**
* Transform a point.
*
* @param source The point to transform.
* @param destination The Point to store the transformed coordinates.
*/
transform(source: FloatPoint, destination: FloatPoint) {
const matrix = this.matrix;
// x
// y
// 1
// m0 m2 m4
// m1 m3 m5
// 0 0 1
const x = matrix[0] * source[0] + matrix[2] * source[1] + matrix[4];
const y = matrix[1] * source[0] + matrix[3] * source[1] + matrix[5];
destination[0] = x;
destination[1] = y;
}
/**
* Invert the matrix.
*/
invert() {
const matrix = this.matrix;
const m0 = matrix[0];
const m1 = matrix[1];
const m2 = matrix[2];
const m3 = matrix[3];
const m4 = matrix[4];
const m5 = matrix[5];
const n = m0 * m3 - m1 * m2;
matrix[0] = m3 / n;
matrix[1] = -m1 / n;
matrix[2] = -m2 / n;
matrix[3] = m0 / n;
matrix[4] = (m2 * m5 - m3 * m4) / n;
matrix[5] = -(m0 * m5 - m1 * m4) / n;
return this;
}
toString() {
const matrix = this.matrix;
return `[[${matrix[0]} ${matrix[1]}] [${matrix[2]} ${matrix[3]}] [${matrix[4]} ${matrix[5]}]]`;
}
}
}

View File

@@ -6,6 +6,8 @@
namespace gdjs {
export namespace evtTools {
export namespace network {
const logger = new gdjs.Logger('Network requests');
/**
* Send an asynchronous request to the specified URL, with the specified (text)
* body, method and contentType (defaults to `application/x-www-form-urlencoded`).
@@ -66,6 +68,37 @@ namespace gdjs {
}
};
export const sendAwaitableAsyncRequest = (
url: string,
body: string,
method: string,
contentType: string,
responseVar: gdjs.Variable,
errorVar: gdjs.Variable
) => {
return new gdjs.PromiseTask(
fetch(url, {
body: method !== 'GET' ? body : undefined,
method,
headers: {
'Content-Type':
contentType || 'application/x-www-form-urlencoded',
},
}).then(
async (response) => {
const result = await response.text();
if (response.status >= 400) {
errorVar.setString('' + response.status);
}
responseVar.setString(result);
},
(error) => {
errorVar.setString('' + error);
}
)
);
};
/**
* @deprecated
*/

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