mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
115 Commits
feature/vi
...
v5.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
23d64aa676 | ||
![]() |
532b86ac58 | ||
![]() |
e1bf859ff4 | ||
![]() |
1ad20ec6c9 | ||
![]() |
b5990ecbe3 | ||
![]() |
f2287dd1ef | ||
![]() |
a8714b8522 | ||
![]() |
ddf0ba7efd | ||
![]() |
9e8491420d | ||
![]() |
075d918619 | ||
![]() |
b68dfe040e | ||
![]() |
053f4a68df | ||
![]() |
216fd30145 | ||
![]() |
fda75e0475 | ||
![]() |
7fe057c180 | ||
![]() |
5d091c0a87 | ||
![]() |
319cea428e | ||
![]() |
0ac2ef7892 | ||
![]() |
fd6b9be49c | ||
![]() |
2d6f0fad90 | ||
![]() |
35f019afa8 | ||
![]() |
f7453a6a1d | ||
![]() |
89570505e6 | ||
![]() |
082318d7e4 | ||
![]() |
59c9812208 | ||
![]() |
62117e42d9 | ||
![]() |
cafa0d512f | ||
![]() |
136964053b | ||
![]() |
6beea7bfaf | ||
![]() |
ab6999a16c | ||
![]() |
937fd1888a | ||
![]() |
755c72c0bf | ||
![]() |
20392d6a79 | ||
![]() |
0a2033db3d | ||
![]() |
6c0fe0359a | ||
![]() |
66ed1110d2 | ||
![]() |
5badb27b35 | ||
![]() |
b7902bb141 | ||
![]() |
b5b3abd155 | ||
![]() |
06b299c4a2 | ||
![]() |
e42d2cbc6d | ||
![]() |
454159db3f | ||
![]() |
4324526689 | ||
![]() |
b57832a131 | ||
![]() |
8d9f5f0df0 | ||
![]() |
b6ec327dfc | ||
![]() |
005aa64aad | ||
![]() |
a18b813140 | ||
![]() |
7cd28062fc | ||
![]() |
2b7e2c8814 | ||
![]() |
767f365a78 | ||
![]() |
5f54583ff5 | ||
![]() |
37c260c19d | ||
![]() |
003f251c9f | ||
![]() |
f8250ec9aa | ||
![]() |
aeb4d278cd | ||
![]() |
2762329dd6 | ||
![]() |
75b1ff5cea | ||
![]() |
1f4042bff0 | ||
![]() |
4e04e79b2f | ||
![]() |
b9ba8e1b7b | ||
![]() |
84ea9a9643 | ||
![]() |
13c44250f2 | ||
![]() |
a5907a6883 | ||
![]() |
5902906bcc | ||
![]() |
4db041bf10 | ||
![]() |
63894f9a86 | ||
![]() |
9a0ec853e7 | ||
![]() |
eb5d120aaf | ||
![]() |
057dd985fc | ||
![]() |
8009e45936 | ||
![]() |
b190731940 | ||
![]() |
a337230195 | ||
![]() |
731b9141fa | ||
![]() |
51ba2e7631 | ||
![]() |
48a0c2d324 | ||
![]() |
25d32ce9bb | ||
![]() |
a7ec57354d | ||
![]() |
cc1d26201e | ||
![]() |
643e3b5c32 | ||
![]() |
0cc4676067 | ||
![]() |
91b895fd92 | ||
![]() |
f95d0ae461 | ||
![]() |
81ce81242b | ||
![]() |
aa71e78507 | ||
![]() |
ee49ca6c14 | ||
![]() |
a649789f4c | ||
![]() |
61e8e95d5b | ||
![]() |
cc158a9250 | ||
![]() |
a91ccacb89 | ||
![]() |
bb9e8a2ea9 | ||
![]() |
3bf40cd46c | ||
![]() |
aa823c1287 | ||
![]() |
24a666ab83 | ||
![]() |
9e652b228d | ||
![]() |
09bedc6ce5 | ||
![]() |
91e57340d4 | ||
![]() |
c385aae845 | ||
![]() |
460b582ab9 | ||
![]() |
3a9f896f04 | ||
![]() |
2851a20787 | ||
![]() |
9077c5d4f7 | ||
![]() |
693b64cddf | ||
![]() |
e6b4373d97 | ||
![]() |
72e705a39a | ||
![]() |
9bc71a42e4 | ||
![]() |
aacef226c4 | ||
![]() |
48c91e5587 | ||
![]() |
43eac4f998 | ||
![]() |
d220c59343 | ||
![]() |
e5f38f626d | ||
![]() |
34673ace70 | ||
![]() |
56b91c4624 | ||
![]() |
c10ae99c4f | ||
![]() |
472c542579 |
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* @4ian
|
@@ -541,7 +541,6 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
if (MetadataProvider::HasBehaviorAction(
|
||||
platform, behaviorType, action.GetType()) &&
|
||||
instrInfos.parameters.size() >= 2) {
|
||||
|
||||
std::vector<gd::String> realObjects =
|
||||
ExpandObjectsName(objectName, context);
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
@@ -1112,6 +1111,33 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
|
||||
}
|
||||
}
|
||||
|
||||
size_t EventsCodeGenerator::GenerateSingleUsageUniqueIdForEventsList() {
|
||||
return eventsListNextUniqueId++;
|
||||
}
|
||||
|
||||
size_t EventsCodeGenerator::GenerateSingleUsageUniqueIdFor(
|
||||
const Instruction* instruction) {
|
||||
if (!instruction) {
|
||||
std::cout << "ERROR: During code generation, a null pointer was passed to "
|
||||
"GenerateSingleUsageUniqueIdFor."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Base the unique id on the adress in memory so that the same instruction
|
||||
// in memory will get the same id across different code generations.
|
||||
size_t uniqueId = (size_t)instruction;
|
||||
|
||||
// While in most case this function is called a single time for each instruction,
|
||||
// it's possible for an instruction to be appearing more than once in the events,
|
||||
// if we used links. In this case, simply increment the unique id to be sure that
|
||||
// ids are effectively uniques, and stay stable (given the same order of links).
|
||||
while (instructionUniqueIds.find(uniqueId) != instructionUniqueIds.end()) {
|
||||
uniqueId++;
|
||||
}
|
||||
instructionUniqueIds.insert(uniqueId);
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GetObjectListName(
|
||||
const gd::String& name, const gd::EventsCodeGenerationContext& context) {
|
||||
return ManObjListName(name);
|
||||
@@ -1140,7 +1166,8 @@ EventsCodeGenerator::EventsCodeGenerator(gd::Project& project_,
|
||||
errorOccurred(false),
|
||||
compilationForRuntime(false),
|
||||
maxCustomConditionsDepth(0),
|
||||
maxConditionsListsSize(0){};
|
||||
maxConditionsListsSize(0),
|
||||
eventsListNextUniqueId(0){};
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(
|
||||
const gd::Platform& platform_,
|
||||
@@ -1155,6 +1182,7 @@ EventsCodeGenerator::EventsCodeGenerator(
|
||||
errorOccurred(false),
|
||||
compilationForRuntime(false),
|
||||
maxCustomConditionsDepth(0),
|
||||
maxConditionsListsSize(0){};
|
||||
maxConditionsListsSize(0),
|
||||
eventsListNextUniqueId(0){};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -123,7 +124,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
*
|
||||
*/
|
||||
std::vector<gd::String> GenerateParametersCodes(
|
||||
const std::vector<gd::Expression> & parameters,
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersInfo,
|
||||
EventsCodeGenerationContext& context,
|
||||
std::vector<std::pair<gd::String, gd::String> >*
|
||||
@@ -321,7 +322,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* group.
|
||||
*
|
||||
* Get a list containing the "real" objects name when the events refers to \a
|
||||
* objectName :<br> If \a objectName if really an object, the list will only
|
||||
* objectName :<br> If \a objectName is really an object, the list will only
|
||||
* contains \a objectName unchanged.<br> If \a objectName is a group, the list
|
||||
* will contains all the objects of the group.<br> If \a objectName is the
|
||||
* "current" object in the context ( i.e: The object being used for launching
|
||||
@@ -411,6 +412,29 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
|
||||
enum VariableScope { LAYOUT_VARIABLE = 0, PROJECT_VARIABLE, OBJECT_VARIABLE };
|
||||
|
||||
/**
|
||||
* Generate a single unique number for the specified instruction.
|
||||
*
|
||||
* This is useful for instructions that need to identify themselves in the
|
||||
* generated code like the "Trigger Once" conditions. The id is stable across
|
||||
* code generations if the instructions are the same objects in memory.
|
||||
*
|
||||
* Note that if this function is called multiple times with the same
|
||||
* instruction, the unique number returned will be *different*. This is
|
||||
* because a single instruction might appear at multiple places in events due
|
||||
* to the usage of links.
|
||||
*/
|
||||
size_t GenerateSingleUsageUniqueIdFor(const gd::Instruction* instruction);
|
||||
|
||||
/**
|
||||
* Generate a single unique number for an events list.
|
||||
*
|
||||
* This is useful to create unique function names for events list, that are
|
||||
* stable across code generation given the exact same list of events. They are
|
||||
* *not* stable if events are moved/reorganized.
|
||||
*/
|
||||
size_t GenerateSingleUsageUniqueIdForEventsList();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Generate the code for a single parameter.
|
||||
@@ -704,7 +728,8 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
/**
|
||||
* Generate the getter to get the name of the specified behavior.
|
||||
*/
|
||||
virtual gd::String GenerateGetBehaviorNameCode(const gd::String& behaviorName);
|
||||
virtual gd::String GenerateGetBehaviorNameCode(
|
||||
const gd::String& behaviorName);
|
||||
|
||||
const gd::Platform& platform; ///< The platform being used.
|
||||
|
||||
@@ -732,6 +757,11 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
size_t maxCustomConditionsDepth; ///< The maximum depth value for all the
|
||||
///< custom conditions created.
|
||||
size_t maxConditionsListsSize; ///< The maximum size of a list of conditions.
|
||||
|
||||
std::set<size_t>
|
||||
instructionUniqueIds; ///< The unique ids generated for instructions.
|
||||
size_t eventsListNextUniqueId; ///< The next identifier to use for an events
|
||||
///< list function name.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -4,9 +4,12 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Events/InstructionsList.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -15,18 +18,14 @@ namespace gd {
|
||||
|
||||
gd::Expression Instruction::badExpression("");
|
||||
|
||||
Instruction::Instruction(gd::String type_)
|
||||
: type(type_),
|
||||
inverted(false) {
|
||||
Instruction::Instruction(gd::String type_) : type(type_), inverted(false) {
|
||||
parameters.reserve(8);
|
||||
}
|
||||
|
||||
Instruction::Instruction(gd::String type_,
|
||||
const std::vector<gd::Expression>& parameters_,
|
||||
bool inverted_)
|
||||
: type(type_),
|
||||
inverted(inverted_),
|
||||
parameters(parameters_) {
|
||||
: type(type_), inverted(inverted_), parameters(parameters_) {
|
||||
parameters.reserve(8);
|
||||
}
|
||||
|
||||
@@ -56,4 +55,17 @@ void Instruction::SetParameter(std::size_t nb, const gd::Expression& val) {
|
||||
parameters[nb] = val;
|
||||
}
|
||||
|
||||
std::shared_ptr<Instruction> GD_CORE_API
|
||||
CloneRememberingOriginalElement(std::shared_ptr<Instruction> instruction) {
|
||||
std::shared_ptr<Instruction> copy =
|
||||
std::make_shared<Instruction>(*instruction);
|
||||
// Original instruction is either the original instruction of the copied
|
||||
// instruction, or the instruction copied.
|
||||
copy->originalInstruction = instruction->originalInstruction.expired()
|
||||
? instruction
|
||||
: instruction->originalInstruction;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
#ifndef INSTRUCTION_H
|
||||
#define INSTRUCTION_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Events/InstructionsList.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -131,6 +133,17 @@ class GD_CORE_API Instruction {
|
||||
*/
|
||||
inline gd::InstructionsList& GetSubInstructions() { return subInstructions; };
|
||||
|
||||
/**
|
||||
* \brief Return the original instruction this instruction was copied from.
|
||||
*
|
||||
* 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; };
|
||||
|
||||
friend std::shared_ptr<Instruction> CloneRememberingOriginalElement(
|
||||
std::shared_ptr<Instruction> instruction);
|
||||
|
||||
private:
|
||||
gd::String type; ///< Instruction type
|
||||
bool inverted; ///< True if the instruction if inverted. Only applicable for
|
||||
@@ -139,9 +152,23 @@ class GD_CORE_API Instruction {
|
||||
parameters; ///< Vector containing the parameters
|
||||
gd::InstructionsList subInstructions; ///< Sub instructions, if applicable.
|
||||
|
||||
std::weak_ptr<Instruction>
|
||||
originalInstruction; ///< Pointer used to remember which gd::Instruction
|
||||
///< this instruction was copied from. Useful to
|
||||
///< ensure the stability of code generation (as
|
||||
///< some part of code generation uses the pointer
|
||||
///< to the instruction as a unique identifier).
|
||||
|
||||
static gd::Expression badExpression;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone the given instruction, returning an instruction for which
|
||||
* `GetOriginalInstruction()` returns the originally copied instruction.
|
||||
*/
|
||||
std::shared_ptr<Instruction> GD_CORE_API
|
||||
CloneRememberingOriginalElement(std::shared_ptr<Instruction> instruction);
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // INSTRUCTION_H
|
||||
|
@@ -34,10 +34,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
"res/actions/net24.png",
|
||||
"res/actions/net.png")
|
||||
.AddParameter("string", _("Host, with protocol"))
|
||||
.SetParameterLongDescription(
|
||||
_("Example: \"http://example.com/\"."))
|
||||
.SetParameterLongDescription(_("Example: \"http://example.com/\"."))
|
||||
.AddParameter("string", _("Path"))
|
||||
.SetParameterLongDescription(_("Example: \"/user/123\" or \"/some-page.php\"."))
|
||||
.SetParameterLongDescription(
|
||||
_("Example: \"/user/123\" or \"/some-page.php\"."))
|
||||
.AddParameter("string", _("Request body content"))
|
||||
.AddParameter("string", _("Method: \"POST\" or \"GET\""), "", true)
|
||||
.SetParameterLongDescription(_("If empty, \"GET\" will be used."))
|
||||
@@ -51,6 +51,52 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
|
||||
"variable. If the server returns *JSON*, you may want to use the "
|
||||
"action \"Convert JSON to a scene variable\" afterwards, to "
|
||||
"explore the results with a *structure variable*."))
|
||||
.MarkAsComplex()
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"SendAsyncRequest",
|
||||
_("Send a request to a web page"),
|
||||
_("Send an asynchronous request to the specified web page.\n\nPlease "
|
||||
"note that for "
|
||||
"the web games, the game must be hosted on the same host "
|
||||
"as specified below, except if the server is configured to answer "
|
||||
"to all requests (cross-domain requests)."),
|
||||
_("Send a _PARAM2_ request to _PARAM0_ with body: _PARAM1_, and "
|
||||
"store the result in _PARAM4_ (or in _PARAM5_ in case of error)"),
|
||||
_("Network"),
|
||||
"res/actions/net24.png",
|
||||
"res/actions/net.png")
|
||||
.AddParameter("string", _("URL (API or web-page address)"))
|
||||
.SetParameterLongDescription(
|
||||
_("Example: \"https://example.com/user/123\". Using *https* is "
|
||||
"highly recommended."))
|
||||
.AddParameter("string", _("Request body content"))
|
||||
.AddParameter("stringWithSelector",
|
||||
_("Resize mode"),
|
||||
"[\"GET\", \"POST\", \"PUT\", \"HEAD\", \"DELETE\", "
|
||||
"\"PATCH\", \"OPTIONS\"]",
|
||||
false)
|
||||
.SetParameterLongDescription(_("If empty, \"GET\" will be used."))
|
||||
.SetDefaultValue("\"GET\"")
|
||||
.AddParameter("string", _("Content type"), "", true)
|
||||
.SetParameterLongDescription(
|
||||
_("If empty, \"application/x-www-form-urlencoded\" will be used."))
|
||||
.AddParameter(
|
||||
"scenevar", _("Variable where to store the response"), "", true)
|
||||
.SetParameterLongDescription(
|
||||
_("The response of the server will be stored, as a string, in this "
|
||||
"variable. If the server returns *JSON*, you may want to use the "
|
||||
"action \"Convert JSON to a scene variable\" afterwards, to "
|
||||
"explore the results with a *structure variable*."))
|
||||
.AddParameter(
|
||||
"scenevar", _("Variable where to store the error message"), "", true)
|
||||
.SetParameterLongDescription(
|
||||
_("Optional, only used if an error occurs. This will contain the "
|
||||
"error message (if request could not be sent) or the [\"status "
|
||||
"code\"](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes), "
|
||||
"if the server returns a status >= 400."))
|
||||
.MarkAsComplex();
|
||||
|
||||
extension
|
||||
|
129
Core/GDCore/Extensions/Metadata/DependencyMetadata.h
Normal file
129
Core/GDCore/Extensions/Metadata/DependencyMetadata.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef DEPENDENCYMETADATA_H
|
||||
#define DEPENDENCYMETADATA_H
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
|
||||
namespace gd {
|
||||
/**
|
||||
* \brief Contains information about a dependency (library, npm/cordova
|
||||
* package, or other according to the export) of an extension.
|
||||
*/
|
||||
class GD_CORE_API DependencyMetadata {
|
||||
public:
|
||||
/**
|
||||
* \brief Sets the name shown to users.
|
||||
*/
|
||||
DependencyMetadata& SetName(const gd::String& name_) {
|
||||
name = name_;
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Sets the name written by the exporter.
|
||||
* Typically, this is what is used by the dependency manager
|
||||
* to find the dependency.
|
||||
*
|
||||
* \example
|
||||
* \code
|
||||
* // For depending upon the NPM package is-thirteen
|
||||
* gd::DependencyMetadata dependencyMetadata = gd::DependencyMetadata();
|
||||
* dependencyMetadata.setExporterName("is-thirteen");
|
||||
* \endcode
|
||||
*/
|
||||
DependencyMetadata& SetExportName(const gd::String& exportName_) {
|
||||
exportName = exportName_;
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Set the version of the dependency to install.
|
||||
* Use an empty string to use the latest version.
|
||||
*/
|
||||
DependencyMetadata& SetVersion(const gd::String& version_) {
|
||||
version = version_;
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Sets the type of dependecy (what will be used to install it)
|
||||
*
|
||||
* This can either be "npm" or "cordova" for now.
|
||||
*/
|
||||
DependencyMetadata& SetDependencyType(const gd::String& dependencyType_) {
|
||||
dependencyType = dependencyType_;
|
||||
if (dependencyType != "npm" && dependencyType != "cordova") {
|
||||
gd::LogWarning("Invalid dependency type: " + dependencyType);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Sets a dependency type specific setting.
|
||||
*/
|
||||
DependencyMetadata& SetExtraSetting(
|
||||
const gd::String& settingName,
|
||||
const gd::PropertyDescriptor& settingValue) {
|
||||
extraData[settingName] = settingValue;
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Mark the dependency to be included in the export only if the
|
||||
* specified setting is not empty.
|
||||
*
|
||||
* If this is called for multiple settings, all settings must be fulfilled for
|
||||
* the dependency to be exported.
|
||||
*/
|
||||
DependencyMetadata& OnlyIfExtraSettingIsNonEmpty(
|
||||
const gd::String& settingName) {
|
||||
nonEmptyExtraSettingsForExport.insert(settingName);
|
||||
return *this;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the list of extra settings that must be fulfilled for the
|
||||
* dependency to be exported.
|
||||
*/
|
||||
const std::set<gd::String>& GetRequiredExtraSettingsForExport() const {
|
||||
return nonEmptyExtraSettingsForExport;
|
||||
};
|
||||
|
||||
const gd::String& GetName() const { return name; };
|
||||
const gd::String& GetExportName() const { return exportName; };
|
||||
const gd::String& GetVersion() const { return version; };
|
||||
const gd::String& GetDependencyType() const {
|
||||
if (dependencyType == "")
|
||||
gd::LogWarning("Dependency has no type, it won't be exported.");
|
||||
return dependencyType;
|
||||
};
|
||||
|
||||
const std::map<gd::String, gd::PropertyDescriptor>& GetAllExtraSettings()
|
||||
const {
|
||||
return extraData;
|
||||
}
|
||||
|
||||
private:
|
||||
gd::String name; ///< The name of the dependency.
|
||||
gd::String exportName; ///< The name used to install the package (example:
|
||||
///< npm package name for npm dependency type).
|
||||
gd::String version; ///< The version of the dependency
|
||||
gd::String dependencyType; ///< The tool used to install the dependency.
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
extraData; ///< Contains dependency type specific additional parameters
|
||||
///< for the dependency.
|
||||
std::set<gd::String>
|
||||
nonEmptyExtraSettingsForExport; ///< The set of extra settings that must
|
||||
///< be non empty for this dependency to
|
||||
///< be included by the exporter.
|
||||
};
|
||||
} // namespace gd
|
||||
#endif // DEPENDENCYMETADATA_H
|
@@ -4,9 +4,12 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/EventMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
@@ -117,6 +120,13 @@ gd::ExpressionMetadata& PlatformExtension::AddStrExpression(
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
gd::DependencyMetadata& PlatformExtension::AddDependency() {
|
||||
extensionDependenciesMetadata.push_back(DependencyMetadata());
|
||||
return extensionDependenciesMetadata.back();
|
||||
}
|
||||
#endif
|
||||
|
||||
gd::ObjectMetadata& PlatformExtension::AddObject(
|
||||
const gd::String& name,
|
||||
const gd::String& fullname,
|
||||
@@ -216,8 +226,7 @@ std::vector<gd::String> PlatformExtension::GetExtensionObjectsTypes() const {
|
||||
|
||||
std::vector<gd::String> PlatformExtension::GetExtensionEffectTypes() const {
|
||||
std::vector<gd::String> effectNames;
|
||||
for (auto& it : effectsMetadata)
|
||||
effectNames.push_back(it.first);
|
||||
for (auto& it : effectsMetadata) effectNames.push_back(it.first);
|
||||
|
||||
return effectNames;
|
||||
}
|
||||
@@ -283,6 +292,10 @@ PlatformExtension::GetAllStrExpressions() {
|
||||
return strExpressionsInfos;
|
||||
}
|
||||
|
||||
std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() {
|
||||
return extensionDependenciesMetadata;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::EventMetadata>& PlatformExtension::GetAllEvents() {
|
||||
return eventsInfos;
|
||||
}
|
||||
@@ -404,7 +417,7 @@ void PlatformExtension::SetNameSpace(gd::String nameSpace_) {
|
||||
name == "BuiltinCommonConversions" ||
|
||||
name == "BuiltinStringInstructions" ||
|
||||
name == "BuiltinMathematicalTools" ||
|
||||
name == "Effects" || // Well-known effects are not namespaced.
|
||||
name == "Effects" || // Well-known effects are not namespaced.
|
||||
name == "CommonDialogs") // New name for BuiltinInterface
|
||||
{
|
||||
nameSpace = "";
|
||||
|
@@ -10,11 +10,14 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/EventMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/VersionPriv.h"
|
||||
|
||||
@@ -25,6 +28,7 @@ class ExpressionMetadata;
|
||||
class ObjectMetadata;
|
||||
class BehaviorMetadata;
|
||||
class EffectMetadata;
|
||||
class DependencyMetadata;
|
||||
class BaseEvent;
|
||||
class EventMetadata;
|
||||
class EventCodeGenerator;
|
||||
@@ -148,6 +152,8 @@ class GD_CORE_API PlatformExtension {
|
||||
const gd::String& group_,
|
||||
const gd::String& smallicon_);
|
||||
|
||||
gd::DependencyMetadata& AddDependency();
|
||||
|
||||
/**
|
||||
* \brief Declare a new object as being part of the extension.
|
||||
* \note This method does nothing when used for GD C++ runtime.
|
||||
@@ -225,6 +231,15 @@ class GD_CORE_API PlatformExtension {
|
||||
const gd::String& smallicon_,
|
||||
std::shared_ptr<gd::BaseEvent> instance);
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
* \brief Adds a property to the extension.
|
||||
*/
|
||||
gd::PropertyDescriptor& RegisterProperty(const gd::String& name) {
|
||||
return extensionPropertiesMetadata[name];
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Return the name extension user friendly name.
|
||||
*/
|
||||
@@ -365,6 +380,12 @@ class GD_CORE_API PlatformExtension {
|
||||
*/
|
||||
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions();
|
||||
|
||||
/**
|
||||
* \brief Return a reference to a vector containing the metadata of all the
|
||||
* dependencies of the extension.
|
||||
*/
|
||||
std::vector<gd::DependencyMetadata>& GetAllDependencies();
|
||||
|
||||
/**
|
||||
* \brief Return a reference to a map containing the names of the actions,
|
||||
* related to the object type, and the metadata associated with.
|
||||
@@ -437,6 +458,13 @@ class GD_CORE_API PlatformExtension {
|
||||
* generator.
|
||||
*/
|
||||
void StripUnimplementedInstructionsAndExpressions();
|
||||
|
||||
/**
|
||||
* \brief Get all the properties of the extension
|
||||
*/
|
||||
std::map<gd::String, gd::PropertyDescriptor>& GetAllProperties() {
|
||||
return extensionPropertiesMetadata;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -480,7 +508,9 @@ class GD_CORE_API PlatformExtension {
|
||||
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
|
||||
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
|
||||
std::map<gd::String, gd::ExpressionMetadata> strExpressionsInfos;
|
||||
std::vector<gd::DependencyMetadata> extensionDependenciesMetadata;
|
||||
std::map<gd::String, gd::EventMetadata> eventsInfos;
|
||||
std::map<gd::String, gd::PropertyDescriptor> extensionPropertiesMetadata;
|
||||
#endif
|
||||
|
||||
ObjectMetadata badObjectMetadata;
|
||||
|
@@ -49,9 +49,15 @@ InstructionSentenceFormatter::GetAsFormattedText(
|
||||
|
||||
gd::String sentence = metadata.GetSentence();
|
||||
std::replace(sentence.Raw().begin(), sentence.Raw().end(), '\n', ' ');
|
||||
bool parse = true;
|
||||
|
||||
size_t loopCount = 0;
|
||||
bool parse = true;
|
||||
while (parse) {
|
||||
if (loopCount > 40) {
|
||||
break;
|
||||
}
|
||||
loopCount++;
|
||||
|
||||
// Search first parameter
|
||||
parse = false;
|
||||
size_t firstParamPosition = gd::String::npos;
|
||||
|
56
Core/GDCore/Project/ExtensionProperties.cpp
Normal file
56
Core/GDCore/Project/ExtensionProperties.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "ExtensionProperties.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
|
||||
namespace gd {
|
||||
const gd::String ExtensionProperties::defaultValue = "";
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
ExtensionProperties::GetAllExtensionProperties(const gd::String& extensionName,
|
||||
gd::Project& project) {
|
||||
// Create a copy
|
||||
std::map<gd::String, gd::PropertyDescriptor> props(
|
||||
project.GetCurrentPlatform()
|
||||
.GetExtension(extensionName)
|
||||
->GetAllProperties());
|
||||
// Set values
|
||||
for (std::pair<gd::String, gd::PropertyDescriptor> property : props) {
|
||||
if (properties.count(extensionName) > 0 &&
|
||||
properties[extensionName].count(property.first) > 0) {
|
||||
props[property.first].SetValue(properties[extensionName][property.first]);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
};
|
||||
|
||||
void ExtensionProperties::SerializeTo(SerializerElement& element) const {
|
||||
element.ConsiderAsArrayOf("extensionProperties");
|
||||
for (const std::pair<gd::String, std::map<gd::String, gd::String>> extension :
|
||||
properties) {
|
||||
for (const std::pair<gd::String, gd::String> property : extension.second) {
|
||||
SerializerElement& propertyElement =
|
||||
element.AddChild("extensionProperties");
|
||||
propertyElement.AddChild("extension").SetStringValue(extension.first);
|
||||
propertyElement.AddChild("property").SetStringValue(property.first);
|
||||
propertyElement.AddChild("value").SetStringValue(property.second);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ExtensionProperties::UnserializeFrom(const SerializerElement& element) {
|
||||
properties.clear();
|
||||
element.ConsiderAsArrayOf("extensionProperties");
|
||||
for (std::pair<const gd::String, std::shared_ptr<SerializerElement>>
|
||||
extensionProperties : element.GetAllChildren()) {
|
||||
std::shared_ptr<SerializerElement> extensionPropertiesElement =
|
||||
extensionProperties.second;
|
||||
properties
|
||||
[extensionPropertiesElement->GetChild("extension").GetStringValue()]
|
||||
[extensionPropertiesElement->GetChild("property").GetStringValue()] =
|
||||
extensionPropertiesElement->GetChild("value").GetStringValue();
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace gd
|
69
Core/GDCore/Project/ExtensionProperties.h
Normal file
69
Core/GDCore/Project/ExtensionProperties.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_EXTENSIONPROPERTIES_H
|
||||
#define GDCORE_EXTENSIONPROPERTIES_H
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class PropertyDescriptor;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
class GD_CORE_API ExtensionProperties {
|
||||
static const gd::String defaultValue;
|
||||
|
||||
public:
|
||||
const gd::String& GetValue(const gd::String& extension,
|
||||
const gd::String& property) const {
|
||||
if (properties.count(extension) == 0 ||
|
||||
properties.at(extension).count(property) == 0) {
|
||||
return ExtensionProperties::defaultValue;
|
||||
}
|
||||
return properties.at(extension).at(property);
|
||||
};
|
||||
|
||||
void SetValue(const gd::String& extension,
|
||||
const gd::String& property,
|
||||
const gd::String& newValue) {
|
||||
properties[extension][property] = newValue;
|
||||
};
|
||||
|
||||
bool HasProperty(const gd::String& extension, const gd::String& property) {
|
||||
for (std::pair<gd::String, gd::String> propertyPair :
|
||||
properties[extension]) {
|
||||
if (propertyPair.first == property) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetAllExtensionProperties(
|
||||
const gd::String& extensionName, gd::Project& project);
|
||||
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize the Extension Properties.
|
||||
*/
|
||||
virtual void SerializeTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize the Extension Properties.
|
||||
*/
|
||||
virtual void UnserializeFrom(const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
private:
|
||||
std::map<gd::String, std::map<gd::String, gd::String>>
|
||||
properties; ///< The properties of the project
|
||||
};
|
||||
} // namespace gd
|
||||
|
||||
#endif // EXTENSIONPROPERTIES_H
|
@@ -5,10 +5,12 @@
|
||||
*/
|
||||
|
||||
#include "GDCore/Project/InitialInstance.h"
|
||||
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/UUID/UUID.h"
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#endif
|
||||
@@ -27,7 +29,8 @@ InitialInstance::InitialInstance()
|
||||
personalizedSize(false),
|
||||
width(0),
|
||||
height(0),
|
||||
locked(false) {}
|
||||
locked(false),
|
||||
persistentUuid(UUID::MakeUuid4()) {}
|
||||
|
||||
void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
SetObjectName(element.GetStringAttribute("name", "", "nom"));
|
||||
@@ -42,6 +45,9 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
SetLayer(element.GetStringAttribute("layer"));
|
||||
SetLocked(element.GetBoolAttribute("locked", false));
|
||||
|
||||
persistentUuid = element.GetStringAttribute("persistentUuid");
|
||||
if (persistentUuid.empty()) ResetPersistentUuid();
|
||||
|
||||
floatInfos.clear();
|
||||
const SerializerElement& floatPropElement =
|
||||
element.GetChild("numberProperties", 0, "floatInfos");
|
||||
@@ -79,6 +85,9 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("height", GetCustomHeight());
|
||||
element.SetAttribute("locked", IsLocked());
|
||||
|
||||
if (persistentUuid.empty()) persistentUuid = UUID::MakeUuid4();
|
||||
element.SetStringAttribute("persistentUuid", persistentUuid);
|
||||
|
||||
SerializerElement& floatPropElement = element.AddChild("numberProperties");
|
||||
floatPropElement.ConsiderAsArrayOf("property");
|
||||
for (std::map<gd::String, float>::const_iterator floatInfo =
|
||||
@@ -104,6 +113,11 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
GetVariables().SerializeTo(element.AddChild("initialVariables"));
|
||||
}
|
||||
|
||||
InitialInstance& InitialInstance::ResetPersistentUuid() {
|
||||
persistentUuid = UUID::MakeUuid4();
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor>
|
||||
InitialInstance::GetCustomProperties(gd::Project& project, gd::Layout& layout) {
|
||||
@@ -146,13 +160,12 @@ const gd::String& InitialInstance::GetRawStringProperty(
|
||||
return it != stringInfos.end() ? it->second : *badStringProperyValue;
|
||||
}
|
||||
|
||||
void InitialInstance::SetRawFloatProperty(const gd::String& name, float value)
|
||||
{
|
||||
void InitialInstance::SetRawFloatProperty(const gd::String& name, float value) {
|
||||
floatInfos[name] = value;
|
||||
}
|
||||
|
||||
void InitialInstance::SetRawStringProperty(const gd::String& name, const gd::String& value)
|
||||
{
|
||||
void InitialInstance::SetRawStringProperty(const gd::String& name,
|
||||
const gd::String& value) {
|
||||
stringInfos[name] = value;
|
||||
}
|
||||
#endif
|
||||
|
@@ -200,7 +200,7 @@ class GD_CORE_API InitialInstance {
|
||||
/**
|
||||
* \brief Get the value of a float property stored in the instance.
|
||||
* \note Only use this when \a GetCustomProperties is too slow (when rendering
|
||||
* instances for example).
|
||||
* instances for example).
|
||||
* \return the value of the property, or 0 if it does
|
||||
* not exists.
|
||||
*/
|
||||
@@ -209,7 +209,7 @@ class GD_CORE_API InitialInstance {
|
||||
/**
|
||||
* \brief Get the value of a string property stored in the instance.
|
||||
* \note Only use this when \a GetCustomProperties is too slow (when rendering
|
||||
* instances for example).
|
||||
* instances for example).
|
||||
* \return the value of the propety, or an empty
|
||||
* string if it does not exists.
|
||||
*/
|
||||
@@ -240,6 +240,12 @@ class GD_CORE_API InitialInstance {
|
||||
* \brief Unserialize the instances container.
|
||||
*/
|
||||
virtual void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
/**
|
||||
* \brief Reset the persistent UUID used to recognize
|
||||
* the same initial instance between serialization.
|
||||
*/
|
||||
InitialInstance& ResetPersistentUuid();
|
||||
///@}
|
||||
|
||||
// More properties can be stored in floatInfos and stringInfos.
|
||||
@@ -260,6 +266,7 @@ class GD_CORE_API InitialInstance {
|
||||
float height; ///< Object custom height
|
||||
gd::VariablesContainer initialVariables; ///< Instance specific variables
|
||||
bool locked; ///< True if the instance is locked
|
||||
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID, useful for hot reloading.
|
||||
|
||||
static gd::String*
|
||||
badStringProperyValue; ///< Empty string returned by GetRawStringProperty
|
||||
|
@@ -13,7 +13,7 @@ namespace gd {
|
||||
Camera Layer::badCamera;
|
||||
Effect Layer::badEffect;
|
||||
|
||||
Layer::Layer() : isVisible(true) {}
|
||||
Layer::Layer() : isVisible(true), isLightingLayer(false), followBaseLayerCamera(false) {}
|
||||
|
||||
/**
|
||||
* Change cameras count, automatically adding/removing them.
|
||||
@@ -29,6 +29,11 @@ void Layer::SetCameraCount(std::size_t n) {
|
||||
void Layer::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", GetName());
|
||||
element.SetAttribute("visibility", GetVisibility());
|
||||
element.SetAttribute("isLightingLayer", IsLightingLayer());
|
||||
element.SetAttribute("followBaseLayerCamera", IsFollowingBaseLayerCamera());
|
||||
element.SetAttribute("ambientLightColorR", (int)GetAmbientLightColorRed());
|
||||
element.SetAttribute("ambientLightColorG", (int)GetAmbientLightColorGreen());
|
||||
element.SetAttribute("ambientLightColorB", (int)GetAmbientLightColorBlue());
|
||||
|
||||
SerializerElement& camerasElement = element.AddChild("cameras");
|
||||
camerasElement.ConsiderAsArrayOf("camera");
|
||||
@@ -61,6 +66,11 @@ void Layer::SerializeTo(SerializerElement& element) const {
|
||||
void Layer::UnserializeFrom(const SerializerElement& element) {
|
||||
SetName(element.GetStringAttribute("name", "", "Name"));
|
||||
SetVisibility(element.GetBoolAttribute("visibility", true, "Visibility"));
|
||||
SetLightingLayer(element.GetBoolAttribute("isLightingLayer", false));
|
||||
SetFollowBaseLayerCamera(element.GetBoolAttribute("followBaseLayerCamera", false));
|
||||
SetAmbientLightColor(element.GetIntAttribute("ambientLightColorR", 200),
|
||||
element.GetIntAttribute("ambientLightColorG", 200),
|
||||
element.GetIntAttribute("ambientLightColorB", 200));
|
||||
|
||||
// Compatibility with GD <= 3.3
|
||||
if (element.HasChild("Camera")) {
|
||||
|
@@ -51,6 +51,26 @@ class GD_CORE_API Layer {
|
||||
*/
|
||||
bool GetVisibility() const { return isVisible; }
|
||||
|
||||
/**
|
||||
* \brief Set if the layer is a lightining layer or not.
|
||||
*/
|
||||
void SetLightingLayer(bool isLightingLayer_) { isLightingLayer = isLightingLayer_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the layer is a lighting layer.
|
||||
*/
|
||||
bool IsLightingLayer() const { return isLightingLayer; }
|
||||
|
||||
/**
|
||||
* \brief Set if the layer automatically follows the base layer or not.
|
||||
*/
|
||||
void SetFollowBaseLayerCamera(bool followBaseLayerCamera_) { followBaseLayerCamera = followBaseLayerCamera_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the layer follows the base layer.
|
||||
*/
|
||||
bool IsFollowingBaseLayerCamera() const { return followBaseLayerCamera; }
|
||||
|
||||
/** \name Cameras
|
||||
*/
|
||||
///@{
|
||||
@@ -96,6 +116,30 @@ class GD_CORE_API Layer {
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
* Get the ambient light color red component.
|
||||
*/
|
||||
unsigned int GetAmbientLightColorRed() const { return ambientLightColorR; }
|
||||
|
||||
/**
|
||||
* Get the ambient light color green component.
|
||||
*/
|
||||
unsigned int GetAmbientLightColorGreen() const { return ambientLightColorG; }
|
||||
|
||||
/**
|
||||
* Get the ambient light color blue component.
|
||||
*/
|
||||
unsigned int GetAmbientLightColorBlue() const { return ambientLightColorB; }
|
||||
|
||||
/**
|
||||
* Set the ambient light color.
|
||||
*/
|
||||
void SetAmbientLightColor(unsigned int r, unsigned int g, unsigned int b) {
|
||||
ambientLightColorR = r;
|
||||
ambientLightColorG = g;
|
||||
ambientLightColorB = b;
|
||||
}
|
||||
|
||||
/** \name Effects
|
||||
*/
|
||||
///@{
|
||||
@@ -177,6 +221,11 @@ class GD_CORE_API Layer {
|
||||
private:
|
||||
gd::String name; ///< The name of the layer
|
||||
bool isVisible; ///< True if the layer is visible
|
||||
bool isLightingLayer; ///< True if the layer is used to display lights and renders an ambient light.
|
||||
bool followBaseLayerCamera; ///< True if the layer automatically follows the base layer
|
||||
unsigned int ambientLightColorR; ///< Ambient light color Red component
|
||||
unsigned int ambientLightColorG; ///< Ambient light color Green component
|
||||
unsigned int ambientLightColorB; ///< Ambient light color Blue component
|
||||
std::vector<gd::Camera> cameras; ///< The camera displayed by the layer
|
||||
std::vector<std::shared_ptr<gd::Effect>>
|
||||
effects; ///< The effects applied to the layer.
|
||||
|
@@ -23,4 +23,14 @@ void NamedPropertyDescriptor::UnserializeFrom(
|
||||
name = element.GetChild("name").GetStringValue();
|
||||
}
|
||||
|
||||
void NamedPropertyDescriptor::SerializeValuesTo(SerializerElement& element) const {
|
||||
PropertyDescriptor::SerializeValuesTo(element);
|
||||
element.AddChild("name").SetStringValue(name);
|
||||
}
|
||||
|
||||
void NamedPropertyDescriptor::UnserializeValuesFrom(const SerializerElement& element) {
|
||||
PropertyDescriptor::UnserializeValuesFrom(element);
|
||||
name = element.GetChild("name").GetStringValue();
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -58,6 +58,16 @@ class GD_CORE_API NamedPropertyDescriptor : public PropertyDescriptor {
|
||||
* \brief Unserialize the NamedPropertyDescriptor.
|
||||
*/
|
||||
void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
/**
|
||||
* \brief Serialize only the value and extra informations of the property.
|
||||
*/
|
||||
virtual void SerializeValuesTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize only the value and extra information of the property.
|
||||
*/
|
||||
virtual void UnserializeValuesFrom(const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
/**
|
||||
|
@@ -5,13 +5,16 @@
|
||||
*/
|
||||
|
||||
#include "Project.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <cctype>
|
||||
|
||||
#include <SFML/System/Utf.hpp>
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
@@ -51,7 +54,6 @@ Project::Project()
|
||||
version("1.0.0"),
|
||||
packageName("com.example.gamename"),
|
||||
orientation("landscape"),
|
||||
adMobAppId(""),
|
||||
folderProject(false),
|
||||
#endif
|
||||
windowWidth(800),
|
||||
@@ -596,7 +598,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
|
||||
SetPackageName(propElement.GetStringAttribute("packageName"));
|
||||
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
|
||||
SetAdMobAppId(propElement.GetStringAttribute("adMobAppId", ""));
|
||||
SetFolderProject(propElement.GetBoolAttribute("folderProject"));
|
||||
SetProjectFile(propElement.GetStringAttribute("projectFile"));
|
||||
SetLastCompilationDirectory(propElement
|
||||
@@ -618,6 +619,20 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
propElement.GetStringAttribute("macExecutableFilename");
|
||||
useExternalSourceFiles =
|
||||
propElement.GetBoolAttribute("useExternalSourceFiles");
|
||||
|
||||
extensionProperties.UnserializeFrom(
|
||||
propElement.GetChild("extensionProperties"));
|
||||
|
||||
// Compatibility with GD <= 5.0.0-beta98
|
||||
// Move AdMob App ID from project property to extension property.
|
||||
if (propElement.GetStringAttribute("adMobAppId", "") != "") {
|
||||
extensionProperties.SetValue(
|
||||
"AdMob",
|
||||
"AdMobAppId",
|
||||
propElement.GetStringAttribute("adMobAppId", ""));
|
||||
}
|
||||
// end of compatibility code
|
||||
|
||||
#endif
|
||||
|
||||
const SerializerElement& extensionsElement =
|
||||
@@ -866,13 +881,13 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
propElement.AddChild("verticalSync")
|
||||
.SetValue(IsVerticalSynchronizationEnabledByDefault());
|
||||
propElement.SetAttribute("scaleMode", scaleMode);
|
||||
propElement.SetAttribute("adaptGameResolutionAtRuntime", adaptGameResolutionAtRuntime);
|
||||
propElement.SetAttribute("adaptGameResolutionAtRuntime",
|
||||
adaptGameResolutionAtRuntime);
|
||||
propElement.SetAttribute("sizeOnStartupMode", sizeOnStartupMode);
|
||||
propElement.SetAttribute("projectFile", gameFile);
|
||||
propElement.SetAttribute("folderProject", folderProject);
|
||||
propElement.SetAttribute("packageName", packageName);
|
||||
propElement.SetAttribute("orientation", orientation);
|
||||
propElement.SetAttribute("adMobAppId", adMobAppId);
|
||||
platformSpecificAssets.SerializeTo(
|
||||
propElement.AddChild("platformSpecificAssets"));
|
||||
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
|
||||
@@ -882,6 +897,8 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
propElement.SetAttribute("macExecutableFilename", macExecutableFilename);
|
||||
propElement.SetAttribute("useExternalSourceFiles", useExternalSourceFiles);
|
||||
|
||||
extensionProperties.SerializeTo(propElement.AddChild("extensionProperties"));
|
||||
|
||||
SerializerElement& extensionsElement = propElement.AddChild("extensions");
|
||||
extensionsElement.ConsiderAsArrayOf("extension");
|
||||
for (std::size_t i = 0; i < GetUsedExtensions().size(); ++i)
|
||||
@@ -1071,13 +1088,14 @@ void Project::Init(const gd::Project& game) {
|
||||
author = game.author;
|
||||
packageName = game.packageName;
|
||||
orientation = game.orientation;
|
||||
adMobAppId = game.adMobAppId;
|
||||
folderProject = game.folderProject;
|
||||
latestCompilationDirectory = game.latestCompilationDirectory;
|
||||
platformSpecificAssets = game.platformSpecificAssets;
|
||||
loadingScreen = game.loadingScreen;
|
||||
objectGroups = game.objectGroups;
|
||||
|
||||
extensionProperties = game.extensionProperties;
|
||||
|
||||
gdMajorVersion = game.gdMajorVersion;
|
||||
gdMinorVersion = game.gdMinorVersion;
|
||||
gdBuildVersion = game.gdBuildVersion;
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Project/ExtensionProperties.h"
|
||||
#include "GDCore/Project/LoadingScreen.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
@@ -114,20 +115,6 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
*/
|
||||
const gd::String& GetOrientation() const { return orientation; }
|
||||
|
||||
/**
|
||||
* \brief Change the project AdMob application ID (needed
|
||||
* to use the AdMob extension). This has no effect on desktop
|
||||
* and web browsers.
|
||||
*/
|
||||
void SetAdMobAppId(const gd::String& adMobAppId_) {
|
||||
adMobAppId = adMobAppId_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the project AdMob application ID.
|
||||
*/
|
||||
const gd::String& GetAdMobAppId() const { return adMobAppId; }
|
||||
|
||||
/**
|
||||
* Called when project file has changed.
|
||||
*/
|
||||
@@ -305,6 +292,26 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
std::vector<gd::String>& GetUsedExtensions() { return extensionsUsed; };
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
* \brief Get the properties set by extensions.
|
||||
*
|
||||
* Each extension can store arbitrary values indexed by a property name, which are
|
||||
* useful to store project wide settings (AdMob id, etc...).
|
||||
*/
|
||||
gd::ExtensionProperties& GetExtensionProperties() {
|
||||
return extensionProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the properties set by extensions.
|
||||
*
|
||||
* Each extension can store arbitrary values indexed by a property name, which are
|
||||
* useful to store project wide settings (AdMob id, etc...).
|
||||
*/
|
||||
const gd::ExtensionProperties& GetExtensionProperties() const {
|
||||
return extensionProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the list of platforms used by the project.
|
||||
*/
|
||||
@@ -968,7 +975,6 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
gd::String packageName; ///< Game package name
|
||||
gd::String orientation; ///< Lock game orientation (on mobile devices).
|
||||
///< "default", "landscape" or "portrait".
|
||||
gd::String adMobAppId; ///< AdMob application ID.
|
||||
bool
|
||||
folderProject; ///< True if folder project, false if single file project.
|
||||
gd::String gameFile; ///< File of the game
|
||||
@@ -978,7 +984,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
gd::PlatformSpecificAssets platformSpecificAssets;
|
||||
gd::LoadingScreen loadingScreen;
|
||||
std::vector<std::unique_ptr<gd::ExternalEvents> >
|
||||
externalEvents; ///< List of all externals events
|
||||
externalEvents; ///< List of all externals events
|
||||
ExtensionProperties
|
||||
extensionProperties; ///< The properties of the extensions.
|
||||
mutable unsigned int gdMajorVersion; ///< The GD major version used the last
|
||||
///< time the project was saved.
|
||||
mutable unsigned int gdMinorVersion; ///< The GD minor version used the last
|
||||
|
@@ -4,7 +4,9 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "PropertyDescriptor.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
@@ -45,4 +47,27 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
|
||||
: false;
|
||||
}
|
||||
|
||||
void PropertyDescriptor::SerializeValuesTo(SerializerElement& element) const {
|
||||
element.AddChild("value").SetStringValue(currentValue);
|
||||
SerializerElement& extraInformationElement =
|
||||
element.AddChild("extraInformation");
|
||||
extraInformationElement.ConsiderAsArray();
|
||||
for (const gd::String& information : extraInformation) {
|
||||
extraInformationElement.AddChild("").SetStringValue(information);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyDescriptor::UnserializeValuesFrom(
|
||||
const SerializerElement& element) {
|
||||
currentValue = element.GetChild("value").GetStringValue();
|
||||
|
||||
extraInformation.clear();
|
||||
const SerializerElement& extraInformationElement =
|
||||
element.GetChild("extraInformation");
|
||||
extraInformationElement.ConsiderAsArray();
|
||||
for (std::size_t i = 0; i < extraInformationElement.GetChildrenCount(); ++i)
|
||||
extraInformation.push_back(
|
||||
extraInformationElement.GetChild(i).GetStringValue());
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#ifndef GDCORE_PROPERTYDESCRIPTOR
|
||||
#define GDCORE_PROPERTYDESCRIPTOR
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
@@ -120,6 +121,16 @@ class GD_CORE_API PropertyDescriptor {
|
||||
* \brief Unserialize the PropertyDescriptor.
|
||||
*/
|
||||
virtual void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
/**
|
||||
* \brief Serialize only the value and extra informations.
|
||||
*/
|
||||
virtual void SerializeValuesTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize only the value and extra informations.
|
||||
*/
|
||||
virtual void UnserializeValuesFrom(const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
private:
|
||||
@@ -127,7 +138,7 @@ class GD_CORE_API PropertyDescriptor {
|
||||
gd::String
|
||||
type; ///< The type of the property. This is arbitrary and interpreted by
|
||||
///< the class responsible for updating the property grid.
|
||||
gd::String label; //< The user-friendly property name
|
||||
gd::String label; //< The user-friendly property name
|
||||
gd::String description; //< The user-friendly property description
|
||||
std::vector<gd::String>
|
||||
extraInformation; ///< Can be used to store for example the available
|
||||
|
@@ -90,7 +90,7 @@ template <typename T>
|
||||
void SPtrList<T>::Init(const gd::SPtrList<T>& other) {
|
||||
elements.clear();
|
||||
for (size_t i = 0; i < other.elements.size(); ++i)
|
||||
elements.push_back(std::make_shared<T>(other[i]));
|
||||
elements.push_back(CloneRememberingOriginalElement(other.elements[i]));
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
24
Core/GDCore/Tools/UUID/UUID.h
Normal file
24
Core/GDCore/Tools/UUID/UUID.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef GDCORE_TOOLS_UUID_UUID_H
|
||||
#define GDCORE_TOOLS_UUID_UUID_H
|
||||
|
||||
#include "GDCore/String.h"
|
||||
#include "sole.h"
|
||||
|
||||
namespace gd {
|
||||
namespace UUID {
|
||||
|
||||
/**
|
||||
* Generate a random UUID v4
|
||||
*/
|
||||
inline gd::String MakeUuid4() { return gd::String::From(sole::uuid4()); }
|
||||
|
||||
} // namespace UUID
|
||||
} // namespace gd
|
||||
|
||||
#endif
|
249
Core/GDCore/Tools/UUID/sole.h
Normal file
249
Core/GDCore/Tools/UUID/sole.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* Modified version of sole (https://github.com/r-lyeh-archived/sole) C++11 library
|
||||
* to only generate UUID v4.
|
||||
*
|
||||
* Sole is a lightweight C++11 library to generate universally unique identificators.
|
||||
* Sole provides interface for UUID versions 0, 1 and 4.
|
||||
*
|
||||
* https://github.com/r-lyeh/sole
|
||||
* Copyright (c) 2013,2014,2015 r-lyeh. zlib/libpng licensed.
|
||||
*
|
||||
* Based on code by Dmitri Bouianov, Philip O'Toole, Poco C++ libraries and anonymous
|
||||
* code found on the net. Thanks guys!
|
||||
*
|
||||
* Theory: (see Hoylen's answer at [1])
|
||||
* - UUID version 1 (48-bit MAC address + 60-bit clock with a resolution of 100ns)
|
||||
* Clock wraps in 3603 A.D.
|
||||
* Up to 10000000 UUIDs per second.
|
||||
* MAC address revealed.
|
||||
*
|
||||
* - UUID Version 4 (122-bits of randomness)
|
||||
* See [2] or other analysis that describe how very unlikely a duplicate is.
|
||||
*
|
||||
* - Use v1 if you need to sort or classify UUIDs per machine.
|
||||
* Use v1 if you are worried about leaving it up to probabilities (e.g. your are the
|
||||
* type of person worried about the earth getting destroyed by a large asteroid in your
|
||||
* lifetime). Just use a v1 and it is guaranteed to be unique till 3603 AD.
|
||||
*
|
||||
* - Use v4 if you are worried about security issues and determinism. That is because
|
||||
* v1 UUIDs reveal the MAC address of the machine it was generated on and they can be
|
||||
* predictable. Use v4 if you need more than 10 million uuids per second, or if your
|
||||
* application wants to live past 3603 A.D.
|
||||
* Additionally a custom UUID v0 is provided:
|
||||
* - 16-bit PID + 48-bit MAC address + 60-bit clock with a resolution of 100ns since Unix epoch
|
||||
* - Format is EPOCH_LOW-EPOCH_MID-VERSION(0)|EPOCH_HI-PID-MAC
|
||||
* - Clock wraps in 3991 A.D.
|
||||
* - Up to 10000000 UUIDs per second.
|
||||
* - MAC address and PID revealed.
|
||||
* References:
|
||||
* - [1] http://stackoverflow.com/questions/1155008/how-unique-is-uuid
|
||||
* - [2] http://en.wikipedia.org/wiki/UUID#Random%5FUUID%5Fprobability%5Fof%5Fduplicates
|
||||
* - http://en.wikipedia.org/wiki/Universally_unique_identifier
|
||||
* - http://en.cppreference.com/w/cpp/numeric/random/random_device
|
||||
* - http://www.itu.int/ITU-T/asn1/uuid.html f81d4fae-7dec-11d0-a765-00a0c91e6bf6
|
||||
* - rlyeh ~~ listening to Hedon Cries / Until The Sun Goes up
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdio.h> // for size_t; should be stddef.h instead; however, clang+archlinux fails when compiling it (@Travis-Ci)
|
||||
#include <sys/types.h> // for uint32_t; should be stdint.h instead; however, GCC 5 on OSX fails when compiling it (See issue #11)
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
// public API
|
||||
|
||||
namespace sole
|
||||
{
|
||||
// 128-bit basic UUID type that allows comparison and sorting.
|
||||
// Use .str() for printing and .pretty() for pretty printing.
|
||||
// Also, ostream friendly.
|
||||
struct uuid
|
||||
{
|
||||
uint64_t ab;
|
||||
uint64_t cd;
|
||||
|
||||
bool operator==( const uuid &other ) const;
|
||||
bool operator!=( const uuid &other ) const;
|
||||
bool operator <( const uuid &other ) const;
|
||||
|
||||
std::string base62() const;
|
||||
std::string str() const;
|
||||
|
||||
template<typename ostream>
|
||||
inline friend ostream &operator<<( ostream &os, const uuid &self ) {
|
||||
return os << self.str(), os;
|
||||
}
|
||||
};
|
||||
|
||||
// Generators
|
||||
uuid uuid4(); // UUID v4, pros: anonymous, fast; con: uuids "can clash"
|
||||
|
||||
// Rebuilders
|
||||
uuid rebuild( uint64_t ab, uint64_t cd );
|
||||
uuid rebuild( const std::string &uustr );
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4127)
|
||||
#endif
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash< sole::uuid > {
|
||||
public:
|
||||
// hash functor: hash uuid to size_t value by pseudorandomizing transform
|
||||
size_t operator()( const sole::uuid &uuid ) const {
|
||||
if( sizeof(size_t) > 4 ) {
|
||||
return size_t( uuid.ab ^ uuid.cd );
|
||||
} else {
|
||||
uint64_t hash64 = uuid.ab ^ uuid.cd;
|
||||
return size_t( uint32_t( hash64 >> 32 ) ^ uint32_t( hash64 ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// implementation
|
||||
|
||||
#include <memory.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
inline bool sole::uuid::operator==( const sole::uuid &other ) const {
|
||||
return ab == other.ab && cd == other.cd;
|
||||
}
|
||||
inline bool sole::uuid::operator!=( const sole::uuid &other ) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
inline bool sole::uuid::operator<( const sole::uuid &other ) const {
|
||||
if( ab < other.ab ) return true;
|
||||
if( ab > other.ab ) return false;
|
||||
if( cd < other.cd ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace sole {
|
||||
|
||||
inline std::string uuid::str() const {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::nouppercase << std::setfill('0');
|
||||
|
||||
uint32_t a = (ab >> 32);
|
||||
uint32_t b = (ab & 0xFFFFFFFF);
|
||||
uint32_t c = (cd >> 32);
|
||||
uint32_t d = (cd & 0xFFFFFFFF);
|
||||
|
||||
ss << std::setw(8) << (a) << '-';
|
||||
ss << std::setw(4) << (b >> 16) << '-';
|
||||
ss << std::setw(4) << (b & 0xFFFF) << '-';
|
||||
ss << std::setw(4) << (c >> 16 ) << '-';
|
||||
ss << std::setw(4) << (c & 0xFFFF);
|
||||
ss << std::setw(8) << d;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
inline std::string uuid::base62() const {
|
||||
int base62len = 10 + 26 + 26;
|
||||
const char base62[] =
|
||||
"0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
char res[24], *end = &res[24]; *(--end) = '\0';
|
||||
uint64_t rem, AB = ab, CD = cd;
|
||||
do {
|
||||
rem = CD % base62len;
|
||||
*--end = base62[int(rem)];
|
||||
CD /= base62len;
|
||||
} while (CD > 0);
|
||||
*--end = '-';
|
||||
do {
|
||||
rem = AB % base62len;
|
||||
*--end = base62[int(rem)];
|
||||
AB /= base62len;
|
||||
} while (AB > 0);
|
||||
return end;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// UUID implementations
|
||||
|
||||
inline uuid uuid4() {
|
||||
static std::random_device rd;
|
||||
static std::uniform_int_distribution<uint64_t> dist(0, (uint64_t)(~0));
|
||||
|
||||
uuid my;
|
||||
|
||||
my.ab = dist(rd);
|
||||
my.cd = dist(rd);
|
||||
|
||||
my.ab = (my.ab & 0xFFFFFFFFFFFF0FFFULL) | 0x0000000000004000ULL;
|
||||
my.cd = (my.cd & 0x3FFFFFFFFFFFFFFFULL) | 0x8000000000000000ULL;
|
||||
|
||||
return my;
|
||||
}
|
||||
|
||||
inline uuid rebuild( uint64_t ab, uint64_t cd ) {
|
||||
uuid u;
|
||||
u.ab = ab; u.cd = cd;
|
||||
return u;
|
||||
}
|
||||
|
||||
inline uuid rebuild( const std::string &uustr ) {
|
||||
char sep;
|
||||
uint64_t a,b,c,d,e;
|
||||
uuid u = { 0, 0 };
|
||||
auto idx = uustr.find_first_of("-");
|
||||
if( idx != std::string::npos ) {
|
||||
// single separator, base62 notation
|
||||
if( uustr.find_first_of("-",idx+1) == std::string::npos ) {
|
||||
auto rebase62 = [&]( const char *input, size_t limit ) -> uint64_t {
|
||||
int base62len = 10 + 26 + 26;
|
||||
auto strpos = []( char ch ) -> size_t {
|
||||
if( ch >= 'a' ) return ch - 'a' + 10 + 26;
|
||||
if( ch >= 'A' ) return ch - 'A' + 10;
|
||||
return ch - '0';
|
||||
};
|
||||
uint64_t res = strpos( input[0] );
|
||||
for( size_t i = 1; i < limit; ++i )
|
||||
res = base62len * res + strpos( input[i] );
|
||||
return res;
|
||||
};
|
||||
u.ab = rebase62( &uustr[0], idx );
|
||||
u.cd = rebase62( &uustr[idx+1], uustr.size() - (idx+1) );
|
||||
}
|
||||
// else classic hex notation
|
||||
else {
|
||||
std::stringstream ss( uustr );
|
||||
if( ss >> std::hex >> a >> sep >> b >> sep >> c >> sep >> d >> sep >> e ) {
|
||||
if( ss.eof() ) {
|
||||
u.ab = (a << 32) | (b << 16) | c;
|
||||
u.cd = (d << 48) | e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
} // ::sole
|
@@ -20,7 +20,10 @@ import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsEx
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function(_/*: (string) => string */, gd/*: libGDevelop */) {
|
||||
createExtension: function (
|
||||
_ /*: (string) => string */,
|
||||
gd /*: libGDevelop */
|
||||
) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
'AdMob',
|
||||
@@ -32,6 +35,24 @@ module.exports = {
|
||||
'MIT'
|
||||
);
|
||||
|
||||
extension
|
||||
.registerProperty('AdMobAppId')
|
||||
.setLabel(_('AdMob App ID'))
|
||||
.setDescription('ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY')
|
||||
.setType('string');
|
||||
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('AdMob Cordova Extension')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('cordova-plugin-admob-free')
|
||||
.setVersion('~0.21.0')
|
||||
.setExtraSetting(
|
||||
'ADMOB_APP_ID',
|
||||
new gd.PropertyDescriptor('AdMobAppId').setType('ExtensionProperty')
|
||||
)
|
||||
.onlyIfExtraSettingIsNonEmpty('ADMOB_APP_ID');
|
||||
|
||||
// Banner
|
||||
extension
|
||||
.addCondition(
|
||||
@@ -364,7 +385,10 @@ module.exports = {
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function(gd /*: libGDevelop */, extension /*: gdPlatformExtension*/) {
|
||||
runExtensionSanityTests: function (
|
||||
gd /*: libGDevelop */,
|
||||
extension /*: gdPlatformExtension*/
|
||||
) {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
@@ -40,6 +40,26 @@ gdjs.AnchorRuntimeBehavior.VerticalAnchor = {
|
||||
PROPORTIONAL: 3
|
||||
};
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.leftEdgeAnchor !== newBehaviorData.leftEdgeAnchor) {
|
||||
this._leftEdgeAnchor = newBehaviorData.leftEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.rightEdgeAnchor !== newBehaviorData.rightEdgeAnchor) {
|
||||
this._rightEdgeAnchor = newBehaviorData.rightEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.topEdgeAnchor !== newBehaviorData.topEdgeAnchor) {
|
||||
this._topEdgeAnchor = newBehaviorData.topEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.bottomEdgeAnchor !== newBehaviorData.bottomEdgeAnchor) {
|
||||
this._bottomEdgeAnchor = newBehaviorData.bottomEdgeAnchor;
|
||||
}
|
||||
if (oldBehaviorData.relativeToOriginalWindowSize !== newBehaviorData.relativeToOriginalWindowSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.AnchorRuntimeBehavior.prototype.onActivate = function() {
|
||||
this._invalidDistances = true;
|
||||
};
|
||||
|
@@ -18,7 +18,11 @@ gdjs.BBTextRuntimeObjectPixiRenderer = function (runtimeObject, runtimeScene) {
|
||||
.getFontManager()
|
||||
.getFontFamily(runtimeObject._fontFamily),
|
||||
fontSize: runtimeObject._fontSize + 'px',
|
||||
fill: runtimeObject._color,
|
||||
fill: gdjs.rgbToHexNumber(
|
||||
runtimeObject._color[0],
|
||||
runtimeObject._color[1],
|
||||
runtimeObject._color[2]
|
||||
),
|
||||
tagStyle: 'bbcode',
|
||||
wordWrap: runtimeObject._wordWrap,
|
||||
wordWrapWidth: runtimeObject._wrappingWidth,
|
||||
@@ -72,7 +76,11 @@ gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateText = function () {
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateColor = function () {
|
||||
this._pixiObject.textStyles.default.fill = this._object._color;
|
||||
this._pixiObject.textStyles.default.fill = gdjs.rgbToHexNumber(
|
||||
this._object._color[0],
|
||||
this._object._color[1],
|
||||
this._object._color[2]
|
||||
);
|
||||
this._pixiObject.dirty = true;
|
||||
};
|
||||
|
||||
|
@@ -29,8 +29,8 @@ gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
|
||||
// parseFloat should not be required, but GDevelop 5.0 beta 92 and below were storing it as a string.
|
||||
/** @type {string} */
|
||||
this._text = objectData.content.text;
|
||||
/** @type {string} */
|
||||
this._color = objectData.content.color;
|
||||
/** @type {number[]} color in format [r, g, b], where each component is in the range [0, 255] */
|
||||
this._color = gdjs.BBTextRuntimeObject.hexToRGBColor(objectData.content.color);
|
||||
/** @type {string} */
|
||||
this._fontFamily = objectData.content.fontFamily;
|
||||
/** @type {number} */
|
||||
@@ -61,18 +61,58 @@ gdjs.BBTextRuntimeObject.prototype = Object.create(
|
||||
);
|
||||
gdjs.registerObject('BBText::BBText', gdjs.BBTextRuntimeObject);
|
||||
|
||||
gdjs.BBTextRuntimeObject.hexToRGBColor = function (hex) {
|
||||
var hexNumber = parseInt(hex.replace('#', ''), 16);
|
||||
return [(hexNumber >> 16) & 0xff, (hexNumber >> 8) & 0xff, hexNumber & 0xff];
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {BBTextObjectDataType} oldObjectData
|
||||
* @param {BBTextObjectDataType} newObjectData
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
if (oldObjectData.content.opacity !== newObjectData.content.opacity) {
|
||||
this.setOpacity(newObjectData.content.opacity);
|
||||
}
|
||||
if (oldObjectData.content.visible !== newObjectData.content.visible) {
|
||||
this.hide(!newObjectData.content.visible);
|
||||
}
|
||||
if (oldObjectData.content.text !== newObjectData.content.text) {
|
||||
this.setBBText(newObjectData.content.text);
|
||||
}
|
||||
if (oldObjectData.content.color !== newObjectData.content.color) {
|
||||
this._color = gdjs.BBTextRuntimeObject.hexToRGBColor(newObjectData.content.color);
|
||||
this._renderer.updateColor();
|
||||
}
|
||||
if (oldObjectData.content.fontFamily !== newObjectData.content.fontFamily) {
|
||||
this.setFontFamily(newObjectData.content.fontFamily);
|
||||
}
|
||||
if (oldObjectData.content.fontSize !== newObjectData.content.fontSize) {
|
||||
this.setFontSize(newObjectData.content.fontSize);
|
||||
}
|
||||
if (oldObjectData.content.wordWrap !== newObjectData.content.wordWrap) {
|
||||
this.setWordWrap(newObjectData.content.wordWrap);
|
||||
}
|
||||
if (oldObjectData.content.align !== newObjectData.content.align) {
|
||||
this.setAlignment(newObjectData.content.align);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @private
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.extraInitializationFromInitialInstance = function(initialInstanceData) {
|
||||
// The wrapping width value (this._wrappingWidth) is using the object's width as an innitial value
|
||||
if (initialInstanceData.customSize)
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
else
|
||||
this.setWrappingWidth(250); // This value is the default wrapping width of the runtime object.
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.onDestroyFromScene = function(runtimeScene) {
|
||||
@@ -97,19 +137,19 @@ gdjs.BBTextRuntimeObject.prototype.getBBText = function() {
|
||||
gdjs.BBTextRuntimeObject.prototype.setColor = function(rgbColorString) {
|
||||
const splitValue = rgbColorString.split(';');
|
||||
if (splitValue.length !== 3) return;
|
||||
const hexColor =
|
||||
'#' +
|
||||
gdjs.rgbToHex(
|
||||
parseInt(splitValue[0], 0),
|
||||
parseInt(splitValue[1], 0),
|
||||
parseInt(splitValue[2], 0)
|
||||
);
|
||||
this._color = hexColor;
|
||||
|
||||
this._color[0] = parseInt(splitValue[0], 10);
|
||||
this._color[1] = parseInt(splitValue[1], 10);
|
||||
this._color[2] = parseInt(splitValue[2], 10);
|
||||
this._renderer.updateColor();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the base color.
|
||||
* @return {string} The color as a "R;G;B" string, for example: "255;0;0"
|
||||
*/
|
||||
gdjs.BBTextRuntimeObject.prototype.getColor = function() {
|
||||
return this._color;
|
||||
return this._color[0] + ";" + this._color[1] + ";" + this._color[2];
|
||||
};
|
||||
|
||||
gdjs.BBTextRuntimeObject.prototype.setFontSize = function(fontSize) {
|
||||
|
@@ -21,7 +21,7 @@ class GD_EXTENSION_API DestroyOutsideBehavior : public gd::Behavior {
|
||||
public:
|
||||
DestroyOutsideBehavior(){};
|
||||
virtual ~DestroyOutsideBehavior(){};
|
||||
virtual Behavior* Clone() const { return new DestroyOutsideBehavior(*this); }
|
||||
virtual Behavior* Clone() const override { return new DestroyOutsideBehavior(*this); }
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
|
@@ -20,6 +20,14 @@ gdjs.DestroyOutsideRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("DestroyOutsideBehavior::DestroyOutside", gdjs.DestroyOutsideRuntimeBehavior);
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.extraBorder !== newBehaviorData.extraBorder) {
|
||||
this._extraBorder = newBehaviorData.extraBorder;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
|
@@ -32,6 +32,13 @@ module.exports = {
|
||||
"Open source (MIT License)"
|
||||
).setExtensionHelpPath("/all-features/device-vibration");
|
||||
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('Vibration Cordova Extension')
|
||||
.setDependencyType('cordova')
|
||||
.setExportName('cordova-plugin-vibration')
|
||||
.setVersion('3.1.1');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
"StartVibration",
|
||||
|
@@ -25,6 +25,11 @@ gdjs.DraggableRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
gdjs.DraggableRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("DraggableBehavior::Draggable", gdjs.DraggableRuntimeBehavior);
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
// Nothing to update.
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.DraggableRuntimeBehavior.prototype.onDeActivate = function() {
|
||||
this._endDrag();
|
||||
};
|
||||
|
@@ -1,124 +1,160 @@
|
||||
// @ts-check
|
||||
describe('gdjs.DraggableRuntimeBehavior', function() {
|
||||
var runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
resources: {resources: []},
|
||||
// @ts-ignore
|
||||
properties: {windowWidth: 800, windowHeight: 600}
|
||||
});
|
||||
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers:[{name:"", visibility: true}],
|
||||
variables: [],
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: []
|
||||
});
|
||||
describe('gdjs.DraggableRuntimeBehavior', function () {
|
||||
var runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
resources: { resources: [] },
|
||||
// @ts-ignore
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
});
|
||||
var 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: [],
|
||||
});
|
||||
|
||||
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{name: "Behavior1", type: "DraggableBehavior::Draggable"}], variables: []});
|
||||
var object2 = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{name: "Behavior1", type: "DraggableBehavior::Draggable"}], variables: []});
|
||||
runtimeScene.addObject(object);
|
||||
runtimeScene.addObject(object2);
|
||||
var object = new gdjs.RuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
type: '',
|
||||
behaviors: [{ name: 'Behavior1', type: 'DraggableBehavior::Draggable' }],
|
||||
variables: [],
|
||||
});
|
||||
var object2 = new gdjs.RuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
type: '',
|
||||
behaviors: [{ name: 'Behavior1', type: 'DraggableBehavior::Draggable' }],
|
||||
variables: [],
|
||||
});
|
||||
runtimeScene.addObject(object);
|
||||
runtimeScene.addObject(object2);
|
||||
|
||||
it('should handle mouse', function() {
|
||||
object.setPosition(450, 500);
|
||||
it('should handle mouse', function () {
|
||||
object.setPosition(450, 500);
|
||||
|
||||
//Drag'n'drop
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onMouseMove(450, 500);
|
||||
runtimeGame.getInputManager().onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onMouseMove(750, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
runtimeScene.renderAndStep();
|
||||
//Drag'n'drop
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onMouseMove(450, 500);
|
||||
runtimeGame
|
||||
.getInputManager()
|
||||
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onMouseMove(750, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame
|
||||
.getInputManager()
|
||||
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
runtimeScene.renderAndStep();
|
||||
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(600);
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(600);
|
||||
|
||||
//Mouse move with dragging
|
||||
runtimeGame.getInputManager().onMouseMove(600, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
//Mouse move with dragging
|
||||
runtimeGame.getInputManager().onMouseMove(600, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(600);
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(600);
|
||||
|
||||
//Start dragging again
|
||||
runtimeGame.getInputManager().onMouseMove(750, 600);
|
||||
runtimeGame.getInputManager().onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onMouseMove(850, 700);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
runtimeScene.renderAndStep();
|
||||
//Start dragging again
|
||||
runtimeGame.getInputManager().onMouseMove(750, 600);
|
||||
runtimeGame
|
||||
.getInputManager()
|
||||
.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onMouseMove(850, 700);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame
|
||||
.getInputManager()
|
||||
.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
runtimeScene.renderAndStep();
|
||||
|
||||
expect(object.getX()).to.be(850);
|
||||
expect(object.getY()).to.be(700);
|
||||
expect(object.getX()).to.be(850);
|
||||
expect(object.getY()).to.be(700);
|
||||
});
|
||||
it('should handle touches', function () {
|
||||
runtimeGame.getInputManager().touchSimulateMouse(false);
|
||||
object.setPosition(450, 500);
|
||||
|
||||
});
|
||||
it('should handle touches', function() {
|
||||
runtimeGame.getInputManager().touchSimulateMouse(false);
|
||||
object.setPosition(450, 500);
|
||||
//Drag'n'drop
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
|
||||
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeGame.getInputManager().onTouchMove(0, 750, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeGame.getInputManager().onTouchEnd(0);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
|
||||
//Drag'n'drop
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
|
||||
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeGame.getInputManager().onTouchMove(0, 750, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeGame.getInputManager().onTouchEnd(0);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(600);
|
||||
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(600);
|
||||
//Move another unrelated touch
|
||||
runtimeGame.getInputManager().onTouchMove(1, 750, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
|
||||
runtimeScene.renderAndStep();
|
||||
|
||||
//Move another unrelated touch
|
||||
runtimeGame.getInputManager().onTouchMove(1, 750, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
|
||||
runtimeScene.renderAndStep();
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(600);
|
||||
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(600);
|
||||
//Start drag'n'drop with another touch
|
||||
runtimeGame.getInputManager().onTouchEnd(1);
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchStart(1, 750, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchEnd(1);
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
|
||||
//Start drag'n'drop with another touch
|
||||
runtimeGame.getInputManager().onTouchEnd(1);
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchStart(1, 750, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchEnd(1);
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
expect(object.getX()).to.be(850);
|
||||
expect(object.getY()).to.be(700);
|
||||
});
|
||||
it('should handle multitouch', function () {
|
||||
runtimeGame.getInputManager().touchSimulateMouse(false);
|
||||
object.setPosition(450, 500);
|
||||
object2.setPosition(650, 600);
|
||||
|
||||
expect(object.getX()).to.be(850);
|
||||
expect(object.getY()).to.be(700);
|
||||
});
|
||||
it('should handle multitouch', function() {
|
||||
runtimeGame.getInputManager().touchSimulateMouse(false);
|
||||
object.setPosition(450, 500);
|
||||
object2.setPosition(650, 600);
|
||||
//Drag'n'drop
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchStart(2, 450, 500);
|
||||
runtimeGame.getInputManager().onTouchStart(1, 650, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeGame.getInputManager().onTouchMove(2, 750, 700);
|
||||
runtimeGame.getInputManager().onTouchMove(1, 100, 200);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeGame.getInputManager().onTouchEnd(2);
|
||||
|
||||
//Drag'n'drop
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onTouchStart(2, 450, 500);
|
||||
runtimeGame.getInputManager().onTouchStart(1, 650, 600);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeGame.getInputManager().onTouchMove(2, 750, 700);
|
||||
runtimeGame.getInputManager().onTouchMove(1, 100, 200);
|
||||
runtimeScene.renderAndStep();
|
||||
runtimeGame.getInputManager().onFrameEnded();
|
||||
runtimeGame.getInputManager().onTouchEnd(2);
|
||||
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(700);
|
||||
expect(object2.getX()).to.be(100);
|
||||
expect(object2.getY()).to.be(200);
|
||||
});
|
||||
expect(object.getX()).to.be(750);
|
||||
expect(object.getY()).to.be(700);
|
||||
expect(object2.getX()).to.be(100);
|
||||
expect(object2.getY()).to.be(200);
|
||||
});
|
||||
});
|
||||
|
@@ -33,6 +33,32 @@ module.exports = {
|
||||
'MIT'
|
||||
);
|
||||
|
||||
// Register Properties
|
||||
extension
|
||||
.registerProperty('DummyPropertyString')
|
||||
.setLabel(_('Dummy Property Name'))
|
||||
.setDescription('Type in anything :)')
|
||||
.setType('string');
|
||||
|
||||
extension
|
||||
.registerProperty('DummyPropertyNumber')
|
||||
.setLabel(_('Dummy Numeric Property Name'))
|
||||
.setDescription('Only numbers here ;)')
|
||||
.setType('number');
|
||||
|
||||
extension
|
||||
.registerProperty('DummyPropertyBoolean')
|
||||
.setDescription(_('A boolean property'))
|
||||
.setType('boolean');
|
||||
|
||||
// Register Cordova/NPM dependencies
|
||||
extension
|
||||
.addDependency()
|
||||
.setName('Thirteen Checker')
|
||||
.setDependencyType('npm')
|
||||
.setExportName('is-thirteen')
|
||||
.setVersion('2.0.0');
|
||||
|
||||
// Declare effects:
|
||||
const dummyEffect = extension
|
||||
.addEffect('DummyEffect')
|
||||
|
@@ -22,6 +22,12 @@ gdjs.DummyRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
gdjs.DummyRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("MyDummyExtension::DummyBehavior", gdjs.DummyRuntimeBehavior);
|
||||
|
||||
gdjs.DummyRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.property1 !== newBehaviorData.property1) {
|
||||
this._textToSet = newBehaviorData.property1;
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.DummyRuntimeBehavior.prototype.onDeActivate = function() {
|
||||
};
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The PIXI.js renderer for the DummyRuntimeObject.
|
||||
*
|
||||
*
|
||||
* @class DummyRuntimeObjectPixiRenderer
|
||||
* @constructor
|
||||
* @param {gdjs.DummyRuntimeObject} runtimeObject The object to render
|
||||
@@ -9,7 +9,7 @@
|
||||
gdjs.DummyRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene)
|
||||
{
|
||||
this._object = runtimeObject; // Keep a reference to the object to read from it later.
|
||||
|
||||
|
||||
// Here we're going to create a dummy text as an example.
|
||||
if ( this._text === undefined ) {
|
||||
this._text = new PIXI.Text(runtimeObject.getText(), {align:"left"});
|
||||
@@ -38,6 +38,10 @@ gdjs.DummyRuntimeObjectPixiRenderer.prototype.ensureUpToDate = function() {
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.DummyRuntimeObjectPixiRenderer.prototype.updateText = function() {
|
||||
this._text.text = this._object.getText();
|
||||
};
|
||||
|
||||
gdjs.DummyRuntimeObjectPixiRenderer.prototype.updatePosition = function() {
|
||||
this._text.position.x = this._object.x+this._text.width/2;
|
||||
this._text.position.y = this._object.y+this._text.height/2;
|
||||
|
@@ -28,6 +28,17 @@ gdjs.DummyRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
gdjs.DummyRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
// Compare previous and new data for the object and update it accordingly.
|
||||
// This is useful for "hot-reloading".
|
||||
if (oldObjectData.content.property1 !== newObjectData.content.property1) {
|
||||
this._property1 = newObjectData.content.property1;
|
||||
this._renderer.updateText();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called once during the game loop, before events and rendering.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The gdjs.RuntimeScene the object belongs to.
|
||||
@@ -109,7 +120,6 @@ gdjs.DummyRuntimeObject.prototype.getText = function() {
|
||||
return this._property1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A dummy method that can be called from events
|
||||
*/
|
||||
|
@@ -23,6 +23,14 @@ gdjs.DummyWithSharedDataRuntimeBehavior = function(runtimeScene, behaviorData, o
|
||||
gdjs.DummyWithSharedDataRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("MyDummyExtension::DummyBehaviorWithSharedData", gdjs.DummyRuntimeBehavior);
|
||||
|
||||
gdjs.DummyWithSharedDataRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.property1 !== newBehaviorData.property1) {
|
||||
this._textToSet = newBehaviorData.property1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.DummyWithSharedDataRuntimeBehavior.prototype.onDeActivate = function() {
|
||||
};
|
||||
|
||||
|
429
Extensions/Lighting/JsExtension.js
Normal file
429
Extensions/Lighting/JsExtension.js
Normal file
@@ -0,0 +1,429 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
* and search for any errors.
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/*::
|
||||
// Import types to allow Flow to do static type checking on this file.
|
||||
// Extensions declaration are typed using Flow (like the editor), but the files
|
||||
// for the game engine are checked with TypeScript annotations.
|
||||
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function (
|
||||
_ /*: (string) => string */,
|
||||
gd /*: libGDevelop */
|
||||
) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
'Lighting',
|
||||
_('Lights'),
|
||||
_(
|
||||
'Allow to display lights on the screen and mark objects as obstacles for the lights.'
|
||||
),
|
||||
'Harsimran Virk',
|
||||
'MIT'
|
||||
);
|
||||
|
||||
const lightObstacleBehavior = new gd.BehaviorJsImplementation();
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||||
lightObstacleBehavior.updateProperty = function (
|
||||
behaviorContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
return false;
|
||||
};
|
||||
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||||
lightObstacleBehavior.getProperties = function (behaviorContent) {
|
||||
const behaviorProperties = new gd.MapStringPropertyDescriptor();
|
||||
|
||||
return behaviorProperties;
|
||||
};
|
||||
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||||
lightObstacleBehavior.initializeContent = function (behaviorContent) {};
|
||||
extension
|
||||
.addBehavior(
|
||||
'LightObstacleBehavior',
|
||||
_('Light Obstacle Behavior'),
|
||||
'LightObstacleBehavior',
|
||||
_(
|
||||
'This behavior makes the object an obstacle to the light. The light emitted by light objects will be stopped by the object.'
|
||||
),
|
||||
'',
|
||||
'CppPlatform/Extensions/lightObstacleIcon32.png',
|
||||
'LightObstacleBehavior',
|
||||
lightObstacleBehavior,
|
||||
new gd.BehaviorsSharedData()
|
||||
)
|
||||
.setIncludeFile('Extensions/Lighting/lightobstacleruntimebehavior.js')
|
||||
.addIncludeFile('Extensions/Lighting/lightruntimeobject.js')
|
||||
.addIncludeFile(
|
||||
'Extensions/Lighting/lightruntimeobject-pixi-renderer.js'
|
||||
);
|
||||
|
||||
const lightObject = new gd.ObjectJsImplementation();
|
||||
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating an object.
|
||||
lightObject.updateProperty = function (
|
||||
objectContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
if (propertyName === 'radius') {
|
||||
objectContent.radius = parseFloat(newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'color') {
|
||||
objectContent.color = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'debugMode') {
|
||||
objectContent.debugMode = newValue === '1';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'texture') {
|
||||
objectContent.texture = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating an object.
|
||||
lightObject.getProperties = function (objectContent) {
|
||||
const objectProperties = new gd.MapStringPropertyDescriptor();
|
||||
|
||||
objectProperties.set(
|
||||
'radius',
|
||||
new gd.PropertyDescriptor(objectContent.radius.toString())
|
||||
.setType('number')
|
||||
.setLabel(_('Radius'))
|
||||
);
|
||||
|
||||
objectProperties.set(
|
||||
'color',
|
||||
new gd.PropertyDescriptor(objectContent.color)
|
||||
.setType('color')
|
||||
.setLabel(_('Color'))
|
||||
);
|
||||
|
||||
objectProperties.set(
|
||||
'debugMode',
|
||||
new gd.PropertyDescriptor(objectContent.debugMode ? 'true' : 'false')
|
||||
.setType('boolean')
|
||||
.setLabel(_('Debug mode'))
|
||||
.setDescription(
|
||||
_(
|
||||
'When activated, display the lines used to render the light - useful to understand how the light is rendered on screen.'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
objectProperties
|
||||
.getOrCreate('texture')
|
||||
.setValue(objectContent.texture)
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Light texture (optional)'))
|
||||
.setDescription(
|
||||
_(
|
||||
"A texture to be used to display the light. If you don't specify a texture, the light is rendered as fading from bright, in its center, to dark."
|
||||
)
|
||||
);
|
||||
|
||||
return objectProperties;
|
||||
};
|
||||
lightObject.setRawJSONContent(
|
||||
JSON.stringify({
|
||||
radius: 50,
|
||||
color: '#ffffff',
|
||||
debugMode: false,
|
||||
texture: '',
|
||||
})
|
||||
);
|
||||
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating an object.
|
||||
lightObject.updateInitialInstanceProperty = function (
|
||||
objectContent,
|
||||
instance,
|
||||
propertyName,
|
||||
newValue,
|
||||
project,
|
||||
layout
|
||||
) {
|
||||
return false;
|
||||
};
|
||||
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating an object.
|
||||
lightObject.getInitialInstanceProperties = function (
|
||||
content,
|
||||
instance,
|
||||
project,
|
||||
layout
|
||||
) {
|
||||
const instanceProperties = new gd.MapStringPropertyDescriptor();
|
||||
|
||||
return instanceProperties;
|
||||
};
|
||||
|
||||
const object = extension
|
||||
.addObject(
|
||||
'LightObject',
|
||||
_('Light'),
|
||||
_(
|
||||
'Displays a light on the scene, with a customizable radius and color. Add then the Light Obstacle behavior to the objects that must act as obstacle to the lights.'
|
||||
),
|
||||
'CppPlatform/Extensions/lightIcon32.png',
|
||||
lightObject
|
||||
)
|
||||
.setIncludeFile('Extensions/Lighting/lightruntimeobject.js')
|
||||
.addIncludeFile('Extensions/Lighting/lightruntimeobject-pixi-renderer.js')
|
||||
.addIncludeFile('Extensions/Lighting/lightobstacleruntimebehavior.js');
|
||||
|
||||
object
|
||||
.addAction(
|
||||
'SetRadius',
|
||||
_('Set the radius of light object'),
|
||||
_('Set the radius of light object'),
|
||||
_('Set the radius of _PARAM0_ to: _PARAM1_'),
|
||||
'',
|
||||
'CppPlatform/Extensions/lightIcon24.png',
|
||||
'CppPlatform/Extensions/lightIcon16.png'
|
||||
)
|
||||
.addParameter('object', _('Object'), 'LightObject', false)
|
||||
.addParameter('expression', _('Radius'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setRadius');
|
||||
|
||||
object
|
||||
.addAction(
|
||||
'SetColor',
|
||||
_('Set the color of light object'),
|
||||
_('Set the color of light object in format "R;G;B" string.'),
|
||||
_('Set the color of _PARAM0_ to: _PARAM1_'),
|
||||
'',
|
||||
'res/actions/color24.png',
|
||||
'res/actions/color.png'
|
||||
)
|
||||
.addParameter('object', _('Object'), 'LightObject', false)
|
||||
.addParameter('string', _('Color'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setColor');
|
||||
|
||||
return extension;
|
||||
},
|
||||
|
||||
runExtensionSanityTests: function (
|
||||
gd /*: libGDevelop */,
|
||||
extension /*: gdPlatformExtension*/
|
||||
) {
|
||||
return [];
|
||||
},
|
||||
|
||||
registerEditorConfigurations: function (
|
||||
objectsEditorService /*: ObjectsEditorService */
|
||||
) {
|
||||
objectsEditorService.registerEditorConfiguration(
|
||||
'Lighting::LightObject',
|
||||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||||
helpPagePath: '/objects/light',
|
||||
})
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Register renderers for instance of objects on the scene editor.
|
||||
*
|
||||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
|
||||
*/
|
||||
registerInstanceRenderers: function (
|
||||
objectsRenderingService /*: ObjectsRenderingService */
|
||||
) {
|
||||
const RenderedInstance = objectsRenderingService.RenderedInstance;
|
||||
const PIXI = objectsRenderingService.PIXI;
|
||||
|
||||
/**
|
||||
* Renderer for instances of LightObject inside the IDE.
|
||||
*
|
||||
* @extends RenderedInstance
|
||||
* @class RenderedLightObjectInstance
|
||||
* @constructor
|
||||
*/
|
||||
function RenderedLightObjectInstance(
|
||||
project,
|
||||
layout,
|
||||
instance,
|
||||
associatedObject,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
) {
|
||||
RenderedInstance.call(
|
||||
this,
|
||||
project,
|
||||
layout,
|
||||
instance,
|
||||
associatedObject,
|
||||
pixiContainer,
|
||||
pixiResourcesLoader
|
||||
);
|
||||
this._radius = parseFloat(
|
||||
this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('radius')
|
||||
.getValue()
|
||||
);
|
||||
if (this._radius <= 0) this._radius = 1;
|
||||
this._colorHex = parseInt(
|
||||
this._associatedObject
|
||||
.getProperties(this.project)
|
||||
.get('color')
|
||||
.getValue()
|
||||
.replace('#', ''),
|
||||
16
|
||||
);
|
||||
this._color = [
|
||||
((this._colorHex >> 16) & 0xff) / 255,
|
||||
((this._colorHex >> 8) & 0xff) / 255,
|
||||
(this._colorHex & 0xff) / 255,
|
||||
];
|
||||
|
||||
const geometry = new PIXI.Geometry();
|
||||
const shader = PIXI.Shader.from(
|
||||
`
|
||||
precision mediump float;
|
||||
attribute vec2 aVertexPosition;
|
||||
|
||||
uniform mat3 translationMatrix;
|
||||
uniform mat3 projectionMatrix;
|
||||
varying vec2 vPos;
|
||||
|
||||
void main() {
|
||||
vPos = aVertexPosition;
|
||||
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
||||
}`,
|
||||
`
|
||||
precision mediump float;
|
||||
uniform vec2 center;
|
||||
uniform float radius;
|
||||
uniform vec3 color;
|
||||
uniform mat3 translationMatrix;
|
||||
uniform mat3 projectionMatrix;
|
||||
|
||||
varying vec2 vPos;
|
||||
|
||||
void main() {
|
||||
float l = length(vPos - center);
|
||||
float intensity = 0.0;
|
||||
if(l < radius)
|
||||
intensity = clamp((radius - l)*(radius - l)/(radius*radius), 0.0, 1.0);
|
||||
gl_FragColor = vec4(color*intensity, 1.0);
|
||||
}
|
||||
`,
|
||||
{
|
||||
center: [this._instance.getX(), this._instance.getY()],
|
||||
radius: this._radius,
|
||||
color: this._color,
|
||||
}
|
||||
);
|
||||
|
||||
this._vertexBuffer = new Float32Array([
|
||||
this._instance.getX() - this._radius,
|
||||
this._instance.getY() + this._radius,
|
||||
this._instance.getX() + this._radius,
|
||||
this._instance.getY() + this._radius,
|
||||
this._instance.getX() + this._radius,
|
||||
this._instance.getY() - this._radius,
|
||||
this._instance.getX() - this._radius,
|
||||
this._instance.getY() - this._radius,
|
||||
]);
|
||||
|
||||
geometry
|
||||
.addAttribute('aVertexPosition', this._vertexBuffer, 2)
|
||||
.addIndex([0, 1, 2, 2, 3, 0]);
|
||||
|
||||
this._pixiObject = new PIXI.Mesh(geometry, shader);
|
||||
this._pixiObject.blendMode = PIXI.BLEND_MODES.ADD;
|
||||
this._pixiContainer.addChild(this._pixiObject);
|
||||
this.update();
|
||||
}
|
||||
RenderedLightObjectInstance.prototype = Object.create(
|
||||
RenderedInstance.prototype
|
||||
);
|
||||
|
||||
/**
|
||||
* Return the path to the thumbnail of the specified object.
|
||||
*/
|
||||
RenderedLightObjectInstance.getThumbnail = function (
|
||||
project,
|
||||
resourcesLoader,
|
||||
object
|
||||
) {
|
||||
return 'CppPlatform/Extensions/lightIcon32.png';
|
||||
};
|
||||
|
||||
/**
|
||||
* This is called to update the PIXI object on the scene editor
|
||||
*/
|
||||
RenderedLightObjectInstance.prototype.update = function () {
|
||||
this._pixiObject.shader.uniforms.center = new Float32Array([
|
||||
this._instance.getX(),
|
||||
this._instance.getY(),
|
||||
]);
|
||||
|
||||
this._vertexBuffer[0] = this._instance.getX() - this._radius;
|
||||
this._vertexBuffer[1] = this._instance.getY() + this._radius;
|
||||
this._vertexBuffer[2] = this._instance.getX() + this._radius;
|
||||
this._vertexBuffer[3] = this._instance.getY() + this._radius;
|
||||
this._vertexBuffer[4] = this._instance.getX() + this._radius;
|
||||
this._vertexBuffer[5] = this._instance.getY() - this._radius;
|
||||
this._vertexBuffer[6] = this._instance.getX() - this._radius;
|
||||
this._vertexBuffer[7] = this._instance.getY() - this._radius;
|
||||
|
||||
this._pixiObject.geometry
|
||||
.getBuffer('aVertexPosition')
|
||||
.update(this._vertexBuffer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the instance, when it's not resized.
|
||||
*/
|
||||
RenderedLightObjectInstance.prototype.getDefaultWidth = function () {
|
||||
return this._pixiObject.width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the height of the instance, when it's not resized.
|
||||
*/
|
||||
RenderedLightObjectInstance.prototype.getDefaultHeight = function () {
|
||||
return this._pixiObject.height;
|
||||
};
|
||||
|
||||
RenderedLightObjectInstance.prototype.getOriginX = function () {
|
||||
return this._radius;
|
||||
};
|
||||
|
||||
RenderedLightObjectInstance.prototype.getOriginY = function () {
|
||||
return this._radius;
|
||||
};
|
||||
|
||||
objectsRenderingService.registerInstanceRenderer(
|
||||
'Lighting::LightObject',
|
||||
RenderedLightObjectInstance
|
||||
);
|
||||
},
|
||||
};
|
153
Extensions/Lighting/lightobstacleruntimebehavior.js
Normal file
153
Extensions/Lighting/lightobstacleruntimebehavior.js
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* @memberof gdjs
|
||||
* @class LightObstaclesManager
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
*/
|
||||
gdjs.LightObstaclesManager = function (runtimeScene) {
|
||||
this._obstacleRBush = new rbush(9, [
|
||||
'.owner.getAABB().min[0]',
|
||||
'.owner.getAABB().min[1]',
|
||||
'.owner.getAABB().max[0]',
|
||||
'.owner.getAABB().max[1]',
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the light obstacles manager of a scene.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @returns {gdjs.LightObstaclesManager}
|
||||
*/
|
||||
gdjs.LightObstaclesManager.getManager = function (runtimeScene) {
|
||||
if (!runtimeScene._lightObstaclesManager) {
|
||||
// Create the shared manager if necessary.
|
||||
runtimeScene._lightObstaclesManager = new gdjs.LightObstaclesManager(
|
||||
runtimeScene
|
||||
);
|
||||
}
|
||||
|
||||
return runtimeScene._lightObstaclesManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a light obstacle to the list of existing obstacles.
|
||||
* @param {gdjs.LightObstacleRuntimeBehavior} obstacle
|
||||
*/
|
||||
gdjs.LightObstaclesManager.prototype.addObstacle = function (obstacle) {
|
||||
this._obstacleRBush.insert(obstacle);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a light obstacle from the list of existing obstacles. Be sure that the obstacle was
|
||||
* added before.
|
||||
* @param {gdjs.LightObstacleRuntimeBehavior} obstacle
|
||||
*/
|
||||
gdjs.LightObstaclesManager.prototype.removeObstacle = function (obstacle) {
|
||||
this._obstacleRBush.remove(obstacle);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns all the light obstacles around the specified object.
|
||||
* @param {gdjs.RuntimeObject} object The object
|
||||
* @param {number} radius Radius of the area to be searched.
|
||||
* @param {gdjs.RuntimeObject[]} result An array with all obstacles near the object.
|
||||
*/
|
||||
gdjs.LightObstaclesManager.prototype.getAllObstaclesAround = function (
|
||||
object,
|
||||
radius,
|
||||
result
|
||||
) {
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
// is not necessarily in the middle of the object (for sprites for example).
|
||||
var x = object.getX();
|
||||
var y = object.getY();
|
||||
|
||||
var searchArea = gdjs.staticObject(
|
||||
gdjs.LightObstaclesManager.prototype.getAllObstaclesAround
|
||||
);
|
||||
searchArea.minX = x - radius;
|
||||
searchArea.minY = y - radius;
|
||||
searchArea.maxX = x + radius;
|
||||
searchArea.maxY = y + radius;
|
||||
var nearbyObstacles = this._obstacleRBush.search(searchArea);
|
||||
result.length = 0;
|
||||
result.push.apply(result, nearbyObstacles);
|
||||
};
|
||||
|
||||
/**
|
||||
* @memberof gdjs
|
||||
* @class LightObstacleRuntimeBehavior
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @param {BehaviorData} behaviorData
|
||||
* @param {gdjs.RuntimeObject} owner
|
||||
*/
|
||||
gdjs.LightObstacleRuntimeBehavior = function (
|
||||
runtimeScene,
|
||||
behaviorData,
|
||||
owner
|
||||
) {
|
||||
gdjs.RuntimeBehavior.call(this, runtimeScene, behaviorData, owner);
|
||||
|
||||
this._oldX = 0;
|
||||
this._oldY = 0;
|
||||
this._oldWidth = 0;
|
||||
this._oldHeight = 0;
|
||||
this._manager = gdjs.LightObstaclesManager.getManager(runtimeScene);
|
||||
this._registeredInManager = false;
|
||||
};
|
||||
|
||||
gdjs.LightObstacleRuntimeBehavior.prototype = Object.create(
|
||||
gdjs.RuntimeBehavior.prototype
|
||||
);
|
||||
gdjs.registerBehavior(
|
||||
'Lighting::LightObstacleBehavior',
|
||||
gdjs.LightObstacleRuntimeBehavior
|
||||
);
|
||||
|
||||
gdjs.LightObstacleRuntimeBehavior.prototype.doStepPreEvents = function (
|
||||
runtimeScene
|
||||
) {
|
||||
// Make sure the obstacle is or is not in the obstacles manager.
|
||||
if (!this.activated() && this._registeredInManager) {
|
||||
this._manager.removeObstacle(this);
|
||||
this._registeredInManager = false;
|
||||
} else if (this.activated() && !this._registeredInManager) {
|
||||
this._manager.addObstacle(this);
|
||||
this._registeredInManager = true;
|
||||
}
|
||||
|
||||
//Track changes in size or position
|
||||
if (
|
||||
this._oldX !== this.owner.getX() ||
|
||||
this._oldY !== this.owner.getY() ||
|
||||
this._oldWidth !== this.owner.getWidth() ||
|
||||
this._oldHeight !== this.owner.getHeight()
|
||||
) {
|
||||
if (this._registeredInManager) {
|
||||
this._manager.removeObstacle(this);
|
||||
this._manager.addObstacle(this);
|
||||
}
|
||||
this._oldX = this.owner.getX();
|
||||
this._oldY = this.owner.getY();
|
||||
this._oldWidth = this.owner.getWidth();
|
||||
this._oldHeight = this.owner.getHeight();
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.LightObstacleRuntimeBehavior.prototype.onDestroy = function () {
|
||||
if (this._manager && this._registeredInManager)
|
||||
this._manager.removeObstacle(this);
|
||||
};
|
||||
|
||||
gdjs.LightObstacleRuntimeBehavior.prototype.onActivate = function () {
|
||||
if (this._registeredInManager) return;
|
||||
|
||||
this._manager.addObstacle(this);
|
||||
this._registeredInManager = true;
|
||||
};
|
||||
|
||||
gdjs.LightObstacleRuntimeBehavior.prototype.onDeActivate = function () {
|
||||
if (!this._registeredInManager) return;
|
||||
|
||||
this._manager.removeObstacle(this);
|
||||
this._registeredInManager = false;
|
||||
};
|
523
Extensions/Lighting/lightruntimeobject-pixi-renderer.js
Normal file
523
Extensions/Lighting/lightruntimeobject-pixi-renderer.js
Normal file
@@ -0,0 +1,523 @@
|
||||
/**
|
||||
* Pixi renderer for light runtime objects.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @constructor LightRuntimeObjectPixiRenderer
|
||||
* @param {gdjs.LightRuntimeObject} runtimeObject
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
*/
|
||||
gdjs.LightRuntimeObjectPixiRenderer = function (runtimeObject, runtimeScene) {
|
||||
this._object = runtimeObject;
|
||||
this._runtimeScene = runtimeScene;
|
||||
this._manager = runtimeObject.getObstaclesManager();
|
||||
this._radius = runtimeObject.getRadius();
|
||||
var objectColor = runtimeObject._color;
|
||||
this._color = [
|
||||
objectColor[0] / 255,
|
||||
objectColor[1] / 255,
|
||||
objectColor[2] / 255,
|
||||
];
|
||||
|
||||
/** @type {?PIXI.Texture} */
|
||||
this._texture = null;
|
||||
this.updateTexture();
|
||||
|
||||
this._center = new Float32Array([runtimeObject.x, runtimeObject.y]);
|
||||
this._defaultVertexBuffer = new Float32Array(8);
|
||||
this._vertexBuffer = new Float32Array([
|
||||
runtimeObject.x - this._radius,
|
||||
runtimeObject.y + this._radius,
|
||||
runtimeObject.x + this._radius,
|
||||
runtimeObject.y + this._radius,
|
||||
runtimeObject.x + this._radius,
|
||||
runtimeObject.y - this._radius,
|
||||
runtimeObject.x - this._radius,
|
||||
runtimeObject.y - this._radius,
|
||||
]);
|
||||
this._indexBuffer = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
||||
|
||||
/** @type {?PIXI.Mesh} */
|
||||
this._light = null;
|
||||
this.updateMesh();
|
||||
|
||||
this._isPreview = runtimeScene.getGame().isPreview();
|
||||
this._debugMode = null;
|
||||
/** @type {?PIXI.Container} */
|
||||
this._debugLight = null;
|
||||
/** @type {?PIXI.Graphics} */
|
||||
this._debugGraphics = null;
|
||||
this.updateDebugMode();
|
||||
|
||||
/** @type {gdjs.Polygon} */
|
||||
this._lightBoundingPoly = new gdjs.Polygon();
|
||||
for (var i = 0; i < 4; i++) {
|
||||
this._lightBoundingPoly.vertices.push(
|
||||
runtimeObject.getHitBoxes()[0].vertices[i]
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectRenderer = gdjs.LightRuntimeObjectPixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer._defaultIndexBuffer = new Uint16Array([
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
]);
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.defaultVertexShader = `
|
||||
precision mediump float;
|
||||
attribute vec2 aVertexPosition;
|
||||
|
||||
uniform mat3 translationMatrix;
|
||||
uniform mat3 projectionMatrix;
|
||||
varying vec2 vPos;
|
||||
|
||||
void main() {
|
||||
vPos = aVertexPosition;
|
||||
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
||||
}`;
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.defaultFragmentShader = `
|
||||
precision mediump float;
|
||||
uniform vec2 center;
|
||||
uniform float radius;
|
||||
uniform vec3 color;
|
||||
varying vec2 vPos;
|
||||
|
||||
void main() {
|
||||
float l = length(vPos - center);
|
||||
float intensity = 0.0;
|
||||
if(l < radius)
|
||||
intensity = clamp((radius - l)*(radius - l)/(radius*radius), 0.0, 1.0);
|
||||
gl_FragColor = vec4(color*intensity, 1.0);
|
||||
}`;
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.texturedFragmentShader = `
|
||||
precision mediump float;
|
||||
uniform vec2 center;
|
||||
uniform float radius;
|
||||
uniform vec3 color;
|
||||
uniform sampler2D uSampler;
|
||||
varying vec2 vPos;
|
||||
|
||||
void main() {
|
||||
vec2 topleft = vec2(center.x - radius, center.y - radius);
|
||||
vec2 texCoord = (vPos - topleft)/(2.0 * radius);
|
||||
gl_FragColor = vec4(color, 1.0) * texture2D(uSampler, texCoord);
|
||||
}`;
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer._verticesWithAngleComparator = function (
|
||||
vertexWithAngleA,
|
||||
vertexWithAngleB
|
||||
) {
|
||||
if (vertexWithAngleA.angle < vertexWithAngleB.angle) return -1;
|
||||
if (vertexWithAngleA.angle === vertexWithAngleB.angle) return 0;
|
||||
if (vertexWithAngleA.angle > vertexWithAngleB.angle) return 1;
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint = function (
|
||||
lightObject,
|
||||
angle,
|
||||
polygons,
|
||||
boundingSquareHalfDiag
|
||||
) {
|
||||
var centerX = lightObject.getX();
|
||||
var centerY = lightObject.getY();
|
||||
var targetX = centerX + boundingSquareHalfDiag * Math.cos(angle);
|
||||
var targetY = centerY + boundingSquareHalfDiag * Math.sin(angle);
|
||||
var minSqDist = boundingSquareHalfDiag * boundingSquareHalfDiag;
|
||||
var closestPoint = [null, null];
|
||||
for (var poly of polygons) {
|
||||
var raycastResult = gdjs.Polygon.raycastTest(
|
||||
poly,
|
||||
centerX,
|
||||
centerY,
|
||||
targetX,
|
||||
targetY
|
||||
);
|
||||
|
||||
if (raycastResult.collision && raycastResult.closeSqDist <= minSqDist) {
|
||||
minSqDist = raycastResult.closeSqDist;
|
||||
closestPoint[0] = raycastResult.closeX;
|
||||
closestPoint[1] = raycastResult.closeY;
|
||||
}
|
||||
}
|
||||
if (closestPoint[0] && closestPoint[1]) return closestPoint;
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {?PIXI.Mesh | PIXI.Container}
|
||||
*/
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype.getRendererObject = function () {
|
||||
if (this._debugLight) {
|
||||
return this._debugLight;
|
||||
}
|
||||
return this._light;
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype.ensureUpToDate = function () {
|
||||
if (this._object.isHidden()) return;
|
||||
|
||||
if (this._debugGraphics) this._updateDebugGraphics();
|
||||
this._updateBuffers();
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateMesh = function () {
|
||||
this.updateTexture();
|
||||
var fragmentShader =
|
||||
this._texture === null
|
||||
? gdjs.LightRuntimeObjectPixiRenderer.defaultFragmentShader
|
||||
: gdjs.LightRuntimeObjectPixiRenderer.texturedFragmentShader;
|
||||
var shaderUniforms = {
|
||||
center: this._center,
|
||||
radius: this._radius,
|
||||
color: this._color,
|
||||
};
|
||||
if (this._texture) {
|
||||
shaderUniforms.uSampler = this._texture;
|
||||
}
|
||||
var shader = PIXI.Shader.from(
|
||||
gdjs.LightRuntimeObjectPixiRenderer.defaultVertexShader,
|
||||
fragmentShader,
|
||||
shaderUniforms
|
||||
);
|
||||
var geometry = new PIXI.Geometry();
|
||||
geometry
|
||||
.addAttribute('aVertexPosition', this._vertexBuffer, 2)
|
||||
.addIndex(this._indexBuffer);
|
||||
if (!this._light) {
|
||||
this._light = new PIXI.Mesh(geometry, shader);
|
||||
this._light.blendMode = PIXI.BLEND_MODES.ADD;
|
||||
} else {
|
||||
this._light.shader = shader;
|
||||
this._light.geometry = geometry;
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateRadius = function () {
|
||||
this._radius = this._object.getRadius();
|
||||
this._light.shader.uniforms.radius = this._radius;
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateColor = function () {
|
||||
var objectColor = this._object._color;
|
||||
this._color = [
|
||||
objectColor[0] / 255,
|
||||
objectColor[1] / 255,
|
||||
objectColor[2] / 255,
|
||||
];
|
||||
this._light.shader.uniforms.color = this._color;
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateTexture = function () {
|
||||
var texture = this._object.getTexture();
|
||||
this._texture =
|
||||
texture !== ''
|
||||
? this._runtimeScene.getGame().getImageManager().getPIXITexture(texture)
|
||||
: null;
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype.updateDebugMode = function () {
|
||||
this._debugMode = this._object.getDebugMode();
|
||||
if (!this._debugLight && (this._isPreview || this._debugMode)) {
|
||||
this._debugLight = new PIXI.Container();
|
||||
this._debugLight.addChild(this._light);
|
||||
}
|
||||
|
||||
if (this._debugMode && !this._debugGraphics) {
|
||||
this._debugGraphics = new PIXI.Graphics();
|
||||
this._debugLight.addChild(this._debugGraphics);
|
||||
}
|
||||
|
||||
if (!this._debugMode && this._debugGraphics) {
|
||||
this._debugLight.removeChild(this._debugGraphics);
|
||||
this._debugGraphics.destroy();
|
||||
this._debugGraphics = null;
|
||||
}
|
||||
|
||||
this.ensureUpToDate();
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype._updateDebugGraphics = function () {
|
||||
var computedVertices = this._computeLightVertices();
|
||||
|
||||
if (!computedVertices.length) {
|
||||
this._debugGraphics.clear();
|
||||
this._debugGraphics
|
||||
.lineStyle(1, 0xff0000, 1)
|
||||
.moveTo(this._object.x, this._object.y)
|
||||
.lineTo(this._object.x - this._radius, this._object.y + this._radius)
|
||||
.lineTo(this._object.x + this._radius, this._object.y + this._radius)
|
||||
.moveTo(this._object.x, this._object.y)
|
||||
.lineTo(this._object.x + this._radius, this._object.y + this._radius)
|
||||
.lineTo(this._object.x + this._radius, this._object.y - this._radius)
|
||||
.moveTo(this._object.x, this._object.y)
|
||||
.lineTo(this._object.x + this._radius, this._object.y - this._radius)
|
||||
.lineTo(this._object.x - this._radius, this._object.y - this._radius)
|
||||
.moveTo(this._object.x, this._object.y)
|
||||
.lineTo(this._object.x - this._radius, this._object.y - this._radius)
|
||||
.lineTo(this._object.x - this._radius, this._object.y + this._radius);
|
||||
return;
|
||||
}
|
||||
|
||||
var vertices = new Array(2 * computedVertices.length + 2);
|
||||
vertices[0] = this._object.x;
|
||||
vertices[1] = this._object.y;
|
||||
|
||||
for (var i = 2; i < 2 * computedVertices.length + 2; i += 2) {
|
||||
vertices[i] = computedVertices[i / 2 - 1][0];
|
||||
vertices[i + 1] = computedVertices[i / 2 - 1][1];
|
||||
}
|
||||
|
||||
this._debugGraphics.clear();
|
||||
this._debugGraphics.moveTo(vertices[2], vertices[3]);
|
||||
var verticesCount = vertices.length;
|
||||
for (var i = 2; i < verticesCount; i += 2) {
|
||||
var lineColor = i % 4 === 0 ? 0xff0000 : 0x00ff00;
|
||||
var lastX = i + 2 >= verticesCount ? 2 : i + 2;
|
||||
var lastY = i + 3 >= verticesCount ? 3 : i + 3;
|
||||
this._debugGraphics
|
||||
.lineStyle(1, lineColor, 1)
|
||||
.lineTo(vertices[i], vertices[i + 1])
|
||||
.lineTo(vertices[lastX], vertices[lastY])
|
||||
.moveTo(vertices[0], vertices[1])
|
||||
.lineTo(vertices[i], vertices[i + 1])
|
||||
.moveTo(vertices[0], vertices[1])
|
||||
.lineTo(vertices[lastX], vertices[lastY]);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype._updateBuffers = function () {
|
||||
this._center[0] = this._object.x;
|
||||
this._center[1] = this._object.y;
|
||||
this._light.shader.uniforms.center = this._center;
|
||||
|
||||
var vertices = this._computeLightVertices();
|
||||
// Fallback to simple quad when there are no obstacles around.
|
||||
if (vertices.length === 0) {
|
||||
this._defaultVertexBuffer[0] = this._object.x - this._radius;
|
||||
this._defaultVertexBuffer[1] = this._object.y + this._radius;
|
||||
this._defaultVertexBuffer[2] = this._object.x + this._radius;
|
||||
this._defaultVertexBuffer[3] = this._object.y + this._radius;
|
||||
this._defaultVertexBuffer[4] = this._object.x + this._radius;
|
||||
this._defaultVertexBuffer[5] = this._object.y - this._radius;
|
||||
this._defaultVertexBuffer[6] = this._object.x - this._radius;
|
||||
this._defaultVertexBuffer[7] = this._object.y - this._radius;
|
||||
|
||||
this._light.geometry
|
||||
.getBuffer('aVertexPosition')
|
||||
.update(this._defaultVertexBuffer);
|
||||
this._light.geometry
|
||||
.getIndex()
|
||||
.update(gdjs.LightRuntimeObjectPixiRenderer._defaultIndexBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
var verticesCount = vertices.length;
|
||||
|
||||
// If the array buffer which is already allocated is atmost
|
||||
// twice the size of memory required, we could avoid re-allocation
|
||||
// and instead use a subarray. Otherwise, allocate new array buffers as
|
||||
// there would be memory wastage.
|
||||
var isSubArrayUsed = false;
|
||||
var vertexBufferSubArray = null;
|
||||
var indexBufferSubArray = null;
|
||||
|
||||
if (this._vertexBuffer.length > 2 * verticesCount + 2) {
|
||||
if (this._vertexBuffer.length < 4 * verticesCount + 4) {
|
||||
isSubArrayUsed = true;
|
||||
vertexBufferSubArray = this._vertexBuffer.subarray(
|
||||
0,
|
||||
2 * verticesCount + 2
|
||||
);
|
||||
indexBufferSubArray = this._indexBuffer.subarray(0, 3 * verticesCount);
|
||||
} else {
|
||||
this._vertexBuffer = new Float32Array(2 * verticesCount + 2);
|
||||
this._indexBuffer = new Uint16Array(3 * verticesCount);
|
||||
}
|
||||
}
|
||||
|
||||
// When the allocated array buffer has less memory than
|
||||
// required, we'll have to allocated new array buffers.
|
||||
if (this._vertexBuffer.length < 2 * verticesCount + 2) {
|
||||
this._vertexBuffer = new Float32Array(2 * verticesCount + 2);
|
||||
this._indexBuffer = new Uint16Array(3 * verticesCount);
|
||||
}
|
||||
|
||||
this._vertexBuffer[0] = this._object.x;
|
||||
this._vertexBuffer[1] = this._object.y;
|
||||
|
||||
for (var i = 2; i < 2 * verticesCount + 2; i += 2) {
|
||||
this._vertexBuffer[i] = vertices[i / 2 - 1][0];
|
||||
this._vertexBuffer[i + 1] = vertices[i / 2 - 1][1];
|
||||
}
|
||||
|
||||
for (var i = 0; i < 3 * verticesCount; i += 3) {
|
||||
this._indexBuffer[i] = 0;
|
||||
this._indexBuffer[i + 1] = i / 3 + 1;
|
||||
if (i / 3 + 1 !== verticesCount) this._indexBuffer[i + 2] = i / 3 + 2;
|
||||
else this._indexBuffer[i + 2] = 1;
|
||||
}
|
||||
|
||||
if (!isSubArrayUsed) {
|
||||
this._light.geometry
|
||||
.getBuffer('aVertexPosition')
|
||||
.update(this._vertexBuffer);
|
||||
this._light.geometry.getIndex().update(this._indexBuffer);
|
||||
} else {
|
||||
this._light.geometry
|
||||
.getBuffer('aVertexPosition')
|
||||
.update(vertexBufferSubArray);
|
||||
this._light.geometry.getIndex().update(indexBufferSubArray);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes the vertices of mesh using raycasting.
|
||||
* @returns {number[][]} the vertices of mesh.
|
||||
*/
|
||||
gdjs.LightRuntimeObjectPixiRenderer.prototype._computeLightVertices = function () {
|
||||
var lightObstacles = [];
|
||||
if (this._manager)
|
||||
this._manager.getAllObstaclesAround(
|
||||
this._object,
|
||||
this._radius,
|
||||
lightObstacles
|
||||
);
|
||||
|
||||
// Bail out early if there are no obstacles.
|
||||
if (lightObstacles.length === 0) return lightObstacles;
|
||||
|
||||
// Synchronize light bounding polygon with the hitbox.
|
||||
var lightHitboxPoly = this._object.getHitBoxes()[0];
|
||||
for (var i = 0; i < 4; i++) {
|
||||
for (var j = 0; j < 2; j++) {
|
||||
this._lightBoundingPoly.vertices[i][j] = lightHitboxPoly.vertices[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
var obstaclesCount = lightObstacles.length;
|
||||
var obstacleHitBoxes = new Array(obstaclesCount);
|
||||
for (var i = 0; i < obstaclesCount; i++) {
|
||||
obstacleHitBoxes[i] = lightObstacles[i].owner.getHitBoxes();
|
||||
}
|
||||
|
||||
var obstaclePolygons = [];
|
||||
obstaclePolygons.push(this._lightBoundingPoly);
|
||||
for (var i = 0; i < obstaclesCount; i++) {
|
||||
var noOfHitBoxes = obstacleHitBoxes[i].length;
|
||||
for (var j = 0; j < noOfHitBoxes; j++)
|
||||
obstaclePolygons.push(obstacleHitBoxes[i][j]);
|
||||
}
|
||||
|
||||
var maxX = this._object.x + this._radius;
|
||||
var minX = this._object.x - this._radius;
|
||||
var maxY = this._object.y + this._radius;
|
||||
var minY = this._object.y - this._radius;
|
||||
|
||||
var flattenVertices = [];
|
||||
for (var i = 1; i < obstaclePolygons.length; i++) {
|
||||
var vertices = obstaclePolygons[i].vertices;
|
||||
var verticesCount = vertices.length;
|
||||
for (var j = 0; j < verticesCount; j++) {
|
||||
flattenVertices.push(vertices[j]);
|
||||
|
||||
if (vertices[j][0] < minX) minX = vertices[j][0];
|
||||
if (vertices[j][0] > maxX) maxX = vertices[j][0];
|
||||
if (vertices[j][1] < minY) minY = vertices[j][1];
|
||||
if (vertices[j][1] > maxY) maxY = vertices[j][1];
|
||||
}
|
||||
}
|
||||
|
||||
obstaclePolygons[0].vertices[0][0] = minX;
|
||||
obstaclePolygons[0].vertices[0][1] = minY;
|
||||
obstaclePolygons[0].vertices[1][0] = maxX;
|
||||
obstaclePolygons[0].vertices[1][1] = minY;
|
||||
obstaclePolygons[0].vertices[2][0] = maxX;
|
||||
obstaclePolygons[0].vertices[2][1] = maxY;
|
||||
obstaclePolygons[0].vertices[3][0] = minX;
|
||||
obstaclePolygons[0].vertices[3][1] = maxY;
|
||||
|
||||
// Find the largest diagonal length.
|
||||
var boundingSquareHalfDiag = Math.sqrt(
|
||||
Math.max(
|
||||
(this._object.x - minX) * (this._object.x - minX) +
|
||||
(this._object.y - minY) * (this._object.y - minY),
|
||||
(maxX - this._object.x) * (maxX - this._object.x) +
|
||||
(this._object.y - minY) * (this._object.y - minY),
|
||||
(maxX - this._object.x) * (maxX - this._object.x) +
|
||||
(maxY - this._object.y) * (maxY - this._object.y),
|
||||
(this._object.x - minX) * (this._object.x - minX) +
|
||||
(maxY - this._object.y) * (maxY - this._object.y)
|
||||
)
|
||||
);
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
flattenVertices.push(obstaclePolygons[0].vertices[i]);
|
||||
}
|
||||
|
||||
var closestVertices = [];
|
||||
var flattenVerticesCount = flattenVertices.length;
|
||||
for (var i = 0; i < flattenVerticesCount; i++) {
|
||||
var xdiff = flattenVertices[i][0] - this._object.x;
|
||||
var ydiff = flattenVertices[i][1] - this._object.y;
|
||||
var angle = Math.atan2(ydiff, xdiff);
|
||||
|
||||
var closestVertex = gdjs.LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
|
||||
this._object,
|
||||
angle,
|
||||
obstaclePolygons,
|
||||
boundingSquareHalfDiag
|
||||
);
|
||||
if (closestVertex) {
|
||||
closestVertices.push({
|
||||
vertex: closestVertex,
|
||||
angle: angle,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Check whether we need to raycast these two extra rays or not.
|
||||
var closestVertexOffsetLeft = gdjs.LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
|
||||
this._object,
|
||||
angle + 0.0001,
|
||||
obstaclePolygons,
|
||||
boundingSquareHalfDiag
|
||||
);
|
||||
if (closestVertexOffsetLeft) {
|
||||
closestVertices.push({
|
||||
vertex: closestVertexOffsetLeft,
|
||||
angle: angle + 0.0001,
|
||||
});
|
||||
}
|
||||
var closestVertexOffsetRight = gdjs.LightRuntimeObjectPixiRenderer._computeClosestIntersectionPoint(
|
||||
this._object,
|
||||
angle - 0.0001,
|
||||
obstaclePolygons,
|
||||
boundingSquareHalfDiag
|
||||
);
|
||||
if (closestVertexOffsetRight) {
|
||||
closestVertices.push({
|
||||
vertex: closestVertexOffsetRight,
|
||||
angle: angle - 0.0001,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
closestVertices.sort(
|
||||
gdjs.LightRuntimeObjectPixiRenderer._verticesWithAngleComparator
|
||||
);
|
||||
|
||||
var filteredVerticesResult = [closestVertices[0].vertex];
|
||||
var closestVerticesCount = closestVertices.length;
|
||||
for (var i = 1; i < closestVerticesCount; i++) {
|
||||
if (closestVertices[i].angle !== closestVertices[i - 1].angle)
|
||||
filteredVerticesResult.push(closestVertices[i].vertex);
|
||||
}
|
||||
|
||||
return filteredVerticesResult;
|
||||
};
|
193
Extensions/Lighting/lightruntimeobject.js
Normal file
193
Extensions/Lighting/lightruntimeobject.js
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* @typedef {Object} LightObjectDataType
|
||||
* @property {Object} content The base parameters of light object.
|
||||
* @property {number} content.radius The radius of light object.
|
||||
* @property {string} content.color A string representing color in hexadecimal format.
|
||||
* @property {string} content.texture A string representing the name of texture used for light object.
|
||||
* @property {boolean} content.debugMode true if the light objects shows debug graphics, false otherwise.
|
||||
*
|
||||
* @typedef {ObjectData & LightObjectDataType} LightObjectData
|
||||
*/
|
||||
|
||||
/**
|
||||
* Displays a Light object.
|
||||
* @memberof gdjs
|
||||
* @class LightRuntimeObject
|
||||
* @extends RuntimeObject
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @param {LightObjectData} lightObjectData
|
||||
*/
|
||||
gdjs.LightRuntimeObject = function (runtimeScene, lightObjectData) {
|
||||
gdjs.RuntimeObject.call(this, runtimeScene, lightObjectData);
|
||||
|
||||
/** @type {number} */
|
||||
this._radius =
|
||||
lightObjectData.content.radius > 0 ? lightObjectData.content.radius : 1;
|
||||
|
||||
/** @type {number[]} color in format [r, g, b], where each component is in the range [0, 255] */
|
||||
this._color = gdjs.LightRuntimeObject.hexToRGBColor(
|
||||
lightObjectData.content.color
|
||||
);
|
||||
|
||||
/** @type {boolean} */
|
||||
this._debugMode = lightObjectData.content.debugMode;
|
||||
|
||||
/** @type {string} */
|
||||
this._texture = lightObjectData.content.texture;
|
||||
|
||||
/** @type {gdjs.LightObstaclesManager} */
|
||||
this._obstaclesManager = gdjs.LightObstaclesManager.getManager(runtimeScene);
|
||||
|
||||
if (this._renderer)
|
||||
gdjs.LightRuntimeObjectRenderer.call(this._renderer, this, runtimeScene);
|
||||
else this._renderer = new gdjs.LightRuntimeObjectRenderer(this, runtimeScene);
|
||||
|
||||
/** @type {gdjs.RuntimeScene} */
|
||||
this._runtimeScene = runtimeScene;
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObject.prototype = Object.create(gdjs.RuntimeObject.prototype);
|
||||
gdjs.registerObject('Lighting::LightObject', gdjs.LightRuntimeObject);
|
||||
|
||||
gdjs.LightRuntimeObject.hexToRGBColor = function (hex) {
|
||||
var hexNumber = parseInt(hex.replace('#', ''), 16);
|
||||
return [(hexNumber >> 16) & 0xff, (hexNumber >> 8) & 0xff, hexNumber & 0xff];
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObject.prototype.getRendererObject = function () {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {LightObjectData} oldObjectData
|
||||
* @param {LightObjectData} newObjectData
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.updateFromObjectData = function (
|
||||
oldObjectData,
|
||||
newObjectData
|
||||
) {
|
||||
if (oldObjectData.content.radius !== newObjectData.content.radius)
|
||||
this.setRadius(newObjectData.content.radius);
|
||||
|
||||
if (oldObjectData.content.color !== newObjectData.content.radius) {
|
||||
this._color = gdjs.LightRuntimeObject.hexToRGBColor(
|
||||
newObjectData.content.color
|
||||
);
|
||||
this._renderer.updateColor();
|
||||
}
|
||||
|
||||
if (oldObjectData.content.texture !== newObjectData.content.texture) {
|
||||
this._texture = newObjectData.content.texture;
|
||||
this._renderer.updateMesh();
|
||||
}
|
||||
|
||||
if (oldObjectData.content.debugMode !== newObjectData.content.debugMode) {
|
||||
this._debugMode = newObjectData.content.debugMode;
|
||||
this._renderer.updateDebugMode();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.LightRuntimeObject.prototype.update = function () {
|
||||
this._renderer.ensureUpToDate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the radius of the light object.
|
||||
* @returns {number} radius of the light object.
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.getRadius = function () {
|
||||
return this._radius;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the radius of the light object.
|
||||
* @param {number} radius
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.setRadius = function (radius) {
|
||||
this._radius = radius > 0 ? radius : 1;
|
||||
this._renderer.updateRadius();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the height of the light object.
|
||||
* @returns {number} height of light object.
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.getHeight = function () {
|
||||
return 2 * this._radius;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the width of the light object.
|
||||
* @returns {number} width of light object.
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.getWidth = function () {
|
||||
return 2 * this._radius;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the x co-ordinate of the top-left vertex/point of light object.
|
||||
* @returns {number} x co-ordinate of the top-left vertex/point.
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.getDrawableX = function () {
|
||||
return this.x - this._radius;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the y co-ordinate of the top-left vertex/point of light object.
|
||||
* @returns {number} y co-ordinate of the top-left vertex/point.
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.getDrawableY = function () {
|
||||
return this.y - this._radius;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the color of the light object as a "R;G;B" string.
|
||||
* @returns {string} the color of light object in "R;G;B" format.
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.getColor = function () {
|
||||
return this._color[0] + ';' + this._color[1] + ';' + this._color[2];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the color of the light object in format "R;G;B" string, with components in the range of [0-255].
|
||||
* @param {string} color
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.setColor = function (color) {
|
||||
var rgbColor = color.split(';');
|
||||
this._color = [
|
||||
parseInt(rgbColor[0], 10),
|
||||
parseInt(rgbColor[1], 10),
|
||||
parseInt(rgbColor[2], 10),
|
||||
];
|
||||
this._renderer.updateColor();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the light obstacles manager if objects with the behavior exist, null otherwise.
|
||||
* @returns {?gdjs.LightObstaclesManager} gdjs.LightObstaclesManager if it exists, otherwise null.
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.getObstaclesManager = function () {
|
||||
return this._obstaclesManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the light shows debug graphics, false otherwise.
|
||||
* @returns {boolean} true if debug mode is activated.
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.getDebugMode = function () {
|
||||
return this._debugMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the path of texture resource.
|
||||
* @returns {string} the path of texture.
|
||||
*/
|
||||
gdjs.LightRuntimeObject.prototype.getTexture = function () {
|
||||
return this._texture;
|
||||
};
|
191
Extensions/Lighting/tests/lightruntimeobject.spec.js
Normal file
191
Extensions/Lighting/tests/lightruntimeobject.spec.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Tests for Light Object
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility function for adding light object for tests.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @param {number} radius
|
||||
* @returns {gdjs.LightRuntimeObject}
|
||||
*/
|
||||
const addLightObject = (runtimeScene, radius) => {
|
||||
const lightObj = new gdjs.LightRuntimeObject(runtimeScene, {
|
||||
name: 'lightObject',
|
||||
type: 'Lighting::LightObject',
|
||||
variables: [],
|
||||
behaviors: [],
|
||||
content: {
|
||||
radius: radius,
|
||||
color: '#b4b4b4',
|
||||
texture: '',
|
||||
debugMode: false,
|
||||
},
|
||||
});
|
||||
runtimeScene.addObject(lightObj);
|
||||
return lightObj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function for adding light obstacle for tests.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
* @returns {gdjs.RuntimeObject}
|
||||
*/
|
||||
const addLightObstacle = (runtimeScene, width, height) => {
|
||||
const obstacle = new gdjs.RuntimeObject(runtimeScene, {
|
||||
name: 'lightObstacle',
|
||||
type: '',
|
||||
behaviors: [
|
||||
{
|
||||
type: 'Lighting::LightObstacleBehavior',
|
||||
},
|
||||
],
|
||||
});
|
||||
obstacle.getWidth = function () {
|
||||
return width;
|
||||
};
|
||||
obstacle.getHeight = function () {
|
||||
return height;
|
||||
};
|
||||
runtimeScene.addObject(obstacle);
|
||||
return obstacle;
|
||||
};
|
||||
|
||||
describe('gdjs.LightRuntimeObject', function () {
|
||||
const runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
resources: {
|
||||
resources: [],
|
||||
},
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
});
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
const lightObj = addLightObject(runtimeScene, 100);
|
||||
lightObj.setPosition(200, 200);
|
||||
|
||||
it('check object properties', function () {
|
||||
expect(lightObj.getRadius()).to.be(100);
|
||||
expect(lightObj.getColor()).to.eql("180;180;180");
|
||||
expect(lightObj.getDebugMode()).to.be(false);
|
||||
expect(lightObj.getDrawableX()).to.be(100);
|
||||
expect(lightObj.getDrawableY()).to.be(100);
|
||||
});
|
||||
|
||||
it('bail out early while raycasting when there is no light obstacle', function () {
|
||||
expect(lightObj._renderer._computeLightVertices()).to.eql([]);
|
||||
lightObj._renderer._updateBuffers();
|
||||
expect(lightObj._renderer._defaultVertexBuffer).to.eql(
|
||||
new Float32Array([100, 300, 300, 300, 300, 100, 100, 100])
|
||||
);
|
||||
expect(gdjs.LightRuntimeObjectPixiRenderer._defaultIndexBuffer).to.eql(
|
||||
new Float32Array([0, 1, 2, 0, 2, 3])
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Light with obstacles around it', function () {
|
||||
const runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
resources: {
|
||||
resources: [],
|
||||
},
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
});
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers: [{ name: '', visibility: true, effects: [] }],
|
||||
variables: [],
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
});
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return (1 / 60) * 1000;
|
||||
};
|
||||
const light = addLightObject(runtimeScene, 100);
|
||||
const obstacle = addLightObstacle(runtimeScene, 50, 50);
|
||||
|
||||
it('Vertex and index buffers when light obstacle is present.', function () {
|
||||
light.setPosition(200, 200);
|
||||
obstacle.setPosition(250, 250);
|
||||
|
||||
runtimeScene.renderAndStep();
|
||||
light.update();
|
||||
|
||||
const vertexBuffer = light._renderer._vertexBuffer;
|
||||
const indexBuffer = light._renderer._indexBuffer;
|
||||
// prettier-ignore
|
||||
const expectedVertexBuffer = [
|
||||
200, 200, 100, 100.0199966430664, 100, 100, 100.0199966430664, 100, 299.9800109863281,
|
||||
100, 300, 100, 300, 100.0199966430664, 300, 249.9875030517578, 300, 250, 299.9750061035156,
|
||||
250, 250.00999450683594, 250, 250, 250, 250,250.00999450683594, 250, 299.9750061035156, 250,
|
||||
300, 249.9875030517578, 300, 100.0199966430664, 300, 100, 300, 100, 299.9800109863281,
|
||||
];
|
||||
// prettier-ignore
|
||||
const expectedIndexBuffer = [
|
||||
0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6,
|
||||
0, 6, 7, 0, 7, 8, 0, 8, 9, 0, 9, 10, 0, 10, 11,
|
||||
0, 11, 12, 0, 12, 13, 0, 13, 14, 0, 14, 15, 0,
|
||||
15, 16, 0, 16, 17, 0, 17, 18, 0, 18, 1,
|
||||
];
|
||||
|
||||
expectedVertexBuffer.forEach((val, index) => {
|
||||
expect(vertexBuffer[index]).to.be(val);
|
||||
});
|
||||
expectedIndexBuffer.forEach((val, index) => {
|
||||
expect(indexBuffer[index]).to.be(val);
|
||||
});
|
||||
});
|
||||
|
||||
it('Vertex and index buffers after obstacle is moved.', function () {
|
||||
obstacle.setPosition(150, 250);
|
||||
runtimeScene.renderAndStep();
|
||||
light.update();
|
||||
|
||||
const vertexBuffer = light._renderer._vertexBuffer;
|
||||
const indexBuffer = light._renderer._indexBuffer;
|
||||
// prettier-ignore
|
||||
const expectedVertexBuffer = [
|
||||
200, 200, 100, 100.0199966430664, 100, 100, 100.0199966430664, 100, 299.9800109863281,
|
||||
100, 300, 100, 300, 100.0199966430664, 300, 299.9800109863281, 300, 300, 299.9800109863281,
|
||||
300, 200.00999450683594, 300, 200, 250, 199.9949951171875, 250, 175.00625610351562, 250,
|
||||
175, 250, 174.99374389648438, 250, 150.00999450683594, 250, 150, 250, 100, 299.9800109863281,
|
||||
];
|
||||
// prettier-ignore
|
||||
const expectedIndexBuffer = [
|
||||
0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6, 0,
|
||||
6, 7, 0, 7, 8, 0, 8, 9, 0, 9, 10, 0, 10, 11, 0,
|
||||
11, 12, 0, 12, 13, 0, 13, 14, 0, 14, 15, 0, 15,
|
||||
16, 0, 16, 17, 0, 17, 18, 0, 18, 1,
|
||||
];
|
||||
|
||||
expectedVertexBuffer.forEach((val, index) => {
|
||||
expect(vertexBuffer[index]).to.be(val);
|
||||
});
|
||||
expectedIndexBuffer.forEach((val, index) => {
|
||||
expect(indexBuffer[index]).to.be(val);
|
||||
});
|
||||
});
|
||||
|
||||
it("Obstacle moved outside light's radius.", function () {
|
||||
obstacle.setPosition(400, 400);
|
||||
runtimeScene.renderAndStep();
|
||||
light.update();
|
||||
// Ensure the fallback to simple quads. There shouldn't be anymore calculations
|
||||
// when the obstacle is not inside light's area.
|
||||
expect(light._renderer._computeLightVertices().length).to.eql(0);
|
||||
|
||||
const vertexBuffer = light._renderer._defaultVertexBuffer;
|
||||
const indexBuffer = gdjs.LightRuntimeObjectPixiRenderer._defaultIndexBuffer;
|
||||
const vertexData = [100, 300, 300, 300, 300, 100, 100, 100];
|
||||
const indexData = [0, 1, 2, 0, 2, 3];
|
||||
|
||||
vertexData.forEach((val, index) => {
|
||||
expect(vertexBuffer[index]).to.be(val);
|
||||
});
|
||||
indexData.forEach((val, index) => {
|
||||
expect(indexBuffer[index]).to.be(val);
|
||||
});
|
||||
});
|
||||
});
|
@@ -49,7 +49,7 @@ gdjs.LinksManager.prototype.removeAllLinksOf = function(obj) {
|
||||
if ( this.links.hasOwnProperty(objLinkedObjects[i].id) ) {
|
||||
var otherObjList = this.links[objLinkedObjects[i].id];
|
||||
var index = otherObjList.indexOf(obj);
|
||||
if ( index !== -1) otherObjList.remove(index);
|
||||
if ( index !== -1) otherObjList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,13 +63,13 @@ gdjs.LinksManager.prototype.removeLinkBetween = function(objA, objB) {
|
||||
if ( this.links.hasOwnProperty(objA.id) ) {
|
||||
list = this.links[objA.id];
|
||||
index = list.indexOf(objB);
|
||||
if ( index !== -1) list.remove(index);
|
||||
if ( index !== -1) list.splice(index, 1);
|
||||
}
|
||||
|
||||
if ( this.links.hasOwnProperty(objB.id) ) {
|
||||
list = this.links[objB.id];
|
||||
index = list.indexOf(objA);
|
||||
if ( index !== -1) list.remove(index);
|
||||
if ( index !== -1) list.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -3,7 +3,7 @@ describe('gdjs.LinksManager', function() {
|
||||
var runtimeGame = new gdjs.RuntimeGame({variables: [], properties: {windowWidth: 800, windowHeight: 600}, resources: {resources: []}});
|
||||
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers:[{name:"", visibility: true}],
|
||||
layers:[{name:"", visibility: true, effects: []}],
|
||||
variables: [],
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
|
71
Extensions/P2P/A_peer.js
Normal file
71
Extensions/P2P/A_peer.js
Normal file
File diff suppressed because one or more lines are too long
339
Extensions/P2P/B_p2ptools.js
Normal file
339
Extensions/P2P/B_p2ptools.js
Normal file
@@ -0,0 +1,339 @@
|
||||
// @ts-check
|
||||
/// <reference path="peerjs" />
|
||||
|
||||
/**
|
||||
* Tools for p2p multiplayer.
|
||||
* @namespace
|
||||
*/
|
||||
gdjs.evtTools.p2p = {
|
||||
/**
|
||||
* The peer to peer configuration.
|
||||
*/
|
||||
peerConfig: { debug: 1 }, // Enable logging of critical errors
|
||||
|
||||
/**
|
||||
* The p2p client.
|
||||
* @type {?Peer}
|
||||
*/
|
||||
peer: null,
|
||||
|
||||
/**
|
||||
* All connected p2p clients, keyed by their id.
|
||||
*/
|
||||
connections: {},
|
||||
|
||||
/**
|
||||
* Contains a list of events triggered by other p2p clients.
|
||||
*/
|
||||
triggeredEvents: {},
|
||||
|
||||
/**
|
||||
* Contains the latest data sent with each event.
|
||||
*/
|
||||
lastEventData: {},
|
||||
|
||||
/**
|
||||
* Tells how to handle an event (with or without data loss)
|
||||
*/
|
||||
eventHandling: {},
|
||||
|
||||
/**
|
||||
* True if PeerJS is initialized and ready.
|
||||
*/
|
||||
ready: false,
|
||||
|
||||
/**
|
||||
* True if an error occured.
|
||||
*/
|
||||
error: false,
|
||||
|
||||
/**
|
||||
* Last error's message.
|
||||
*/
|
||||
lastError: '',
|
||||
|
||||
/**
|
||||
* True if a peer diconnected.
|
||||
*/
|
||||
peerJustDisconnected: false,
|
||||
|
||||
/**
|
||||
* The last peer that has disconnected.
|
||||
*/
|
||||
lastDisconnectedPeerId: '',
|
||||
};
|
||||
|
||||
gdjs.evtTools.p2p.loadPeerJS = function () {
|
||||
if (gdjs.evtTools.p2p.peer != null) return;
|
||||
gdjs.evtTools.p2p.peer = new Peer(gdjs.evtTools.p2p.peerConfig);
|
||||
gdjs.evtTools.p2p.peer.on('open', function () {
|
||||
gdjs.evtTools.p2p.ready = true;
|
||||
});
|
||||
gdjs.evtTools.p2p.peer.on('error', function (errorMessage) {
|
||||
gdjs.evtTools.p2p.error = true;
|
||||
gdjs.evtTools.p2p.lastError = errorMessage;
|
||||
});
|
||||
gdjs.evtTools.p2p.peer.on('connection', gdjs.evtTools.p2p._onConnection);
|
||||
gdjs.evtTools.p2p.peer.on('close', function () {
|
||||
gdjs.evtTools.p2p.peer = null;
|
||||
gdjs.evtTools.p2p.loadPeerJS();
|
||||
});
|
||||
gdjs.evtTools.p2p.peer.on('disconnected', gdjs.evtTools.p2p.peer.reconnect);
|
||||
};
|
||||
|
||||
gdjs.evtTools.p2p._onConnection = function (connection) {
|
||||
gdjs.evtTools.p2p.connections[connection.peer] = connection;
|
||||
connection.on('data', function (data) {
|
||||
if (data.eventName === undefined) return;
|
||||
var dataLoss = gdjs.evtTools.p2p.eventHandling[data.eventName];
|
||||
|
||||
if (typeof dataLoss === 'undefined' || dataLoss === false) {
|
||||
if (typeof gdjs.evtTools.p2p.lastEventData[data.eventName] !== 'object')
|
||||
gdjs.evtTools.p2p.lastEventData[data.eventName] = [];
|
||||
gdjs.evtTools.p2p.lastEventData[data.eventName].push(data.data);
|
||||
} else {
|
||||
gdjs.evtTools.p2p.triggeredEvents[data.eventName] = true;
|
||||
gdjs.evtTools.p2p.lastEventData[data.eventName] = data.data;
|
||||
}
|
||||
});
|
||||
connection.on('error', function () {
|
||||
// Close event is only for graceful disconnection, also handle error aka ungraceful disconnection
|
||||
gdjs.evtTools.p2p._onDisconnect(connection.peer);
|
||||
});
|
||||
connection.on('close', function () {
|
||||
gdjs.evtTools.p2p._onDisconnect(connection.peer);
|
||||
});
|
||||
// Regularly check for disconnection as the built in way is not reliable.
|
||||
var disconnectChecker = function () {
|
||||
if (
|
||||
connection.peerConnection.connectionState === 'failed' ||
|
||||
connection.peerConnection.connectionState === 'disconnected'
|
||||
) {
|
||||
gdjs.evtTools.p2p._onDisconnect(connection.peer);
|
||||
} else {
|
||||
setTimeout(disconnectChecker, 500);
|
||||
}
|
||||
};
|
||||
disconnectChecker();
|
||||
};
|
||||
|
||||
gdjs.evtTools.p2p._onDisconnect = function (connectionID) {
|
||||
gdjs.evtTools.p2p.peerJustDisconnected = true;
|
||||
gdjs.evtTools.p2p.lastDisconnectedPeerId = connectionID;
|
||||
delete gdjs.evtTools.p2p.connections[connectionID];
|
||||
};
|
||||
|
||||
/**
|
||||
* Connects to another p2p client.
|
||||
* @param {string} id - The other client's id.
|
||||
*/
|
||||
gdjs.evtTools.p2p.connect = function (id) {
|
||||
var connection = gdjs.evtTools.p2p.peer.connect(id);
|
||||
connection.on('open', function () {
|
||||
gdjs.evtTools.p2p._onConnection(connection);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true when the event got triggered by another p2p client.
|
||||
* @param {string} eventName
|
||||
* @param {boolean} _dataLoss Is data loss allowed (accelerates event handling when true)?
|
||||
* @returns {boolean}
|
||||
*/
|
||||
gdjs.evtTools.p2p.onEvent = function (eventName, _dataLoss) {
|
||||
var dataLoss = gdjs.evtTools.p2p.eventHandling[eventName];
|
||||
if (dataLoss == undefined) {
|
||||
gdjs.evtTools.p2p.eventHandling[eventName] = _dataLoss;
|
||||
return gdjs.evtTools.p2p.onEvent(eventName, _dataLoss);
|
||||
}
|
||||
if (dataLoss) {
|
||||
var returnValue = gdjs.evtTools.p2p.triggeredEvents[eventName];
|
||||
if (typeof returnValue === 'undefined') return false;
|
||||
gdjs.evtTools.p2p.triggeredEvents[eventName] = false;
|
||||
return returnValue;
|
||||
} else {
|
||||
var returnValue = gdjs.evtTools.p2p.lastEventData[eventName];
|
||||
if (typeof returnValue === 'undefined') return false;
|
||||
return returnValue.length !== 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an event to one specific connected client.
|
||||
* @param {string} id - The id of the client to send the event to.
|
||||
* @param {string} eventName - The event to trigger.
|
||||
* @param {string} [eventData] - Additional data to send with the event.
|
||||
*/
|
||||
gdjs.evtTools.p2p.sendDataTo = function (id, eventName, eventData) {
|
||||
if (gdjs.evtTools.p2p.connections[id])
|
||||
gdjs.evtTools.p2p.connections[id].send({
|
||||
eventName: eventName,
|
||||
data: eventData,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an event to all connected clients.
|
||||
* @param {string} eventName - The event to trigger.
|
||||
* @param {string} [eventData] - Additional data to send with the event.
|
||||
*/
|
||||
gdjs.evtTools.p2p.sendDataToAll = function (eventName, eventData) {
|
||||
for (var id in gdjs.evtTools.p2p.connections) {
|
||||
gdjs.evtTools.p2p.connections[id].send({
|
||||
eventName: eventName,
|
||||
data: eventData,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an event to one specific connected client.
|
||||
* @param {string} id - The id of the client to send the event to.
|
||||
* @param {string} eventName - The event to trigger.
|
||||
* @param {gdjs.Variable} variable - Additional variable to send with the event.
|
||||
*/
|
||||
gdjs.evtTools.p2p.sendVariableTo = function (id, eventName, variable) {
|
||||
if (gdjs.evtTools.p2p.connections[id])
|
||||
gdjs.evtTools.p2p.connections[id].send({
|
||||
eventName: eventName,
|
||||
data: gdjs.evtTools.network.variableStructureToJSON(variable),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an event to all connected clients.
|
||||
* @param {string} eventName - The event to trigger.
|
||||
* @param {gdjs.Variable} variable - Additional variable to send with the event.
|
||||
*/
|
||||
gdjs.evtTools.p2p.sendVariableToAll = function (eventName, variable) {
|
||||
for (var id in gdjs.evtTools.p2p.connections) {
|
||||
gdjs.evtTools.p2p.connections[id].send({
|
||||
eventName: eventName,
|
||||
data: gdjs.evtTools.network.variableStructureToJSON(variable),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get some data associated to the last trigger of an event.
|
||||
* @param {string} eventName - The event to get data from.
|
||||
* @returns {string} - The data as JSON.
|
||||
*/
|
||||
gdjs.evtTools.p2p.getEventData = function (eventName) {
|
||||
var dataLoss = gdjs.evtTools.p2p.eventHandling[eventName];
|
||||
if (typeof dataLoss === 'undefined' || dataLoss === false) {
|
||||
var event = gdjs.evtTools.p2p.lastEventData[eventName];
|
||||
return event[event.length - 1];
|
||||
} else {
|
||||
return gdjs.evtTools.p2p.lastEventData[eventName];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a variable associated to the last trigger of an event.
|
||||
* @param {string} eventName - The event to get the variable from.
|
||||
* @param {gdjs.Variable} variable - The variable where to store the variable content.
|
||||
*/
|
||||
gdjs.evtTools.p2p.getEventVariable = function (eventName, variable) {
|
||||
gdjs.evtTools.network.jsonToVariableStructure(
|
||||
gdjs.evtTools.p2p.getEventData(eventName),
|
||||
variable
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Connects to a custom broker server.
|
||||
* @param {string} host The host of the broker server.
|
||||
* @param {number} port The port of the broker server.
|
||||
* @param {string} path The path (part of the url after the host) to the broker server.
|
||||
* @param {string} key Optional password to connect to the broker server.
|
||||
* @param {boolean} ssl Use ssl?
|
||||
*/
|
||||
gdjs.evtTools.p2p.useCustomBrokerServer = function (
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
key,
|
||||
ssl
|
||||
) {
|
||||
key = key.length === 0 ? 'peerjs' : key; // All servers have "peerjs" as default key
|
||||
gdjs.evtTools.p2p.peerConfig = {
|
||||
debug: 1,
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
secure: ssl,
|
||||
key,
|
||||
};
|
||||
gdjs.evtTools.p2p.loadPeerJS();
|
||||
};
|
||||
|
||||
/**
|
||||
* Use default broker server.
|
||||
* This is not recommended for published games,
|
||||
* this server should only be used for quick testing in development.
|
||||
*/
|
||||
gdjs.evtTools.p2p.useDefaultBrokerServer = function () {
|
||||
gdjs.evtTools.p2p.loadPeerJS();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the own current peer ID
|
||||
* @see Peer.id
|
||||
* @returns {string}
|
||||
*/
|
||||
gdjs.evtTools.p2p.getCurrentId = function () {
|
||||
if (gdjs.evtTools.p2p.peer == undefined) return '';
|
||||
return gdjs.evtTools.p2p.peer.id || '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true once PeerJS is initialized
|
||||
* @see gdjs.evtTools.p2p.ready
|
||||
* @returns {boolean}
|
||||
*/
|
||||
gdjs.evtTools.p2p.isReady = function () {
|
||||
return gdjs.evtTools.p2p.ready;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true once when there is an error.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
gdjs.evtTools.p2p.onError = function () {
|
||||
var returnValue = gdjs.evtTools.p2p.error;
|
||||
gdjs.evtTools.p2p.error = false;
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the latest error message.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
gdjs.evtTools.p2p.getLastError = function () {
|
||||
return gdjs.evtTools.p2p.lastError;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true once a peer disconnected.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
gdjs.evtTools.p2p.onDisconnect = function () {
|
||||
var returnValue = gdjs.evtTools.p2p.peerJustDisconnected;
|
||||
gdjs.evtTools.p2p.peerJustDisconnected = false;
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
gdjs.evtTools.p2p.getDisconnectedPeer = function () {
|
||||
return gdjs.evtTools.p2p.lastDisconnectedPeerId;
|
||||
};
|
||||
|
||||
gdjs.callbacksRuntimeScenePostEvents.push(function () {
|
||||
for (var i in gdjs.evtTools.p2p.lastEventData) {
|
||||
if (
|
||||
typeof gdjs.evtTools.p2p.lastEventData[i] === 'object' &&
|
||||
gdjs.evtTools.p2p.lastEventData[i].length > 0
|
||||
)
|
||||
gdjs.evtTools.p2p.lastEventData[i].pop();
|
||||
}
|
||||
});
|
333
Extensions/P2P/JsExtension.js
Normal file
333
Extensions/P2P/JsExtension.js
Normal file
@@ -0,0 +1,333 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
* and search for any errors.
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/*::
|
||||
// Import types to allow Flow to do static type checking on this file.
|
||||
// Extensions declaration are typed using Flow (like the editor), but the files
|
||||
// for the game engine are checked with TypeScript annotations.
|
||||
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createExtension: function (
|
||||
_ /*: (string) => string */,
|
||||
gd /*: libGDevelop */
|
||||
) {
|
||||
const extension /*: gdPlatformExtension */ = new gd.PlatformExtension();
|
||||
extension
|
||||
.setExtensionInformation(
|
||||
'P2P',
|
||||
_('Peer-to-Peer communication (experimental)'),
|
||||
_(
|
||||
'Allow game instances to communicate remotely using messages sent via WebRTC (P2P)'
|
||||
),
|
||||
'Arthur Pacaud (arthuro555)',
|
||||
'MIT'
|
||||
)
|
||||
.setExtensionHelpPath('/all-features/p2p');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'OnEvent',
|
||||
_('Event triggered by peer'),
|
||||
_('Triggers once when a connected client sends the event'),
|
||||
_('Event _PARAM0_ received from other client (data loss: _PARAM1_)'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.addParameter('string', _('Event name'), '', false)
|
||||
.addParameter('yesorno', _('Data loss allowed?'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.onEvent');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'IsReady',
|
||||
_('Is P2P ready'),
|
||||
_(
|
||||
'True if the peer-to-peer extension initialized and is ready to use.'
|
||||
),
|
||||
_('Is P2P ready?'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.isReady');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'OnError',
|
||||
_('An error occurred'),
|
||||
_(
|
||||
'Triggers once when an error occurs. ' +
|
||||
'Use P2P::GetLastError() expression to get the content of the error ' +
|
||||
'if you want to analyse it or display it to the user.'
|
||||
),
|
||||
_('P2P error occurred'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.onError');
|
||||
|
||||
extension
|
||||
.addCondition(
|
||||
'OnDisconnection',
|
||||
_('Peer disconnected'),
|
||||
_('Triggers once when a peer disconnects.'),
|
||||
_('P2P peer disconnected'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.onDisconnect');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'Connect',
|
||||
_('Connect to another client'),
|
||||
_('Connects the current client to another client using its id.'),
|
||||
_('Connect to P2P client _PARAM0_'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.addParameter('string', _('ID of the other client'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.connect');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'UseOwnBroker',
|
||||
_('Connect to a broker server'),
|
||||
_('Connects the extension to a broker server.'),
|
||||
_('Connect to the broker server at http://_PARAM0_:_PARAM1_/'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.addParameter('string', _('Host'), '', false)
|
||||
.addParameter('number', _('Port'), '', false)
|
||||
.addParameter('string', _('Path'), '', false)
|
||||
.addParameter('string', _('Key'), '', false)
|
||||
.addParameter('yesorno', _('SSl enabled?'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.useCustomBrokerServer');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'UseDefaultBroker',
|
||||
_('Connect to the default broker server'),
|
||||
_('Connects to the default broker server.'),
|
||||
_('Connect to the default broker server'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.useDefaultBrokerServer');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SendToAll',
|
||||
_('Trigger event on all connected clients'),
|
||||
_('Triggers an event on all connected clients'),
|
||||
_(
|
||||
'Trigger event _PARAM0_ on all connected clients (extra data: _PARAM1_)'
|
||||
),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.addParameter('string', _('Event name'), '', false)
|
||||
.addParameter('string', _('Extra data (optional)'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.sendDataToAll');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SendToOne',
|
||||
_('Trigger event on a specific client'),
|
||||
_('Triggers an event on a specific connected client'),
|
||||
_('Trigger event _PARAM1_ on client _PARAM0_ (extra data: _PARAM2_)'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.addParameter('string', _('ID of the other client'), '', false)
|
||||
.addParameter('string', _('Event name'), '', false)
|
||||
.addParameter('string', _('Extra data (optional)'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.sendDataTo');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SendToAllVariable',
|
||||
_('Trigger event on all connected clients (variable)'),
|
||||
_('Triggers an event on all connected clients'),
|
||||
_(
|
||||
'Trigger event _PARAM0_ on all connected clients (extra data: _PARAM1_)'
|
||||
),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.addParameter('string', _('Event name'), '', false)
|
||||
.addParameter(
|
||||
'scenevar',
|
||||
_('Variable containing the extra data'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.sendDataToAll');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'SendToOneVariable',
|
||||
_('Trigger event on a specific client (variable)'),
|
||||
_('Triggers an event on a specific connected client'),
|
||||
_('Trigger event _PARAM1_ on client _PARAM0_ (extra data: _PARAM2_)'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.addParameter('string', _('ID of the other client'), '', false)
|
||||
.addParameter('string', _('Event name'), '', false)
|
||||
.addParameter(
|
||||
'scenevar',
|
||||
_('Variable containing the extra data'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.sendVariableTo');
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'GetEventVariable',
|
||||
_('Get event data (variable)'),
|
||||
_(
|
||||
'Store the data of the specified event in a variable. ' +
|
||||
'Check in the conditions that the event was received using the "Event received" condition.'
|
||||
),
|
||||
_(
|
||||
'Overwrite _PARAM1_ with variable sent with last trigger of _PARAM0_'
|
||||
),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg',
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.addParameter('string', _('Event name'), '', false)
|
||||
.addParameter(
|
||||
'scenevar',
|
||||
_('Variable where to store the received data'),
|
||||
'',
|
||||
false
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.getEventVariable');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetEventData',
|
||||
_('Get event data'),
|
||||
_(
|
||||
'Returns the data received when the specified event was last triggered'
|
||||
),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.addParameter('string', _('Event name'), '', false)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.getEventData');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetID',
|
||||
_('Get client ID'),
|
||||
_('Gets the client ID of the current game instance'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.getCurrentId');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetLastError',
|
||||
_('Get last error'),
|
||||
_('Gets the description of the last P2P error'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.getLastError');
|
||||
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetLastDisconnectedPeer',
|
||||
_('Get last disconnected peer'),
|
||||
_('Gets the id of the latest peer that has disconnected.'),
|
||||
_('P2P (experimental)'),
|
||||
'JsPlatform/Extensions/p2picon.svg'
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
.setIncludeFile('Extensions/P2P/A_peer.js')
|
||||
.addIncludeFile('Extensions/P2P/B_p2ptools.js')
|
||||
.setFunctionName('gdjs.evtTools.p2p.getDisconnectedPeer');
|
||||
|
||||
return extension;
|
||||
},
|
||||
runExtensionSanityTests: function (
|
||||
gd /*: libGDevelop */,
|
||||
extension /*: gdPlatformExtension*/
|
||||
) {
|
||||
return [];
|
||||
},
|
||||
};
|
208
Extensions/P2P/peerjs.d.ts
vendored
Normal file
208
Extensions/P2P/peerjs.d.ts
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
// Type definitions for the PeerJS class module
|
||||
// Original definitions by Toshiya Nakakura <https://github.com/nakakura>
|
||||
// at https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
declare class Peer {
|
||||
prototype: RTCIceServer;
|
||||
|
||||
/**
|
||||
* A peer can connect to other peers and listen for connections.
|
||||
* @param id Other peers can connect to this peer using the provided ID.
|
||||
* If no ID is given, one will be generated by the brokering server.
|
||||
* @param options for specifying details about PeerServer
|
||||
*/
|
||||
constructor(id?: string, options?: Peer.PeerJSOption);
|
||||
|
||||
/**
|
||||
* A peer can connect to other peers and listen for connections.
|
||||
* @param options for specifying details about PeerServer
|
||||
*/
|
||||
constructor(options: Peer.PeerJSOption);
|
||||
|
||||
/**
|
||||
* Connects to the remote peer specified by id and returns a data connection.
|
||||
* @param id The brokering ID of the remote peer (their peer.id).
|
||||
* @param options for specifying details about Peer Connection
|
||||
*/
|
||||
connect(id: string, options?: Peer.PeerConnectOption): Peer.DataConnection;
|
||||
/**
|
||||
* Calls the remote peer specified by id and returns a media connection.
|
||||
* @param id The brokering ID of the remote peer (their peer.id).
|
||||
* @param stream The caller's media stream
|
||||
* @param options Metadata associated with the connection, passed in by whoever initiated the connection.
|
||||
*/
|
||||
call(id: string, stream: MediaStream, options?: Peer.CallOption): Peer.MediaConnection;
|
||||
/**
|
||||
* Set listeners for peer events.
|
||||
* @param event Event name
|
||||
* @param cb Callback Function
|
||||
*/
|
||||
on(event: string, cb: () => void): void;
|
||||
/**
|
||||
* Emitted when a connection to the PeerServer is established.
|
||||
* @param event Event name
|
||||
* @param cb id is the brokering ID of the peer
|
||||
*/
|
||||
on(event: "open", cb: (id: string) => void): void;
|
||||
/**
|
||||
* Emitted when a new data connection is established from a remote peer.
|
||||
* @param event Event name
|
||||
* @param cb Callback Function
|
||||
*/
|
||||
on(
|
||||
event: "connection",
|
||||
cb: (dataConnection: Peer.DataConnection) => void
|
||||
): void;
|
||||
/**
|
||||
* Emitted when a remote peer attempts to call you.
|
||||
* @param event Event name
|
||||
* @param cb Callback Function
|
||||
*/
|
||||
on(event: "call", cb: (mediaConnection: Peer.MediaConnection) => void): void;
|
||||
/**
|
||||
* Emitted when the peer is destroyed and can no longer accept or create any new connections.
|
||||
* @param event Event name
|
||||
* @param cb Callback Function
|
||||
*/
|
||||
on(event: "close", cb: () => void): void;
|
||||
/**
|
||||
* Emitted when the peer is disconnected from the signalling server
|
||||
* @param event Event name
|
||||
* @param cb Callback Function
|
||||
*/
|
||||
on(event: "disconnected", cb: () => void): void;
|
||||
/**
|
||||
* Errors on the peer are almost always fatal and will destroy the peer.
|
||||
* @param event Event name
|
||||
* @param cb Callback Function
|
||||
*/
|
||||
on(event: "error", cb: (err: any) => void): void;
|
||||
/**
|
||||
* Remove event listeners.(EventEmitter3)
|
||||
* @param {String} event The event we want to remove.
|
||||
* @param {Function} fn The listener that we need to find.
|
||||
* @param {Boolean} once Only remove once listeners.
|
||||
*/
|
||||
off(event: string, fn: Function, once?: boolean): void;
|
||||
/**
|
||||
* Close the connection to the server, leaving all existing data and media connections intact.
|
||||
*/
|
||||
disconnect(): void;
|
||||
/**
|
||||
* Attempt to reconnect to the server with the peer's old ID
|
||||
*/
|
||||
reconnect(): void;
|
||||
/**
|
||||
* Close the connection to the server and terminate all existing connections.
|
||||
*/
|
||||
destroy(): void;
|
||||
|
||||
/**
|
||||
* Retrieve a data/media connection for this peer.
|
||||
* @param peerId
|
||||
* @param connectionId
|
||||
*/
|
||||
getConnection(peerId: string, connectionId: string): Peer.MediaConnection | Peer.DataConnection | null;
|
||||
|
||||
/**
|
||||
* Get a list of available peer IDs
|
||||
* @param callback
|
||||
*/
|
||||
listAllPeers(callback: (peerIds: Array<string>) => void): void;
|
||||
/**
|
||||
* The brokering ID of this peer
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* A hash of all connections associated with this peer, keyed by the remote peer's ID.
|
||||
*/
|
||||
connections: any;
|
||||
/**
|
||||
* false if there is an active connection to the PeerServer.
|
||||
*/
|
||||
disconnected: boolean;
|
||||
/**
|
||||
* true if this peer and all of its connections can no longer be used.
|
||||
*/
|
||||
destroyed: boolean;
|
||||
}
|
||||
|
||||
declare namespace Peer {
|
||||
interface PeerJSOption {
|
||||
key?: string;
|
||||
host?: string;
|
||||
port?: number;
|
||||
path?: string;
|
||||
secure?: boolean;
|
||||
config?: RTCConfiguration;
|
||||
debug?: number;
|
||||
}
|
||||
|
||||
interface PeerConnectOption {
|
||||
label?: string;
|
||||
metadata?: any;
|
||||
serialization?: string;
|
||||
reliable?: boolean;
|
||||
}
|
||||
|
||||
interface CallOption {
|
||||
metadata?: any;
|
||||
sdpTransform?: Function;
|
||||
}
|
||||
|
||||
interface AnswerOption {
|
||||
sdpTransform?: Function;
|
||||
}
|
||||
|
||||
interface DataConnection {
|
||||
send(data: any): void;
|
||||
close(): void;
|
||||
on(event: string, cb: () => void): void;
|
||||
on(event: "data", cb: (data: any) => void): void;
|
||||
on(event: "open", cb: () => void): void;
|
||||
on(event: "close", cb: () => void): void;
|
||||
on(event: "error", cb: (err: any) => void): void;
|
||||
off(event: string, fn: Function, once?: boolean): void;
|
||||
dataChannel: RTCDataChannel;
|
||||
label: string;
|
||||
metadata: any;
|
||||
open: boolean;
|
||||
peerConnection: RTCPeerConnection;
|
||||
peer: string;
|
||||
reliable: boolean;
|
||||
serialization: string;
|
||||
type: string;
|
||||
bufferSize: number;
|
||||
stringify: (data: any) => string;
|
||||
parse: (data: string) => any;
|
||||
}
|
||||
|
||||
interface MediaConnection {
|
||||
answer(stream?: MediaStream, options?: AnswerOption): void;
|
||||
close(): void;
|
||||
on(event: string, cb: () => void): void;
|
||||
on(event: "stream", cb: (stream: MediaStream) => void): void;
|
||||
on(event: "close", cb: () => void): void;
|
||||
on(event: "error", cb: (err: any) => void): void;
|
||||
off(event: string, fn: Function, once?: boolean): void;
|
||||
open: boolean;
|
||||
metadata: any;
|
||||
peerConnection: RTCPeerConnection;
|
||||
peer: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface UtilSupportsObj {
|
||||
browser: boolean,
|
||||
webRTC: boolean;
|
||||
audioVideo: boolean;
|
||||
data: boolean;
|
||||
binaryBlob: boolean;
|
||||
reliable: boolean;
|
||||
}
|
||||
|
||||
interface util {
|
||||
browser: string;
|
||||
supports: UtilSupportsObj;
|
||||
}
|
||||
}
|
@@ -13,7 +13,7 @@
|
||||
* @property {number} width The object width
|
||||
* @property {number} height The object height
|
||||
* @property {string} texture The name of the resource containing the texture to use
|
||||
*
|
||||
*
|
||||
* @typedef {ObjectData & PanelSpriteObjectDataType} PanelSpriteObjectData
|
||||
*/
|
||||
|
||||
@@ -80,6 +80,49 @@ gdjs.PanelSpriteRuntimeObject.prototype = Object.create(
|
||||
);
|
||||
gdjs.registerObject("PanelSpriteObject::PanelSprite", gdjs.PanelSpriteRuntimeObject);
|
||||
|
||||
/**
|
||||
* @param {PanelSpriteObjectData} oldObjectData
|
||||
* @param {PanelSpriteObjectData} newObjectData
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
if (oldObjectData.width !== newObjectData.width) {
|
||||
this.setWidth(newObjectData.width);
|
||||
}
|
||||
if (oldObjectData.height !== newObjectData.height) {
|
||||
this.setHeight(newObjectData.height);
|
||||
}
|
||||
|
||||
var updateTexture = false;
|
||||
if (oldObjectData.rightMargin !== newObjectData.rightMargin) {
|
||||
this._rBorder = newObjectData.rightMargin;
|
||||
updateTexture = true;
|
||||
}
|
||||
if (oldObjectData.leftMargin !== newObjectData.leftMargin) {
|
||||
this._lBorder = newObjectData.leftMargin;
|
||||
updateTexture = true;
|
||||
}
|
||||
if (oldObjectData.topMargin !== newObjectData.topMargin) {
|
||||
this._tBorder = newObjectData.topMargin;
|
||||
updateTexture = true;
|
||||
}
|
||||
if (oldObjectData.bottomMargin !== newObjectData.bottomMargin) {
|
||||
this._bBorder = newObjectData.bottomMargin;
|
||||
updateTexture = true;
|
||||
}
|
||||
if (oldObjectData.texture !== newObjectData.texture) {
|
||||
updateTexture = true;
|
||||
}
|
||||
if (updateTexture) {
|
||||
this.setTexture(newObjectData.texture, this._runtimeScene);
|
||||
}
|
||||
|
||||
if (oldObjectData.tiled !== newObjectData.tiled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
@@ -215,7 +258,7 @@ gdjs.PanelSpriteRuntimeObject.prototype.setColor = function(rgbColor) {
|
||||
/**
|
||||
* Get the tint of the panel sprite object.
|
||||
*
|
||||
* @returns {string} rgbColor The color, in RGB format ("128;200;255").
|
||||
* @returns {string} The color, in RGB format ("128;200;255").
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.getColor = function() {
|
||||
return this._renderer.getColor();
|
||||
|
@@ -194,7 +194,7 @@ gdjs.ParticleEmitterObjectCocosRenderer = function(runtimeScene, runtimeObject,
|
||||
|
||||
this.started = false;
|
||||
|
||||
var renderer = runtimeScene.getLayer("").getRenderer();
|
||||
var renderer = runtimeScene.getLayer(runtimeObject.getLayer()).getRenderer();
|
||||
renderer.addRendererObject(this.renderer, runtimeObject.getZOrder());
|
||||
this._convertYPosition = renderer.convertYPosition;
|
||||
};
|
||||
|
@@ -152,7 +152,7 @@ gdjs.ParticleEmitterObjectPixiRenderer = function(runtimeScene, runtimeObject, o
|
||||
this.emitter.emit = true;
|
||||
this.started = false;
|
||||
|
||||
var layer = runtimeScene.getLayer("");
|
||||
var layer = runtimeScene.getLayer(runtimeObject.getLayer());
|
||||
if (layer) layer.getRenderer().addRendererObject(this.renderer, runtimeObject.getZOrder());
|
||||
};
|
||||
gdjs.ParticleEmitterObjectRenderer = gdjs.ParticleEmitterObjectPixiRenderer;
|
||||
|
@@ -24,17 +24,19 @@
|
||||
* @property {number} particleBlue2
|
||||
* @property {number} particleSize1
|
||||
* @property {number} particleSize2
|
||||
* @property {number} sizeParam
|
||||
* @property {number} particleAngle1
|
||||
* @property {number} particleAngle2
|
||||
* @property {string} sizeParam
|
||||
* @property {number} particleAlpha1
|
||||
* @property {number} particleAlpha2
|
||||
* @property {string} rendererType
|
||||
* @property {number} rendererParam1
|
||||
* @property {number} rendererParam1
|
||||
* @property {number} rendererParam2
|
||||
* @property {string} textureParticleName Resource name for image in particle
|
||||
* @property {number} flow
|
||||
* @property {number} tank
|
||||
* @property {boolean} destroyWhenNoParticles Destroy the object when there is no particles?
|
||||
*
|
||||
*
|
||||
* @typedef {ObjectData & ParticleEmitterObjectDataType} ParticleEmitterObjectData
|
||||
*/
|
||||
|
||||
@@ -107,7 +109,7 @@ gdjs.ParticleEmitterObject = function(runtimeScene, particleObjectData){
|
||||
/** @type {number} */
|
||||
this.size2 = particleObjectData.particleSize2;
|
||||
|
||||
/** @type {number} */
|
||||
/** @type {string} */
|
||||
this.sizeParam = particleObjectData.sizeParam;
|
||||
|
||||
/** @type {number} */
|
||||
@@ -121,7 +123,7 @@ gdjs.ParticleEmitterObject = function(runtimeScene, particleObjectData){
|
||||
|
||||
/** @type {number} */
|
||||
this.rendererParam1 = particleObjectData.rendererParam1;
|
||||
|
||||
|
||||
/** @type {number} */
|
||||
this.rendererParam2 = particleObjectData.rendererParam2;
|
||||
|
||||
@@ -185,6 +187,116 @@ gdjs.ParticleEmitterObject.prototype.getRendererObject = function(){
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ParticleEmitterObjectData} oldObjectData
|
||||
* @param {ParticleEmitterObjectData} newObjectData
|
||||
*/
|
||||
gdjs.ParticleEmitterObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
if (oldObjectData.emissionEditionSimpleMode !== newObjectData.emissionEditionSimpleMode) {
|
||||
this.singleAngle = newObjectData.emissionEditionSimpleMode;
|
||||
this._angleDirty = true;
|
||||
}
|
||||
if (oldObjectData.emitterAngleA !== newObjectData.emitterAngleA) {
|
||||
this.setEmitterAngleA(newObjectData.emitterAngleA);
|
||||
}
|
||||
if (oldObjectData.emitterAngleB !== newObjectData.emitterAngleB) {
|
||||
this.setEmitterAngleB(newObjectData.emitterAngleB);
|
||||
}
|
||||
if (oldObjectData.emitterForceMin !== newObjectData.emitterForceMin) {
|
||||
this.setEmitterForceMin(newObjectData.emitterForceMin);
|
||||
}
|
||||
if (oldObjectData.emitterForceMax !== newObjectData.emitterForceMax) {
|
||||
this.setEmitterForceMax(newObjectData.emitterForceMax);
|
||||
}
|
||||
if (oldObjectData.zoneRadius !== newObjectData.zoneRadius) {
|
||||
this.setZoneRadius(newObjectData.zoneRadius);
|
||||
}
|
||||
if (oldObjectData.particleLifeTimeMin !== newObjectData.particleLifeTimeMin) {
|
||||
this.setParticleLifeTimeMin(newObjectData.particleLifeTimeMin);
|
||||
}
|
||||
if (oldObjectData.particleLifeTimeMax !== newObjectData.particleLifeTimeMax) {
|
||||
this.setParticleLifeTimeMax(newObjectData.particleLifeTimeMax);
|
||||
}
|
||||
if (oldObjectData.particleGravityX !== newObjectData.particleGravityX) {
|
||||
this.setParticleGravityX(newObjectData.particleGravityX);
|
||||
}
|
||||
if (oldObjectData.particleGravityY !== newObjectData.particleGravityY) {
|
||||
this.setParticleGravityY(newObjectData.particleGravityY);
|
||||
}
|
||||
if (oldObjectData.particleRed1 !== newObjectData.particleRed1) {
|
||||
this.setParticleRed1(newObjectData.particleRed1);
|
||||
}
|
||||
if (oldObjectData.particleRed2 !== newObjectData.particleRed2) {
|
||||
this.setParticleRed2(newObjectData.particleRed2);
|
||||
}
|
||||
if (oldObjectData.particleGreen1 !== newObjectData.particleGreen1) {
|
||||
this.setParticleGreen1(newObjectData.particleGreen1);
|
||||
}
|
||||
if (oldObjectData.particleGreen2 !== newObjectData.particleGreen2) {
|
||||
this.setParticleGreen2(newObjectData.particleGreen2);
|
||||
}
|
||||
if (oldObjectData.particleBlue1 !== newObjectData.particleBlue1) {
|
||||
this.setParticleBlue1(newObjectData.particleBlue1);
|
||||
}
|
||||
if (oldObjectData.particleBlue2 !== newObjectData.particleBlue2) {
|
||||
this.setParticleBlue2(newObjectData.particleBlue2);
|
||||
}
|
||||
if (oldObjectData.particleSize1 !== newObjectData.particleSize1) {
|
||||
this.setParticleSize1(newObjectData.particleSize1);
|
||||
}
|
||||
if (oldObjectData.particleSize2 !== newObjectData.particleSize2) {
|
||||
this.setParticleSize2(newObjectData.particleSize2);
|
||||
}
|
||||
if (oldObjectData.sizeParam !== newObjectData.sizeParam) {
|
||||
this.sizeParam = newObjectData.sizeParam;
|
||||
this._sizeDirty = true;
|
||||
}
|
||||
if (oldObjectData.particleAlpha1 !== newObjectData.particleAlpha1) {
|
||||
this.setParticleAlpha1(newObjectData.particleAlpha1);
|
||||
}
|
||||
if (oldObjectData.particleAlpha2 !== newObjectData.particleAlpha2) {
|
||||
this.setParticleAlpha2(newObjectData.particleAlpha2);
|
||||
}
|
||||
if (oldObjectData.textureParticleName !== newObjectData.textureParticleName) {
|
||||
this.setTexture(newObjectData.textureParticleName, this._runtimeScene);
|
||||
}
|
||||
if (oldObjectData.flow !== newObjectData.flow) {
|
||||
this.setFlow(newObjectData.flow);
|
||||
}
|
||||
if (oldObjectData.tank !== newObjectData.tank) {
|
||||
this.setTank(newObjectData.tank);
|
||||
}
|
||||
if (oldObjectData.destroyWhenNoParticles !== newObjectData.destroyWhenNoParticles) {
|
||||
this.destroyWhenNoParticles = newObjectData.destroyWhenNoParticles;
|
||||
}
|
||||
|
||||
if (oldObjectData.particleSizeRandomness1 !== newObjectData.particleSizeRandomness1 ||
|
||||
oldObjectData.particleSizeRandomness2 !== newObjectData.particleSizeRandomness2 ||
|
||||
oldObjectData.particleAngle1 !== newObjectData.particleAngle1 ||
|
||||
oldObjectData.particleAngle2 !== newObjectData.particleAngle2 ||
|
||||
oldObjectData.maxParticleNb !== newObjectData.maxParticleNb ||
|
||||
oldObjectData.additive !== newObjectData.additive ||
|
||||
oldObjectData.rendererType !== newObjectData.rendererType ||
|
||||
oldObjectData.rendererParam1 !== newObjectData.rendererParam1 ||
|
||||
oldObjectData.rendererParam2 !== newObjectData.rendererParam2) {
|
||||
// Destroy the renderer, ensure it's removed from the layer.
|
||||
var layer = this._runtimeScene.getLayer(this.layer);
|
||||
layer.getRenderer().removeRendererObject(this._renderer.getRendererObject());
|
||||
this._renderer.destroy();
|
||||
|
||||
// and recreate the renderer, which will add itself to the layer.
|
||||
this._renderer = new gdjs.ParticleEmitterObjectRenderer(this._runtimeScene, this, newObjectData);
|
||||
|
||||
// Consider every state dirty as the renderer was just re-created, so it needs
|
||||
// to be repositioned, angle updated, etc...
|
||||
this._posDirty = this._angleDirty = this._forceDirty = this._zoneRadiusDirty = true;
|
||||
this._lifeTimeDirty = this._gravityDirty = this._colorDirty = this._sizeDirty = true;
|
||||
this._alphaDirty = this._flowDirty = this._textureDirty = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.ParticleEmitterObject.prototype.update = function(runtimeScene){
|
||||
if(this._posDirty){
|
||||
this._renderer.setPosition(this.getX(), this.getY());
|
||||
|
@@ -104,6 +104,17 @@ gdjs.PathfindingObstacleRuntimeBehavior = function(runtimeScene, behaviorData, o
|
||||
gdjs.PathfindingObstacleRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("PathfindingBehavior::PathfindingObstacleBehavior", gdjs.PathfindingObstacleRuntimeBehavior);
|
||||
|
||||
gdjs.PathfindingObstacleRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.impassable !== newBehaviorData.impassable) {
|
||||
this.setImpassable(newBehaviorData.impassable);
|
||||
}
|
||||
if (oldBehaviorData.cost !== newBehaviorData.cost) {
|
||||
this.setCost(newBehaviorData.cost);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.PathfindingObstacleRuntimeBehavior.prototype.onDestroy = function() {
|
||||
if ( this._manager && this._registeredInManager ) this._manager.removeObstacle(this);
|
||||
};
|
||||
|
@@ -48,6 +48,38 @@ gdjs.PathfindingRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
gdjs.PathfindingRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("PathfindingBehavior::PathfindingBehavior", gdjs.PathfindingRuntimeBehavior);
|
||||
|
||||
gdjs.PathfindingRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.allowDiagonals !== newBehaviorData.allowDiagonals) {
|
||||
this.allowDiagonals(newBehaviorData.allowDiagonals);
|
||||
}
|
||||
if (oldBehaviorData.acceleration !== newBehaviorData.acceleration) {
|
||||
this.setAcceleration(newBehaviorData.acceleration);
|
||||
}
|
||||
if (oldBehaviorData.maxSpeed !== newBehaviorData.maxSpeed) {
|
||||
this.setMaxSpeed(newBehaviorData.maxSpeed);
|
||||
}
|
||||
if (oldBehaviorData.angularMaxSpeed !== newBehaviorData.angularMaxSpeed) {
|
||||
this.setAngularMaxSpeed(newBehaviorData.angularMaxSpeed);
|
||||
}
|
||||
if (oldBehaviorData.rotateObject !== newBehaviorData.rotateObject) {
|
||||
this.setRotateObject(newBehaviorData.rotateObject);
|
||||
}
|
||||
if (oldBehaviorData.angleOffset !== newBehaviorData.angleOffset) {
|
||||
this.setAngleOffset(newBehaviorData.angleOffset);
|
||||
}
|
||||
if (oldBehaviorData.cellWidth !== newBehaviorData.cellWidth) {
|
||||
this.setCellWidth(newBehaviorData.cellWidth);
|
||||
}
|
||||
if (oldBehaviorData.cellHeight !== newBehaviorData.cellHeight) {
|
||||
this.setCellHeight(newBehaviorData.cellHeight);
|
||||
}
|
||||
if (oldBehaviorData.extraBorder !== newBehaviorData.extraBorder) {
|
||||
this.setExtraBorder(newBehaviorData.extraBorder);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.PathfindingRuntimeBehavior.prototype.setCellWidth = function(width) {
|
||||
this._cellWidth = width;
|
||||
};
|
||||
|
@@ -53,9 +53,9 @@ gdjs.Physics2SharedData = function(runtimeScene, sharedData) {
|
||||
var behaviorB = contact.GetFixtureB().GetBody().gdjsAssociatedBehavior;
|
||||
// Remove each other contact
|
||||
var i = behaviorA.currentContacts.indexOf(behaviorB);
|
||||
if (i !== -1) behaviorA.currentContacts.remove(i);
|
||||
if (i !== -1) behaviorA.currentContacts.splice(i, 1);
|
||||
i = behaviorB.currentContacts.indexOf(behaviorA);
|
||||
if (i !== -1) behaviorB.currentContacts.remove(i);
|
||||
if (i !== -1) behaviorB.currentContacts.splice(i, 1);
|
||||
};
|
||||
|
||||
this.contactListener.PreSolve = function() {};
|
||||
@@ -252,6 +252,75 @@ gdjs.Physics2RuntimeBehavior.prototype.b2Vec2Sec = function(x, y) {
|
||||
return this._tempb2Vec2Sec;
|
||||
};
|
||||
|
||||
gdjs.Physics2RuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.bullet !== newBehaviorData.bullet) {
|
||||
this.setBullet(newBehaviorData.bullet);
|
||||
}
|
||||
if (oldBehaviorData.fixedRotation !== newBehaviorData.fixedRotation) {
|
||||
this.setFixedRotation(newBehaviorData.fixedRotation);
|
||||
}
|
||||
if (oldBehaviorData.canSleep !== newBehaviorData.canSleep) {
|
||||
this.setSleepingAllowed(newBehaviorData.canSleep);
|
||||
}
|
||||
if (oldBehaviorData.shapeDimensionA !== newBehaviorData.shapeDimensionA) {
|
||||
this.shapeDimensionA = newBehaviorData.shapeDimensionA;
|
||||
this.recreateShape();
|
||||
}
|
||||
if (oldBehaviorData.shapeDimensionB !== newBehaviorData.shapeDimensionB) {
|
||||
this.shapeDimensionB = newBehaviorData.shapeDimensionB;
|
||||
this.recreateShape();
|
||||
}
|
||||
if (oldBehaviorData.shapeOffsetX !== newBehaviorData.shapeOffsetX) {
|
||||
this.shapeOffsetX = newBehaviorData.shapeOffsetX;
|
||||
this.recreateShape();
|
||||
}
|
||||
if (oldBehaviorData.shapeOffsetY !== newBehaviorData.shapeOffsetY) {
|
||||
this.shapeOffsetY = newBehaviorData.shapeOffsetY;
|
||||
this.recreateShape();
|
||||
}
|
||||
if (oldBehaviorData.polygonOrigin !== newBehaviorData.polygonOrigin) {
|
||||
this.polygonOrigin = newBehaviorData.polygonOrigin;
|
||||
this.recreateShape();
|
||||
}
|
||||
if (oldBehaviorData.density !== newBehaviorData.density) {
|
||||
this.setDensity(newBehaviorData.density);
|
||||
}
|
||||
if (oldBehaviorData.friction !== newBehaviorData.friction) {
|
||||
this.setFriction(newBehaviorData.friction);
|
||||
}
|
||||
if (oldBehaviorData.restitution !== newBehaviorData.restitution) {
|
||||
this.setRestitution(newBehaviorData.restitution);
|
||||
}
|
||||
if (oldBehaviorData.linearDamping !== newBehaviorData.linearDamping) {
|
||||
this.setLinearDamping(newBehaviorData.linearDamping);
|
||||
}
|
||||
if (oldBehaviorData.angularDamping !== newBehaviorData.angularDamping) {
|
||||
this.setAngularDamping(newBehaviorData.angularDamping);
|
||||
}
|
||||
if (oldBehaviorData.gravityScale !== newBehaviorData.gravityScale) {
|
||||
this.setGravityScale(newBehaviorData.gravityScale);
|
||||
}
|
||||
|
||||
// TODO: make these properties updatable.
|
||||
if (oldBehaviorData.layers !== newBehaviorData.layers) {
|
||||
return false;
|
||||
}
|
||||
if (oldBehaviorData.masks !== newBehaviorData.masks) {
|
||||
return false;
|
||||
}
|
||||
if (oldBehaviorData.vertices !== newBehaviorData.vertices) {
|
||||
return false;
|
||||
}
|
||||
if (oldBehaviorData.bodyType !== newBehaviorData.bodyType) {
|
||||
return false;
|
||||
}
|
||||
if (oldBehaviorData.shape !== newBehaviorData.shape) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.Physics2RuntimeBehavior.prototype.onDeActivate = function() {
|
||||
if (this._body !== null) {
|
||||
// When a body is deleted, Box2D removes automatically its joints, leaving an invalid pointer in our joints list
|
||||
@@ -573,10 +642,21 @@ gdjs.Physics2RuntimeBehavior.prototype.doStepPreEvents = function(
|
||||
gdjs.Physics2RuntimeBehavior.prototype.doStepPostEvents = function(
|
||||
runtimeScene
|
||||
) {
|
||||
this._updateBodyFromObject();
|
||||
|
||||
// Reset world step to update next frame
|
||||
this._sharedData.stepped = false;
|
||||
};
|
||||
|
||||
gdjs.Physics2RuntimeBehavior.prototype.onObjectHotReloaded = function() {
|
||||
this._updateBodyFromObject();
|
||||
}
|
||||
|
||||
gdjs.Physics2RuntimeBehavior.prototype._updateBodyFromObject = function() {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) this.createBody();
|
||||
|
||||
// GD object size has changed, recreate shape
|
||||
// The object size has changed, recreate the shape.
|
||||
// The width has changed and there is no custom dimension A (box: width, circle: radius, edge: length) or
|
||||
// The height has changed, the shape is not an edge (edges doesn't have height),
|
||||
// it isn't a box with custom height or a circle with custom radius
|
||||
@@ -591,7 +671,7 @@ gdjs.Physics2RuntimeBehavior.prototype.doStepPostEvents = function(
|
||||
this.recreateShape();
|
||||
}
|
||||
|
||||
// GD object transform has changed, update body transform
|
||||
// The object object transform has changed, update body transform:
|
||||
if (
|
||||
this._objectOldX !== this.owner.getX() ||
|
||||
this._objectOldY !== this.owner.getY() ||
|
||||
@@ -606,10 +686,7 @@ gdjs.Physics2RuntimeBehavior.prototype.doStepPostEvents = function(
|
||||
this._body.SetTransform(pos, gdjs.toRad(this.owner.getAngle()));
|
||||
this._body.SetAwake(true);
|
||||
}
|
||||
|
||||
// Reset world step to update next frame
|
||||
this._sharedData.stepped = false;
|
||||
};
|
||||
}
|
||||
|
||||
gdjs.Physics2RuntimeBehavior.prototype.getGravityX = function() {
|
||||
return this._sharedData.gravityX;
|
||||
|
@@ -58,10 +58,10 @@ gdjs.PhysicsSharedData = function(runtimeScene, sharedData)
|
||||
behaviorB = contact.GetFixtureB().GetBody().gdjsAssociatedBehavior;
|
||||
|
||||
var i = behaviorA.currentContacts.indexOf(behaviorB);
|
||||
if ( i !== -1 ) behaviorA.currentContacts.remove(i);
|
||||
if ( i !== -1 ) behaviorA.currentContacts.splice(i, 1);
|
||||
|
||||
i = behaviorB.currentContacts.indexOf(behaviorA);
|
||||
if ( i !== -1 ) behaviorB.currentContacts.remove(i);
|
||||
if ( i !== -1 ) behaviorB.currentContacts.splice(i, 1);
|
||||
};
|
||||
|
||||
this.contactListener.PreSolve = function() {};
|
||||
@@ -147,6 +147,43 @@ gdjs.PhysicsRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
gdjs.PhysicsRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("PhysicsBehavior::PhysicsBehavior", gdjs.PhysicsRuntimeBehavior);
|
||||
|
||||
gdjs.PhysicsRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.dynamic !== newBehaviorData.dynamic) {
|
||||
if (newBehaviorData.dynamic) this.setDynamic();
|
||||
else this.setStatic();
|
||||
}
|
||||
if (oldBehaviorData.angularDamping !== newBehaviorData.angularDamping) {
|
||||
this.setAngularDamping(newBehaviorData.angularDamping);
|
||||
}
|
||||
if (oldBehaviorData.linearDamping !== newBehaviorData.linearDamping) {
|
||||
this.setLinearDamping(newBehaviorData.linearDamping);
|
||||
}
|
||||
if (oldBehaviorData.isBullet !== newBehaviorData.isBullet) {
|
||||
if (newBehaviorData.isBullet) this.setAsBullet();
|
||||
else this.dontSetAsBullet();
|
||||
}
|
||||
if (oldBehaviorData.fixedRotation !== newBehaviorData.fixedRotation) {
|
||||
if (newBehaviorData.fixedRotation) this.setFixedRotation();
|
||||
else this.setFreeRotation();
|
||||
}
|
||||
|
||||
// TODO: make these properties updatable.
|
||||
if (oldBehaviorData.massDensity !== newBehaviorData.massDensity) {
|
||||
return false;
|
||||
}
|
||||
if (oldBehaviorData.averageFriction !== newBehaviorData.averageFriction) {
|
||||
return false;
|
||||
}
|
||||
if (oldBehaviorData.averageRestitution !== newBehaviorData.averageRestitution) {
|
||||
return false;
|
||||
}
|
||||
if (oldBehaviorData.shapeType !== newBehaviorData.shapeType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.PhysicsRuntimeBehavior.prototype.onDeActivate = function() {
|
||||
if ( this._box2DBody !== null ) {
|
||||
this._sharedData.world.DestroyBody(this._box2DBody);
|
||||
|
@@ -77,6 +77,44 @@ gdjs.registerBehavior(
|
||||
gdjs.PlatformerObjectRuntimeBehavior
|
||||
);
|
||||
|
||||
gdjs.PlatformerObjectRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.roundCoordinates !== newBehaviorData.roundCoordinates) {
|
||||
this._roundCoordinates = newBehaviorData.roundCoordinates;
|
||||
}
|
||||
if (oldBehaviorData.gravity !== newBehaviorData.gravity) {
|
||||
this.setGravity(newBehaviorData.gravity);
|
||||
}
|
||||
if (oldBehaviorData.maxFallingSpeed !== newBehaviorData.maxFallingSpeed) {
|
||||
this.setMaxFallingSpeed(newBehaviorData.maxFallingSpeed);
|
||||
}
|
||||
if (oldBehaviorData.acceleration !== newBehaviorData.acceleration) {
|
||||
this.setAcceleration(newBehaviorData.acceleration);
|
||||
}
|
||||
if (oldBehaviorData.deceleration !== newBehaviorData.deceleration) {
|
||||
this.setDeceleration(newBehaviorData.deceleration);
|
||||
}
|
||||
if (oldBehaviorData.maxSpeed !== newBehaviorData.maxSpeed) {
|
||||
this.setMaxSpeed(newBehaviorData.maxSpeed);
|
||||
}
|
||||
if (oldBehaviorData.jumpSpeed !== newBehaviorData.jumpSpeed) {
|
||||
this.setJumpSpeed(newBehaviorData.jumpSpeed);
|
||||
}
|
||||
if (oldBehaviorData.canGrabPlatforms !== newBehaviorData.canGrabPlatforms) {
|
||||
this.setCanGrabPlatforms(newBehaviorData.canGrabPlatforms);
|
||||
}
|
||||
if (oldBehaviorData.yGrabOffset !== newBehaviorData.yGrabOffset) {
|
||||
this._yGrabOffset = newBehaviorData.yGrabOffset;
|
||||
}
|
||||
if (oldBehaviorData.xGrabTolerance !== newBehaviorData.xGrabTolerance) {
|
||||
this._xGrabTolerance = newBehaviorData.xGrabTolerance;
|
||||
}
|
||||
if (oldBehaviorData.jumpSustainTime !== newBehaviorData.jumpSustainTime) {
|
||||
this.setJumpSustainTime(newBehaviorData.jumpSustainTime);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.PlatformerObjectRuntimeBehavior.prototype.doStepPreEvents = function(
|
||||
runtimeScene
|
||||
) {
|
||||
|
@@ -106,6 +106,20 @@ gdjs.PlatformRuntimeBehavior.LADDER = 2;
|
||||
gdjs.PlatformRuntimeBehavior.JUMPTHRU = 1;
|
||||
gdjs.PlatformRuntimeBehavior.NORMALPLAFTORM = 0;
|
||||
|
||||
gdjs.PlatformRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.platformType !== newBehaviorData.platformType) {
|
||||
this.changePlatformType(newBehaviorData.platformType);
|
||||
}
|
||||
if (oldBehaviorData.canBeGrabbed !== newBehaviorData.canBeGrabbed) {
|
||||
this._canBeGrabbed = newBehaviorData.canBeGrabbed;
|
||||
}
|
||||
if (oldBehaviorData.yGrabOffset !== newBehaviorData.yGrabOffset) {
|
||||
this._yGrabOffset = newBehaviorData.yGrabOffset;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.PlatformRuntimeBehavior.prototype.onDestroy = function() {
|
||||
if ( this._manager && this._registeredInManager ) this._manager.removePlatform(this);
|
||||
};
|
||||
|
@@ -8,7 +8,7 @@ const makeTestRuntimeScene = () => {
|
||||
});
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers: [{ name: '', visibility: true }],
|
||||
layers: [{ name: '', visibility: true, effects: [] }],
|
||||
variables: [],
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
|
@@ -19,7 +19,7 @@
|
||||
* @property {number} outlineSize The size of the outline of the painted shape, in pixels.
|
||||
* @property {boolean} absoluteCoordinates Use absolute coordinates?
|
||||
* @property {boolean} clearBetweenFrames Clear the previous render before the next draw?
|
||||
*
|
||||
*
|
||||
* @typedef {ObjectData & ShapePainterObjectDataType} ShapePainterObjectData
|
||||
*/
|
||||
|
||||
@@ -74,6 +74,50 @@ gdjs.ShapePainterRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ShapePainterObjectData} oldObjectData
|
||||
* @param {ShapePainterObjectData} newObjectData
|
||||
*/
|
||||
gdjs.ShapePainterRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
if (oldObjectData.fillColor.r !== newObjectData.fillColor.r ||
|
||||
oldObjectData.fillColor.g !== newObjectData.fillColor.g ||
|
||||
oldObjectData.fillColor.b !== newObjectData.fillColor.b) {
|
||||
this.setFillColor(
|
||||
'' + newObjectData.fillColor.r + ';' +
|
||||
newObjectData.fillColor.g + ';' +
|
||||
newObjectData.fillColor.b
|
||||
);
|
||||
}
|
||||
if (oldObjectData.outlineColor.r !== newObjectData.outlineColor.r ||
|
||||
oldObjectData.outlineColor.g !== newObjectData.outlineColor.g ||
|
||||
oldObjectData.outlineColor.b !== newObjectData.outlineColor.b) {
|
||||
this.setOutlineColor(
|
||||
'' + newObjectData.outlineColor.r + ';' +
|
||||
newObjectData.outlineColor.g + ';' +
|
||||
newObjectData.outlineColor.b
|
||||
);
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (oldObjectData.absoluteCoordinates !== newObjectData.absoluteCoordinates) {
|
||||
this._absoluteCoordinates = newObjectData.absoluteCoordinates;
|
||||
this._renderer.updateXPosition();
|
||||
this._renderer.updateYPosition();
|
||||
}
|
||||
if (oldObjectData.clearBetweenFrames !== newObjectData.clearBetweenFrames) {
|
||||
this._clearBetweenFrames = newObjectData.clearBetweenFrames
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.stepBehaviorsPreEvents = function(runtimeScene) {
|
||||
//We redefine stepBehaviorsPreEvents just to clear the graphics before running events.
|
||||
if(this._clearBetweenFrames){
|
||||
@@ -120,11 +164,11 @@ gdjs.ShapePainterRuntimeObject.prototype.drawArc = function(centerX, centerY, ra
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawBezierCurve = function(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2) {
|
||||
this._renderer.drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2);
|
||||
this._renderer.drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawQuadraticCurve = function(x1, y1, cpX, cpY, x2, y2) {
|
||||
this._renderer.drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2);
|
||||
this._renderer.drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.beginFillPath = function(x1, y1) {
|
||||
@@ -145,7 +189,7 @@ gdjs.ShapePainterRuntimeObject.prototype.drawPathLineTo = function(x1, y1) {
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawPathBezierCurveTo = function(cpX, cpY, cpX2, cpY2, toX, toY) {
|
||||
this._renderer.drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
|
||||
this._renderer.drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawPathArc = function(cx, cy, radius, startAngle, endAngle, anticlockwise) {
|
||||
@@ -153,11 +197,11 @@ gdjs.ShapePainterRuntimeObject.prototype.drawPathArc = function(cx, cy, radius,
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.drawPathQuadraticCurveTo = function(cpX, cpY, toX, toY) {
|
||||
this._renderer.drawPathQuadraticCurveTo(cpX, cpY, toX, toY);
|
||||
this._renderer.drawPathQuadraticCurveTo(cpX, cpY, toX, toY);
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.closePath = function() {
|
||||
this._renderer.closePath();
|
||||
this._renderer.closePath();
|
||||
};
|
||||
|
||||
gdjs.ShapePainterRuntimeObject.prototype.setClearBetweenFrames = function(value) {
|
||||
|
@@ -35,6 +35,11 @@ gdjs.TextEntryRuntimeObject = function(runtimeScene, textEntryObjectData)
|
||||
gdjs.TextEntryRuntimeObject.prototype = Object.create( gdjs.RuntimeObject.prototype );
|
||||
gdjs.registerObject("TextEntryObject::TextEntry", gdjs.TextEntryRuntimeObject);
|
||||
|
||||
gdjs.TextEntryRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
// Nothing to update.
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.TextEntryRuntimeObject.prototype.onDestroyFromScene = function(runtimeScene) {
|
||||
gdjs.RuntimeObject.prototype.onDestroyFromScene.call(this, runtimeScene);
|
||||
|
||||
|
@@ -117,6 +117,41 @@ gdjs.TextRuntimeObject = function(runtimeScene, textObjectData)
|
||||
gdjs.TextRuntimeObject.prototype = Object.create( gdjs.RuntimeObject.prototype );
|
||||
gdjs.registerObject("TextObject::Text", gdjs.TextRuntimeObject);
|
||||
|
||||
/**
|
||||
* @param {TextObjectData} oldObjectData
|
||||
* @param {TextObjectData} newObjectData
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
if (oldObjectData.characterSize !== newObjectData.characterSize) {
|
||||
this.setCharacterSize(newObjectData.characterSize);
|
||||
}
|
||||
if (oldObjectData.font !== newObjectData.font) {
|
||||
this.setFontName(newObjectData.font);
|
||||
}
|
||||
if (oldObjectData.bold !== newObjectData.bold) {
|
||||
this.setBold(newObjectData.bold);
|
||||
}
|
||||
if (oldObjectData.italic !== newObjectData.italic) {
|
||||
this.setItalic(newObjectData.italic);
|
||||
}
|
||||
if (oldObjectData.color.r !== newObjectData.color.r ||
|
||||
oldObjectData.color.g !== newObjectData.color.g ||
|
||||
oldObjectData.color.b !== newObjectData.color.b) {
|
||||
this.setColor('' + newObjectData.color.r + ';' + newObjectData.color.g +
|
||||
';' + newObjectData.color.b);
|
||||
}
|
||||
if (oldObjectData.string !== newObjectData.string) {
|
||||
this.setString(newObjectData.string);
|
||||
}
|
||||
|
||||
if (oldObjectData.underlined !== newObjectData.underlined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
gdjs.TextRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
@@ -133,6 +168,8 @@ gdjs.TextRuntimeObject.prototype.extraInitializationFromInitialInstance = functi
|
||||
if ( initialInstanceData.customSize ) {
|
||||
this.setWrapping(true);
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
} else {
|
||||
this.setWrapping(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -224,6 +261,15 @@ gdjs.TextRuntimeObject.prototype.setCharacterSize = function(newSize) {
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the name of the resource to use for the font.
|
||||
* @param {string} fontResourceName The name of the font resource.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setFontName = function(fontResourceName) {
|
||||
this._fontName = fontResourceName;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the text is bold.
|
||||
*/
|
||||
@@ -338,7 +384,7 @@ gdjs.TextRuntimeObject.prototype.setColor = function(str) {
|
||||
|
||||
/**
|
||||
* Get the text color.
|
||||
* @return {String} The color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @return {string} The color as a "R;G;B" string, for example: "255;0;0"
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getColor = function(str) {
|
||||
return this._color[0] + ";" + this._color[1] + ";" + this._color[2];
|
||||
|
@@ -43,6 +43,20 @@ gdjs.TiledSpriteRuntimeObject = function(runtimeScene, tiledSpriteObjectData)
|
||||
gdjs.TiledSpriteRuntimeObject.prototype = Object.create( gdjs.RuntimeObject.prototype );
|
||||
gdjs.registerObject("TiledSpriteObject::TiledSprite", gdjs.TiledSpriteRuntimeObject);
|
||||
|
||||
gdjs.TiledSpriteRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
if (oldObjectData.texture !== newObjectData.texture) {
|
||||
this.setTexture(newObjectData.texture, this._runtimeScene);
|
||||
}
|
||||
if (oldObjectData.width !== newObjectData.width) {
|
||||
this.setWidth(newObjectData.width);
|
||||
}
|
||||
if (oldObjectData.height !== newObjectData.height) {
|
||||
this.setHeight(newObjectData.height);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.TiledSpriteRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
@@ -199,7 +213,7 @@ gdjs.TiledSpriteRuntimeObject.prototype.setColor = function(rgbColor) {
|
||||
/**
|
||||
* Get the tint of the tiled sprite object.
|
||||
*
|
||||
* @returns {string} rgbColor The color, in RGB format ("128;200;255").
|
||||
* @returns {string} The color, in RGB format ("128;200;255").
|
||||
*/
|
||||
gdjs.TiledSpriteRuntimeObject.prototype.getColor = function() {
|
||||
return this._renderer.getColor();
|
||||
|
@@ -46,6 +46,34 @@ gdjs.registerBehavior(
|
||||
gdjs.TopDownMovementRuntimeBehavior
|
||||
);
|
||||
|
||||
gdjs.TopDownMovementRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
if (oldBehaviorData.allowDiagonals !== newBehaviorData.allowDiagonals) {
|
||||
this._allowDiagonals = newBehaviorData.allowDiagonals;
|
||||
}
|
||||
if (oldBehaviorData.acceleration !== newBehaviorData.acceleration) {
|
||||
this._acceleration = newBehaviorData.acceleration;
|
||||
}
|
||||
if (oldBehaviorData.deceleration !== newBehaviorData.deceleration) {
|
||||
this._deceleration = newBehaviorData.deceleration;
|
||||
}
|
||||
if (oldBehaviorData.maxSpeed !== newBehaviorData.maxSpeed) {
|
||||
this._maxSpeed = newBehaviorData.maxSpeed;
|
||||
}
|
||||
if (oldBehaviorData.angularMaxSpeed !== newBehaviorData.angularMaxSpeed) {
|
||||
this._angularMaxSpeed = newBehaviorData.angularMaxSpeed;
|
||||
}
|
||||
if (oldBehaviorData.rotateObject !== newBehaviorData.rotateObject) {
|
||||
this._rotateObject = newBehaviorData.rotateObject;
|
||||
}
|
||||
if (oldBehaviorData.angleOffset !== newBehaviorData.angleOffset) {
|
||||
this._angleOffset = newBehaviorData.angleOffset;
|
||||
}
|
||||
if (oldBehaviorData.ignoreDefaultControls !== newBehaviorData.ignoreDefaultControls) {
|
||||
this._ignoreDefaultControls = newBehaviorData.ignoreDefaultControls;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
gdjs.TopDownMovementRuntimeBehavior.prototype.setAcceleration = function(
|
||||
acceleration
|
||||
) {
|
||||
|
@@ -66,8 +66,8 @@ module.exports = {
|
||||
tweenBehavior,
|
||||
new gd.BehaviorsSharedData()
|
||||
)
|
||||
.setIncludeFile("Extensions/TweenBehavior/tweenruntimebehavior.js")
|
||||
.addIncludeFile("Extensions/TweenBehavior/shifty.js");
|
||||
.setIncludeFile("Extensions/TweenBehavior/shifty.js")
|
||||
.addIncludeFile("Extensions/TweenBehavior/tweenruntimebehavior.js");
|
||||
|
||||
const easingChoices = JSON.stringify([
|
||||
"linear",
|
||||
|
@@ -58,6 +58,11 @@ gdjs.TweenRuntimeBehavior.easings = [
|
||||
'easeTo',
|
||||
];
|
||||
|
||||
gdjs.TweenRuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
// Nothing to update.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tween being played in a behavior.
|
||||
* @param {shifty.Tweenable} instance The Shifty tween that is played
|
||||
|
@@ -56,6 +56,28 @@ gdjs.VideoRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {VideoObjectData} oldObjectData
|
||||
* @param {VideoObjectData} newObjectData
|
||||
*/
|
||||
gdjs.VideoRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
if (oldObjectData.content.opacity !== newObjectData.content.opacity) {
|
||||
this.setOpacity(newObjectData.content.opacity);
|
||||
}
|
||||
if (oldObjectData.content.loop !== newObjectData.content.loop) {
|
||||
this.setLoop(newObjectData.content.loop);
|
||||
}
|
||||
if (oldObjectData.content.volume !== newObjectData.content.volume) {
|
||||
this.setVolume(newObjectData.content.volume);
|
||||
}
|
||||
|
||||
if (oldObjectData.content.videoResource !== newObjectData.content.videoResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @private
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include "GDCore/Events/Builtin/CommentEvent.h"
|
||||
@@ -215,7 +216,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
[](gd::Instruction& instruction,
|
||||
gd::EventsCodeGenerator& codeGenerator,
|
||||
gd::EventsCodeGenerationContext& parentContext) {
|
||||
size_t uniqueId = (size_t)&instruction;
|
||||
size_t uniqueId = codeGenerator.GenerateSingleUsageUniqueIdFor(
|
||||
instruction.GetOriginalInstruction().lock().get());
|
||||
return "conditionTrue = runtimeContext->TriggerOnce(" +
|
||||
gd::String::From(uniqueId) + ");\n";
|
||||
});
|
||||
|
@@ -96,6 +96,17 @@ gd::String BehaviorCodeGenerator::GenerateRuntimeBehaviorCompleteCode(
|
||||
}
|
||||
|
||||
return runtimeBehaviorMethodsCode;
|
||||
},
|
||||
[&]() {
|
||||
gd::String updateFromBehaviorCode;
|
||||
for (auto& property :
|
||||
eventsBasedBehavior.GetPropertyDescriptors().GetInternalVector()) {
|
||||
updateFromBehaviorCode +=
|
||||
GenerateUpdatePropertyFromBehaviorDataCode(
|
||||
eventsBasedBehavior, codeNamespace, *property);
|
||||
}
|
||||
|
||||
return updateFromBehaviorCode;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,7 +116,8 @@ gd::String BehaviorCodeGenerator::GenerateRuntimeBehaviorTemplateCode(
|
||||
const gd::String& codeNamespace,
|
||||
std::function<gd::String()> generateInitializePropertiesCode,
|
||||
std::function<gd::String()> generateMethodsCode,
|
||||
std::function<gd::String()> generatePropertiesCode) {
|
||||
std::function<gd::String()> generatePropertiesCode,
|
||||
std::function<gd::String()> generateUpdateFromBehaviorDataCode) {
|
||||
return gd::String(R"jscode_template(
|
||||
CODE_NAMESPACE = CODE_NAMESPACE || {};
|
||||
|
||||
@@ -128,6 +140,13 @@ CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME = function(runtimeScene, behaviorData,
|
||||
CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("EXTENSION_NAME::BEHAVIOR_NAME", CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME);
|
||||
|
||||
// Hot-reload:
|
||||
CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
UPDATE_FROM_BEHAVIOR_DATA_CODE
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Properties:
|
||||
PROPERTIES_CODE
|
||||
|
||||
@@ -142,6 +161,7 @@ METHODS_CODE
|
||||
.FindAndReplace("CODE_NAMESPACE", codeNamespace)
|
||||
.FindAndReplace("INITIALIZE_PROPERTIES_CODE",
|
||||
generateInitializePropertiesCode())
|
||||
.FindAndReplace("UPDATE_FROM_BEHAVIOR_DATA_CODE", generateUpdateFromBehaviorDataCode())
|
||||
.FindAndReplace("PROPERTIES_CODE", generatePropertiesCode())
|
||||
.FindAndReplace("METHODS_CODE", generateMethodsCode());
|
||||
;
|
||||
@@ -183,7 +203,16 @@ CODE_NAMESPACE.RUNTIME_BEHAVIOR_CLASSNAME.prototype.SETTER_NAME = function(newVa
|
||||
.FindAndReplace("RUNTIME_BEHAVIOR_CLASSNAME",
|
||||
eventsBasedBehavior.GetName())
|
||||
.FindAndReplace("CODE_NAMESPACE", codeNamespace);
|
||||
;
|
||||
}
|
||||
|
||||
gd::String BehaviorCodeGenerator::GenerateUpdatePropertyFromBehaviorDataCode(
|
||||
const gd::EventsBasedBehavior& eventsBasedBehavior,
|
||||
const gd::String& codeNamespace,
|
||||
const gd::NamedPropertyDescriptor& property) {
|
||||
return gd::String(R"jscode_template(
|
||||
if (oldBehaviorData.PROPERTY_NAME !== newBehaviorData.PROPERTY_NAME)
|
||||
this._behaviorData.PROPERTY_NAME = newBehaviorData.PROPERTY_NAME;)jscode_template")
|
||||
.FindAndReplace("PROPERTY_NAME", property.GetName());
|
||||
}
|
||||
|
||||
gd::String BehaviorCodeGenerator::GeneratePropertyValueCode(
|
||||
|
@@ -64,7 +64,8 @@ class BehaviorCodeGenerator {
|
||||
const gd::String& codeNamespace,
|
||||
std::function<gd::String()> generateInitializePropertiesCode,
|
||||
std::function<gd::String()> generateMethodsCode,
|
||||
std::function<gd::String()> generatePropertiesCode);
|
||||
std::function<gd::String()> generatePropertiesCode,
|
||||
std::function<gd::String()> generateUpdateFromBehaviorDataCode);
|
||||
gd::String GenerateRuntimeBehaviorPropertyTemplateCode(
|
||||
const gd::EventsBasedBehavior& eventsBasedBehavior,
|
||||
const gd::String& codeNamespace,
|
||||
@@ -74,6 +75,10 @@ class BehaviorCodeGenerator {
|
||||
gd::String GenerateInitializePropertyFromDefaultValueCode(
|
||||
const gd::NamedPropertyDescriptor& property);
|
||||
gd::String GeneratePropertyValueCode(const gd::PropertyDescriptor& property);
|
||||
gd::String GenerateUpdatePropertyFromBehaviorDataCode(
|
||||
const gd::EventsBasedBehavior& eventsBasedBehavior,
|
||||
const gd::String& codeNamespace,
|
||||
const gd::NamedPropertyDescriptor& property);
|
||||
gd::String GenerateBehaviorOnDestroyToDeprecatedOnOwnerRemovedFromScene(
|
||||
const gd::EventsBasedBehavior& eventsBasedBehavior,
|
||||
const gd::String& codeNamespace);
|
||||
|
@@ -63,8 +63,6 @@ gd::String EventsCodeGenerator::GenerateEventsListCompleteFunctionCode(
|
||||
gd::String globalObjectLists = allObjectsDeclarationsAndResets.first;
|
||||
gd::String globalObjectListsReset = allObjectsDeclarationsAndResets.second;
|
||||
|
||||
codeGenerator.AddAllObjectsIncludeFiles();
|
||||
|
||||
// "Booleans" used by conditions
|
||||
gd::String globalConditionsBooleans =
|
||||
codeGenerator.GenerateAllConditionsBooleanDeclarations();
|
||||
@@ -126,8 +124,7 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
|
||||
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
eventsFunction.GetParameters(), false),
|
||||
codeGenerator.GenerateEventsFunctionContext(
|
||||
eventsFunction.GetParameters(),
|
||||
"runtimeScene.getOnceTriggers()"),
|
||||
eventsFunction.GetParameters(), "runtimeScene.getOnceTriggers()"),
|
||||
eventsFunction.GetEvents(),
|
||||
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
|
||||
|
||||
@@ -156,8 +153,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
|
||||
|
||||
// Generate the code setting up the context of the function.
|
||||
gd::String fullPreludeCode =
|
||||
preludeCode + "\n" +
|
||||
"var that = this;\n" +
|
||||
preludeCode + "\n" + "var that = this;\n" +
|
||||
// runtimeScene is supposed to be always accessible, read
|
||||
// it from the behavior
|
||||
"var runtimeScene = this._runtimeScene;\n" +
|
||||
@@ -175,7 +171,8 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
|
||||
onceTriggersVariable,
|
||||
// Pass the names of the parameters considered as the current
|
||||
// object and behavior parameters:
|
||||
"Object", "Behavior");
|
||||
"Object",
|
||||
"Behavior");
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
project,
|
||||
@@ -327,9 +324,9 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
|
||||
" createObject: function(objectName) {\n"
|
||||
" var objectsList = "
|
||||
"eventsFunctionContext._objectsMap[objectName];\n" +
|
||||
// TODO: we could speed this up by storing a map of object names, but the
|
||||
// cost of creating/storing it for each events function might not be
|
||||
// worth it.
|
||||
// TODO: we could speed this up by storing a map of object names, but
|
||||
// the cost of creating/storing it for each events function might not
|
||||
// be worth it.
|
||||
" if (objectsList) {\n" +
|
||||
" return parentEventsFunctionContext ?\n" +
|
||||
" "
|
||||
@@ -342,12 +339,11 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
|
||||
" },\n"
|
||||
// Getter for arguments that are not objects
|
||||
" getArgument: function(argName) {\n" +
|
||||
argumentsGetters + " return \"\";\n" +
|
||||
" },\n" +
|
||||
// Expose OnceTriggers (will be pointing either to the runtime scene ones,
|
||||
// or the ones from the behavior):
|
||||
" getOnceTriggers: function() { return " + onceTriggersVariable + "; }\n" +
|
||||
"};\n";
|
||||
argumentsGetters + " return \"\";\n" + " },\n" +
|
||||
// Expose OnceTriggers (will be pointing either to the runtime scene
|
||||
// ones, or the ones from the behavior):
|
||||
" getOnceTriggers: function() { return " + onceTriggersVariable +
|
||||
"; }\n" + "};\n";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateEventsFunctionReturn(
|
||||
@@ -396,33 +392,6 @@ EventsCodeGenerator::GenerateAllObjectsDeclarationsAndResets(
|
||||
return std::make_pair(globalObjectLists, globalObjectListsReset);
|
||||
}
|
||||
|
||||
void EventsCodeGenerator::AddAllObjectsIncludeFiles() {
|
||||
auto addIncludeFiles = [this](const gd::Object& object) {
|
||||
gd::String type = gd::GetTypeOfObject(
|
||||
GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), object.GetName());
|
||||
|
||||
// Ensure needed files are included for the object type and its behaviors.
|
||||
const gd::ObjectMetadata& metadata =
|
||||
gd::MetadataProvider::GetObjectMetadata(JsPlatform::Get(), type);
|
||||
AddIncludeFiles(metadata.includeFiles);
|
||||
|
||||
std::vector<gd::String> behaviors = object.GetAllBehaviorNames();
|
||||
for (std::size_t j = 0; j < behaviors.size(); ++j) {
|
||||
const gd::BehaviorMetadata& metadata =
|
||||
gd::MetadataProvider::GetBehaviorMetadata(
|
||||
JsPlatform::Get(),
|
||||
object.GetBehavior(behaviors[j]).GetTypeName());
|
||||
AddIncludeFiles(metadata.includeFiles);
|
||||
}
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < globalObjectsAndGroups.GetObjectsCount(); ++i)
|
||||
addIncludeFiles(globalObjectsAndGroups.GetObject(i));
|
||||
|
||||
for (std::size_t i = 0; i < objectsAndGroups.GetObjectsCount(); ++i)
|
||||
addIncludeFiles(objectsAndGroups.GetObject(i));
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateAllConditionsBooleanDeclarations() {
|
||||
gd::String globalConditionsBooleans;
|
||||
for (unsigned int i = 0; i <= GetMaxCustomConditionsDepth(); ++i) {
|
||||
@@ -768,16 +737,16 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
|
||||
|
||||
gd::String copiedListName =
|
||||
GetObjectListName(object, *context.GetParentContext());
|
||||
return objectListName + ".createFrom(" + copiedListName + ");\n";
|
||||
return "gdjs.copyArray(" + copiedListName + ", " + objectListName + ");\n";
|
||||
};
|
||||
|
||||
gd::String declarationsCode;
|
||||
for (auto object : context.GetObjectsListsToBeDeclared()) {
|
||||
gd::String objectListDeclaration = "";
|
||||
if (!context.ObjectAlreadyDeclared(object)) {
|
||||
objectListDeclaration += GetObjectListName(object, context) +
|
||||
".createFrom(" +
|
||||
GenerateAllInstancesGetterCode(object) + ");";
|
||||
objectListDeclaration += "gdjs.copyArray(" +
|
||||
GenerateAllInstancesGetterCode(object) + ", " +
|
||||
GetObjectListName(object, context) + ");";
|
||||
context.SetObjectDeclared(object);
|
||||
} else
|
||||
objectListDeclaration = declareObjectList(object, context);
|
||||
@@ -839,15 +808,17 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(
|
||||
: "runtimeScene, eventsFunctionContext";
|
||||
|
||||
// Generate a unique name for the function.
|
||||
gd::String uniqueId =
|
||||
gd::String::From(GenerateSingleUsageUniqueIdForEventsList());
|
||||
gd::String functionName =
|
||||
GetCodeNamespaceAccessor() + "eventsList" + gd::String::From(&events);
|
||||
GetCodeNamespaceAccessor() + "eventsList" + uniqueId;
|
||||
|
||||
// The only local parameters are runtimeScene and context.
|
||||
// List of objects, conditions booleans and any variables used by events
|
||||
// are stored in static variables that are globally available by the whole
|
||||
// code.
|
||||
AddCustomCodeOutsideMain(functionName + " = function(" + parametersCode +
|
||||
") {\n" + code + "\n" + "}; //End of " +
|
||||
functionName + "\n");
|
||||
") {\n" + code + "\n" + "};");
|
||||
|
||||
// Replace the code of the events by the call to the function. This does not
|
||||
// interfere with the objects picking as the lists are in static variables
|
||||
|
@@ -310,12 +310,6 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
|
||||
std::pair<gd::String, gd::String> GenerateAllObjectsDeclarationsAndResets(
|
||||
unsigned int maxDepthLevelReached);
|
||||
|
||||
/**
|
||||
* \brief Add to include files all the files required by the object and their
|
||||
* behaviors.
|
||||
*/
|
||||
void AddAllObjectsIncludeFiles();
|
||||
|
||||
/**
|
||||
* \brief Generate the list of parameters of a function.
|
||||
*
|
||||
|
@@ -4,8 +4,10 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "CommonInstructionsExtension.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/Builtin/CommentEvent.h"
|
||||
#include "GDCore/Events/Builtin/ForEachEvent.h"
|
||||
@@ -227,8 +229,9 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
gd::String::From(parentContext.GetContextDepth()) + "_" +
|
||||
gd::String::From(parentContext.GetCurrentConditionDepth()) +
|
||||
"final";
|
||||
code += codeGenerator.GetObjectListName(*it, parentContext) +
|
||||
".createFrom(" + finalObjList + ");\n";
|
||||
code += "gdjs.copyArray(" + finalObjList + ", " +
|
||||
codeGenerator.GetObjectListName(*it, parentContext) +
|
||||
");\n";
|
||||
}
|
||||
code += "}\n";
|
||||
|
||||
@@ -325,7 +328,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
[](gd::Instruction& instruction,
|
||||
gd::EventsCodeGenerator& codeGenerator,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
size_t uniqueId = (size_t)&instruction;
|
||||
size_t uniqueId = codeGenerator.GenerateSingleUsageUniqueIdFor(
|
||||
instruction.GetOriginalInstruction().lock().get());
|
||||
gd::String outputCode = codeGenerator.GenerateBooleanFullName(
|
||||
"conditionTrue", context) +
|
||||
".val = ";
|
||||
|
@@ -16,7 +16,9 @@ NetworkExtension::NetworkExtension() {
|
||||
gd::BuiltinExtensionsImplementer::ImplementsNetworkExtension(*this);
|
||||
|
||||
GetAllActions()["SendRequest"].SetFunctionName(
|
||||
"gdjs.evtTools.network.sendHttpRequest");
|
||||
"gdjs.evtTools.network.sendDeprecatedSynchronousRequest");
|
||||
GetAllActions()["SendAsyncRequest"].SetFunctionName(
|
||||
"gdjs.evtTools.network.sendAsyncRequest");
|
||||
GetAllActions()["JSONToVariableStructure"].SetFunctionName(
|
||||
"gdjs.evtTools.network.jsonToVariableStructure");
|
||||
GetAllActions()["JSONToGlobalVariableStructure"].SetFunctionName(
|
||||
|
@@ -4,11 +4,13 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDJS/IDE/Exporter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
|
||||
@@ -35,28 +37,10 @@ Exporter::Exporter(gd::AbstractFileSystem& fileSystem, gd::String gdjsRoot_)
|
||||
|
||||
Exporter::~Exporter() {}
|
||||
|
||||
bool Exporter::ExportLayoutForPixiPreview(gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
gd::String exportDir) {
|
||||
bool Exporter::ExportProjectForPixiPreview(
|
||||
const PreviewExportOptions& options) {
|
||||
ExporterHelper helper(fs, gdjsRoot, codeOutputDir);
|
||||
gd::SerializerElement options;
|
||||
options.AddChild("isPreview").SetBoolValue(true);
|
||||
|
||||
return helper.ExportLayoutForPixiPreview(project, layout, exportDir, gd::Serializer::ToJSON(options));
|
||||
}
|
||||
|
||||
bool Exporter::ExportExternalLayoutForPixiPreview(
|
||||
gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
gd::ExternalLayout& externalLayout,
|
||||
gd::String exportDir) {
|
||||
gd::SerializerElement options;
|
||||
options.AddChild("injectExternalLayout").SetValue(externalLayout.GetName());
|
||||
options.AddChild("isPreview").SetBoolValue(true);
|
||||
|
||||
ExporterHelper helper(fs, gdjsRoot, codeOutputDir);
|
||||
return helper.ExportLayoutForPixiPreview(
|
||||
project, layout, exportDir, gd::Serializer::ToJSON(options));
|
||||
return helper.ExportProjectForPixiPreview(options);
|
||||
}
|
||||
|
||||
bool Exporter::ExportWholePixiProject(
|
||||
@@ -68,7 +52,6 @@ bool Exporter::ExportWholePixiProject(
|
||||
|
||||
auto exportProject = [this, &exportedProject, &exportOptions, &helper](
|
||||
gd::String exportDir) {
|
||||
bool minify = exportOptions["minify"];
|
||||
bool exportForCordova = exportOptions["exportForCordova"];
|
||||
bool exportForFacebookInstantGames =
|
||||
exportOptions["exportForFacebookInstantGames"];
|
||||
@@ -95,7 +78,11 @@ bool Exporter::ExportWholePixiProject(
|
||||
// Export engine libraries
|
||||
helper.AddLibsInclude(true, false, false, includesFiles);
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves to the engine)
|
||||
// Export files for object and behaviors
|
||||
helper.ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves
|
||||
// to the engine)
|
||||
helper.ExportEffectIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Export events
|
||||
@@ -120,13 +107,14 @@ bool Exporter::ExportWholePixiProject(
|
||||
gd::ProjectStripper::StripProjectForExport(exportedProject);
|
||||
|
||||
//...and export it
|
||||
helper.ExportToJSON(
|
||||
fs, exportedProject, codeOutputDir + "/data.js", "gdjs.projectData");
|
||||
gd::SerializerElement noRuntimeGameOptions;
|
||||
helper.ExportProjectData(
|
||||
fs, exportedProject, codeOutputDir + "/data.js", noRuntimeGameOptions);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
|
||||
// Copy all dependencies and the index (or metadata) file.
|
||||
helper.RemoveIncludes(false, true, includesFiles);
|
||||
helper.ExportIncludesAndLibs(includesFiles, exportDir, minify);
|
||||
helper.ExportIncludesAndLibs(includesFiles, exportDir);
|
||||
|
||||
gd::String source = gdjsRoot + "/Runtime/index.html";
|
||||
if (exportForCordova)
|
||||
@@ -195,7 +183,11 @@ bool Exporter::ExportWholeCocos2dProject(gd::Project& project,
|
||||
// Export engine libraries
|
||||
helper.AddLibsInclude(false, true, false, includesFiles);
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves to the engine)
|
||||
// Export files for object and behaviors
|
||||
helper.ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves to
|
||||
// the engine)
|
||||
helper.ExportEffectIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Export events
|
||||
@@ -219,13 +211,14 @@ bool Exporter::ExportWholeCocos2dProject(gd::Project& project,
|
||||
gd::ProjectStripper::StripProjectForExport(exportedProject);
|
||||
|
||||
//...and export it
|
||||
helper.ExportToJSON(
|
||||
fs, exportedProject, codeOutputDir + "/data.js", "gdjs.projectData");
|
||||
gd::SerializerElement noRuntimeGameOptions;
|
||||
helper.ExportProjectData(
|
||||
fs, exportedProject, codeOutputDir + "/data.js", noRuntimeGameOptions);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
|
||||
// Copy all dependencies and the index (or metadata) file.
|
||||
helper.RemoveIncludes(true, false, includesFiles);
|
||||
helper.ExportIncludesAndLibs(includesFiles, exportDir + "/src", false);
|
||||
helper.ExportIncludesAndLibs(includesFiles, exportDir + "/src");
|
||||
|
||||
if (!helper.ExportCocos2dFiles(
|
||||
project, exportDir, debugMode, includesFiles)) {
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Project;
|
||||
@@ -16,6 +17,7 @@ class Layout;
|
||||
class ExternalLayout;
|
||||
class AbstractFileSystem;
|
||||
} // namespace gd
|
||||
namespace gdjs { struct PreviewExportOptions; }
|
||||
|
||||
namespace gdjs {
|
||||
|
||||
@@ -30,32 +32,13 @@ class Exporter {
|
||||
virtual ~Exporter();
|
||||
|
||||
/**
|
||||
* \brief Create a preview for the specified layout.
|
||||
* \brief Create a preview for the specified options.
|
||||
* \note The preview is not launched, it is the caller responsibility to open
|
||||
* a browser pointing to the preview.
|
||||
*
|
||||
* \param layout The layout to be previewed.
|
||||
* \param exportDir The directory where the preview must be created.
|
||||
* \return true if export was successful.
|
||||
* \param options The options to generate the preview.
|
||||
*/
|
||||
bool ExportLayoutForPixiPreview(gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
gd::String exportDir);
|
||||
|
||||
/**
|
||||
* \brief Create a preview for the specified external layout and layout.
|
||||
* \note The preview is not launched, it is the caller responsibility to open
|
||||
* a browser pointing to the preview.
|
||||
*
|
||||
* \param layout The layout to be previewed.
|
||||
* \param externalLayout The external layout with objects to be created at
|
||||
* scene startup. \param exportDir The directory where the preview must be
|
||||
* created. \return true if export was successful.
|
||||
*/
|
||||
bool ExportExternalLayoutForPixiPreview(gd::Project& project,
|
||||
gd::Layout& layout,
|
||||
gd::ExternalLayout& externalLayout,
|
||||
gd::String exportDir);
|
||||
bool ExportProjectForPixiPreview(const PreviewExportOptions& options);
|
||||
|
||||
/**
|
||||
* \brief Export the specified project, using Pixi.js.
|
||||
|
@@ -13,6 +13,10 @@
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/CodeGeneration/EffectsCodeGenerator.h"
|
||||
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
|
||||
#include "GDCore/IDE/ProjectStripper.h"
|
||||
@@ -21,14 +25,53 @@
|
||||
#include "GDCore/Project/ExternalLayout.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Project/SourceFile.h"
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
#include "GDCore/TinyXml/tinyxml.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
#include "GDJS/Events/CodeGeneration/LayoutCodeGenerator.h"
|
||||
#include "GDJS/Extensions/JsPlatform.h"
|
||||
#undef CopyFile // Disable an annoying macro
|
||||
|
||||
namespace {
|
||||
|
||||
std::map<gd::String, gd::String> GetExtensionDependencyExtraSettingValues(
|
||||
const gd::Project &project,
|
||||
const gd::String &extensionName,
|
||||
const gd::DependencyMetadata &dependency) {
|
||||
std::map<gd::String, gd::String> values;
|
||||
|
||||
for (const auto &extraSetting : dependency.GetAllExtraSettings()) {
|
||||
const gd::String &type = extraSetting.second.GetType();
|
||||
const gd::String extraSettingValue =
|
||||
type == "ExtensionProperty"
|
||||
? project.GetExtensionProperties().GetValue(
|
||||
extensionName, extraSetting.second.GetValue())
|
||||
: extraSetting.second.GetValue();
|
||||
|
||||
if (!extraSettingValue.empty())
|
||||
values[extraSetting.first] = extraSettingValue;
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
bool AreMapKeysMissingElementOfSet(const std::map<gd::String, gd::String> &map,
|
||||
const std::set<gd::String> &set) {
|
||||
bool missingKey = false;
|
||||
for (auto &key : set) {
|
||||
if (map.find(key) == map.end()) {
|
||||
missingKey = true;
|
||||
}
|
||||
}
|
||||
|
||||
return missingKey;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace gdjs {
|
||||
|
||||
static void InsertUnique(std::vector<gd::String> &container, gd::String str) {
|
||||
@@ -41,87 +84,124 @@ ExporterHelper::ExporterHelper(gd::AbstractFileSystem &fileSystem,
|
||||
gd::String codeOutputDir_)
|
||||
: fs(fileSystem), gdjsRoot(gdjsRoot_), codeOutputDir(codeOutputDir_){};
|
||||
|
||||
bool ExporterHelper::ExportLayoutForPixiPreview(gd::Project &project,
|
||||
gd::Layout &layout,
|
||||
gd::String exportDir,
|
||||
gd::String additionalSpec) {
|
||||
fs.MkDir(exportDir);
|
||||
fs.ClearDir(exportDir);
|
||||
bool ExporterHelper::ExportProjectForPixiPreview(
|
||||
const PreviewExportOptions &options) {
|
||||
fs.MkDir(options.exportPath);
|
||||
fs.ClearDir(options.exportPath);
|
||||
std::vector<gd::String> includesFiles;
|
||||
|
||||
gd::Project exportedProject = project;
|
||||
gd::Project exportedProject = options.project;
|
||||
|
||||
// Always disable the splash for preview
|
||||
exportedProject.GetLoadingScreen().ShowGDevelopSplash(false);
|
||||
|
||||
// Export resources (*before* generating events as some resources filenames
|
||||
// may be updated)
|
||||
ExportResources(fs, exportedProject, exportDir);
|
||||
ExportResources(fs, exportedProject, options.exportPath);
|
||||
|
||||
// Compatibility with GD <= 5.0-beta56
|
||||
// Stay compatible with text objects declaring their font as just a filename
|
||||
// without a font resource - by manually adding these resources.
|
||||
AddDeprecatedFontFilesToFontResources(
|
||||
fs, exportedProject.GetResourcesManager(), exportDir);
|
||||
fs, exportedProject.GetResourcesManager(), options.exportPath);
|
||||
// end of compatibility code
|
||||
|
||||
// Export engine libraries
|
||||
AddLibsInclude(true, false, true, includesFiles);
|
||||
|
||||
// Export files for object and behaviors
|
||||
ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves to
|
||||
// the engine)
|
||||
ExportEffectIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Generate events code
|
||||
if (!ExportEventsCode(exportedProject, codeOutputDir, includesFiles, true))
|
||||
return false;
|
||||
if (!options.projectDataOnlyExport) {
|
||||
// Generate events code
|
||||
if (!ExportEventsCode(exportedProject, codeOutputDir, includesFiles, true))
|
||||
return false;
|
||||
|
||||
// Export source files
|
||||
if (!ExportExternalSourceFiles(
|
||||
exportedProject, codeOutputDir, includesFiles)) {
|
||||
gd::LogError(_("Error during exporting! Unable to export source files:\n") +
|
||||
lastError);
|
||||
return false;
|
||||
// Export source files
|
||||
if (!ExportExternalSourceFiles(
|
||||
exportedProject, codeOutputDir, includesFiles)) {
|
||||
gd::LogError(
|
||||
_("Error during exporting! Unable to export source files:\n") +
|
||||
lastError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Strip the project (*after* generating events as the events may use stripped
|
||||
// things (objects groups...))
|
||||
gd::ProjectStripper::StripProjectForExport(exportedProject);
|
||||
exportedProject.SetFirstLayout(layout.GetName());
|
||||
exportedProject.SetFirstLayout(options.layoutName);
|
||||
|
||||
// Strip the includes to only have Pixi.js files (*before* creating
|
||||
// runtimeGameOptions, since otherwise Cocos files will be passed to the
|
||||
// hot-reloader).
|
||||
RemoveIncludes(false, true, includesFiles);
|
||||
|
||||
// Create the setup options passed to the gdjs.RuntimeGame
|
||||
gd::SerializerElement runtimeGameOptions;
|
||||
runtimeGameOptions.AddChild("isPreview").SetBoolValue(true);
|
||||
if (!options.externalLayoutName.empty()) {
|
||||
runtimeGameOptions.AddChild("injectExternalLayout")
|
||||
.SetValue(options.externalLayoutName);
|
||||
}
|
||||
runtimeGameOptions.AddChild("projectDataOnlyExport")
|
||||
.SetBoolValue(options.projectDataOnlyExport);
|
||||
runtimeGameOptions.AddChild("debuggerServerAddress")
|
||||
.SetStringValue(options.debuggerServerAddress);
|
||||
runtimeGameOptions.AddChild("debuggerServerPort")
|
||||
.SetStringValue(options.debuggerServerPort);
|
||||
|
||||
// Pass in the options the list of scripts files - useful for hot-reloading.
|
||||
auto &scriptFilesElement = runtimeGameOptions.AddChild("scriptFiles");
|
||||
scriptFilesElement.ConsiderAsArrayOf("scriptFile");
|
||||
|
||||
for (const auto &includeFile : includesFiles) {
|
||||
auto hashIt = options.includeFileHashes.find(includeFile);
|
||||
gd::String scriptSrc = GetExportedIncludeFilename(includeFile);
|
||||
scriptFilesElement.AddChild("scriptFile")
|
||||
.SetStringAttribute("path", scriptSrc)
|
||||
.SetIntAttribute(
|
||||
"hash",
|
||||
hashIt != options.includeFileHashes.end() ? hashIt->second : 0);
|
||||
}
|
||||
|
||||
// Export the project
|
||||
ExportToJSON(
|
||||
fs, exportedProject, codeOutputDir + "/data.js", "gdjs.projectData");
|
||||
ExportProjectData(
|
||||
fs, exportedProject, codeOutputDir + "/data.js", runtimeGameOptions);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
|
||||
// Copy all the dependencies
|
||||
RemoveIncludes(false, true, includesFiles);
|
||||
ExportIncludesAndLibs(includesFiles, exportDir, false);
|
||||
ExportIncludesAndLibs(includesFiles, options.exportPath);
|
||||
|
||||
// Create the index file
|
||||
if (!ExportPixiIndexFile(exportedProject,
|
||||
gdjsRoot + "/Runtime/index.html",
|
||||
exportDir,
|
||||
options.exportPath,
|
||||
includesFiles,
|
||||
additionalSpec))
|
||||
"gdjs.runtimeGameOptions"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String ExporterHelper::ExportToJSON(gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
gd::String filename,
|
||||
gd::String wrapIntoVariable) {
|
||||
gd::String ExporterHelper::ExportProjectData(
|
||||
gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions) {
|
||||
fs.MkDir(fs.DirNameFrom(filename));
|
||||
|
||||
// Save the project to JSON
|
||||
gd::SerializerElement rootElement;
|
||||
project.SerializeTo(rootElement);
|
||||
|
||||
gd::String output = gd::Serializer::ToJSON(rootElement);
|
||||
if (!wrapIntoVariable.empty())
|
||||
output = wrapIntoVariable + " = " + output + ";";
|
||||
gd::String output =
|
||||
"gdjs.projectData = " + gd::Serializer::ToJSON(rootElement) + ";\n" +
|
||||
"gdjs.runtimeGameOptions = " +
|
||||
gd::Serializer::ToJSON(runtimeGameOptions) + ";\n";
|
||||
|
||||
if (!fs.WriteToFile(filename, output)) return "Unable to write " + filename;
|
||||
|
||||
@@ -222,16 +302,41 @@ bool ExporterHelper::ExportCordovaFiles(const gd::Project &project,
|
||||
.FindAndReplace("<!-- GDJS_ICONS_ANDROID -->", makeIconsAndroid())
|
||||
.FindAndReplace("<!-- GDJS_ICONS_IOS -->", makeIconsIos());
|
||||
|
||||
if (!project.GetAdMobAppId().empty()) {
|
||||
str = str.FindAndReplace(
|
||||
"<!-- GDJS_ADMOB_PLUGIN_AND_APPLICATION_ID -->",
|
||||
"<plugin name=\"cordova-plugin-admob-free\" spec=\"~0.21.0\">\n"
|
||||
"\t\t<variable name=\"ADMOB_APP_ID\" value=\"" +
|
||||
project.GetAdMobAppId() +
|
||||
"\" />\n"
|
||||
"\t</plugin>");
|
||||
gd::String plugins = "";
|
||||
|
||||
for (std::shared_ptr<gd::PlatformExtension> extension :
|
||||
project.GetCurrentPlatform().GetAllPlatformExtensions()) {
|
||||
for (gd::DependencyMetadata dependency : extension->GetAllDependencies()) {
|
||||
if (dependency.GetDependencyType() == "cordova") {
|
||||
gd::String plugin;
|
||||
plugin += "<plugin name=\"" + dependency.GetExportName();
|
||||
if (dependency.GetVersion() != "") {
|
||||
plugin += "\" spec=\"" + dependency.GetVersion();
|
||||
}
|
||||
plugin += "\">\n";
|
||||
|
||||
auto extraSettingValues = GetExtensionDependencyExtraSettingValues(
|
||||
project, extension->GetName(), dependency);
|
||||
|
||||
// For Cordova, all settings are considered a plugin variable.
|
||||
for (auto &extraSetting : extraSettingValues) {
|
||||
plugin += "\t\t<variable name=\"" + extraSetting.first +
|
||||
"\" value=\"" + extraSetting.second + "\" />\n";
|
||||
}
|
||||
|
||||
plugin += "\t</plugin>";
|
||||
|
||||
// Don't include the plugin if an extra setting was not fulfilled.
|
||||
bool missingSetting = AreMapKeysMissingElementOfSet(
|
||||
extraSettingValues, dependency.GetRequiredExtraSettingsForExport());
|
||||
if (!missingSetting) plugins += plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
str =
|
||||
str.FindAndReplace("<!-- GDJS_EXTENSION_CORDOVA_DEPENDENCY -->", plugins);
|
||||
|
||||
if (!fs.WriteToFile(exportDir + "/config.xml", str)) {
|
||||
lastError = "Unable to write Cordova config.xml file.";
|
||||
return false;
|
||||
@@ -307,15 +412,21 @@ bool ExporterHelper::ExportCocos2dFiles(
|
||||
{
|
||||
gd::String includeFilesStr = "";
|
||||
bool first = true;
|
||||
for (auto &file : includesFiles) {
|
||||
if (!fs.FileExists(exportDir + "/src/" + file)) {
|
||||
std::cout << "Warning: Unable to find " << exportDir + "/" + file << "."
|
||||
for (auto &include : includesFiles) {
|
||||
gd::String scriptSrc = GetExportedIncludeFilename(include);
|
||||
|
||||
// Sanity check if the file exists - if not skip it to avoid
|
||||
// including it in the list of scripts.
|
||||
gd::String absoluteFilename = scriptSrc;
|
||||
fs.MakeAbsolute(absoluteFilename, exportDir + "/src");
|
||||
if (!fs.FileExists(absoluteFilename)) {
|
||||
std::cout << "Warning: Unable to find " << absoluteFilename << "."
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
includeFilesStr +=
|
||||
gd::String(first ? "" : ", ") + "\"src/" + file + "\"\n";
|
||||
gd::String(first ? "" : ", ") + "\"src/" + scriptSrc + "\"\n";
|
||||
first = false;
|
||||
}
|
||||
|
||||
@@ -376,6 +487,39 @@ bool ExporterHelper::ExportElectronFiles(const gd::Project &project,
|
||||
.FindAndReplace("\"GDJS_GAME_VERSION\"", jsonVersion)
|
||||
.FindAndReplace("\"GDJS_GAME_MANGLED_NAME\"", jsonMangledName);
|
||||
|
||||
gd::String packages = "";
|
||||
|
||||
for (std::shared_ptr<gd::PlatformExtension> extension :
|
||||
project.GetCurrentPlatform()
|
||||
.GetAllPlatformExtensions()) { // TODO Add a way to select only
|
||||
// used Extensions
|
||||
for (gd::DependencyMetadata dependency :
|
||||
extension->GetAllDependencies()) {
|
||||
if (dependency.GetDependencyType() == "npm") {
|
||||
if (dependency.GetVersion() == "") {
|
||||
gd::LogError(
|
||||
"Latest Version not available for NPM dependencies, "
|
||||
"dependency " +
|
||||
dependency.GetName() +
|
||||
" is not exported. Please specify a version when calling "
|
||||
"addDependency.");
|
||||
continue;
|
||||
}
|
||||
packages += "\n\t\"" + dependency.GetExportName() + "\": \"" +
|
||||
dependency.GetVersion() + "\",";
|
||||
// For node extra settings are ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!packages.empty()) {
|
||||
// Remove the , at the end as last item cannot have , in JSON.
|
||||
packages = packages.substr(0, packages.size() - 1);
|
||||
}
|
||||
|
||||
str = str.FindAndReplace("\"GDJS_EXTENSION_NPM_DEPENDENCY\": \"0\"",
|
||||
packages);
|
||||
|
||||
if (!fs.WriteToFile(exportDir + "/package.json", str)) {
|
||||
lastError = "Unable to write Electron package.json file.";
|
||||
return false;
|
||||
@@ -425,25 +569,17 @@ bool ExporterHelper::CompleteIndexFile(
|
||||
if (additionalSpec.empty()) additionalSpec = "{}";
|
||||
|
||||
gd::String codeFilesIncludes;
|
||||
for (std::vector<gd::String>::const_iterator it = includesFiles.begin();
|
||||
it != includesFiles.end();
|
||||
++it) {
|
||||
gd::String scriptSrc = "";
|
||||
if (fs.IsAbsolute(*it)) {
|
||||
// Most of the time, script source are file paths relative to GDJS root or
|
||||
// have been copied in the output directory, so they are relative. It's
|
||||
// still useful to test here for absolute files as the exporter could be
|
||||
// configured with a file system dealing with URL.
|
||||
scriptSrc = *it;
|
||||
} else {
|
||||
if (!fs.FileExists(exportDir + "/" + *it)) {
|
||||
std::cout << "Warning: Unable to find " << exportDir + "/" + *it << "."
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
for (auto &include : includesFiles) {
|
||||
gd::String scriptSrc = GetExportedIncludeFilename(include);
|
||||
|
||||
scriptSrc = exportDir + "/" + *it;
|
||||
fs.MakeRelative(scriptSrc, exportDir);
|
||||
// Sanity check if the file exists - if not skip it to avoid
|
||||
// including it in the list of scripts.
|
||||
gd::String absoluteFilename = scriptSrc;
|
||||
fs.MakeAbsolute(absoluteFilename, exportDir);
|
||||
if (!fs.FileExists(absoluteFilename)) {
|
||||
std::cout << "Warning: Unable to find " << absoluteFilename << "."
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
codeFilesIncludes += "\t<script src=\"" + scriptSrc +
|
||||
@@ -500,6 +636,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
InsertUnique(includesFiles, "events-tools/networktools.js");
|
||||
|
||||
if (websocketDebuggerClient) {
|
||||
InsertUnique(includesFiles, "websocket-debugger-client/hot-reloader.js");
|
||||
InsertUnique(includesFiles,
|
||||
"websocket-debugger-client/websocket-debugger-client.js");
|
||||
}
|
||||
@@ -635,38 +772,50 @@ bool ExporterHelper::ExportExternalSourceFiles(
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String ExporterHelper::GetExportedIncludeFilename(
|
||||
const gd::String &include) {
|
||||
if (!fs.IsAbsolute(include)) {
|
||||
// By convention, an include file that is relative is relative to
|
||||
// the "<GDJS Root>/Runtime" folder, and will have the same relative
|
||||
// path when exported.
|
||||
|
||||
// We still do this seemingly useless relative to absolute to relative
|
||||
// conversion, because some filesystems are using a URL for gdjsRoot, and
|
||||
// will convert the relative include to an absolute URL.
|
||||
gd::String relativeInclude = gdjsRoot + "/Runtime/" + include;
|
||||
fs.MakeRelative(relativeInclude, gdjsRoot + "/Runtime/");
|
||||
return relativeInclude;
|
||||
} else {
|
||||
// Note: all the code generated from events are generated in another
|
||||
// folder and fall in this case:
|
||||
return fs.FileNameFrom(include);
|
||||
}
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportIncludesAndLibs(
|
||||
std::vector<gd::String> &includesFiles,
|
||||
gd::String exportDir,
|
||||
bool /*minify*/) {
|
||||
for (std::vector<gd::String>::iterator include = includesFiles.begin();
|
||||
include != includesFiles.end();
|
||||
++include) {
|
||||
if (!fs.IsAbsolute(*include)) {
|
||||
gd::String source = gdjsRoot + "/Runtime/" + *include;
|
||||
const std::vector<gd::String> &includesFiles, gd::String exportDir) {
|
||||
for (auto &include : includesFiles) {
|
||||
if (!fs.IsAbsolute(include)) {
|
||||
// By convention, an include file that is relative is relative to
|
||||
// the "<GDJS Root>/Runtime" folder, and will have the same relative
|
||||
// path when exported.
|
||||
gd::String source = gdjsRoot + "/Runtime/" + include;
|
||||
if (fs.FileExists(source)) {
|
||||
gd::String path = fs.DirNameFrom(exportDir + "/" + *include);
|
||||
gd::String path = fs.DirNameFrom(exportDir + "/" + include);
|
||||
if (!fs.DirExists(path)) fs.MkDir(path);
|
||||
|
||||
fs.CopyFile(source, exportDir + "/" + *include);
|
||||
|
||||
gd::String relativeInclude = source;
|
||||
fs.MakeRelative(relativeInclude, gdjsRoot + "/Runtime/");
|
||||
*include = relativeInclude;
|
||||
fs.CopyFile(source, exportDir + "/" + include);
|
||||
} else {
|
||||
std::cout << "Could not find GDJS include file " << *include
|
||||
std::cout << "Could not find GDJS include file " << include
|
||||
<< std::endl;
|
||||
}
|
||||
} else {
|
||||
// Note: all the code generated from events are generated in another
|
||||
// folder and fall in this case:
|
||||
|
||||
if (fs.FileExists(*include)) {
|
||||
fs.CopyFile(*include, exportDir + "/" + fs.FileNameFrom(*include));
|
||||
*include = fs.FileNameFrom(
|
||||
*include); // Ensure filename is relative to the export dir.
|
||||
if (fs.FileExists(include)) {
|
||||
fs.CopyFile(include, exportDir + "/" + fs.FileNameFrom(include));
|
||||
} else {
|
||||
std::cout << "Could not find include file " << *include << std::endl;
|
||||
std::cout << "Could not find include file " << include << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -674,6 +823,45 @@ bool ExporterHelper::ExportIncludesAndLibs(
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExporterHelper::ExportObjectAndBehaviorsIncludes(
|
||||
gd::Project &project, std::vector<gd::String> &includesFiles) {
|
||||
auto addIncludeFiles = [&](const std::vector<gd::String> &newIncludeFiles) {
|
||||
for (const auto &includeFile : newIncludeFiles) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
}
|
||||
};
|
||||
|
||||
auto addObjectIncludeFiles = [&](const gd::Object &object) {
|
||||
// Ensure needed files are included for the object type and its behaviors.
|
||||
const gd::ObjectMetadata &metadata =
|
||||
gd::MetadataProvider::GetObjectMetadata(JsPlatform::Get(),
|
||||
object.GetType());
|
||||
addIncludeFiles(metadata.includeFiles);
|
||||
|
||||
std::vector<gd::String> behaviors = object.GetAllBehaviorNames();
|
||||
for (std::size_t j = 0; j < behaviors.size(); ++j) {
|
||||
const gd::BehaviorMetadata &metadata =
|
||||
gd::MetadataProvider::GetBehaviorMetadata(
|
||||
JsPlatform::Get(),
|
||||
object.GetBehavior(behaviors[j]).GetTypeName());
|
||||
addIncludeFiles(metadata.includeFiles);
|
||||
}
|
||||
};
|
||||
|
||||
auto addObjectsIncludeFiles =
|
||||
[&](const gd::ObjectsContainer &objectsContainer) {
|
||||
for (std::size_t i = 0; i < objectsContainer.GetObjectsCount(); ++i) {
|
||||
addObjectIncludeFiles(objectsContainer.GetObject(i));
|
||||
}
|
||||
};
|
||||
|
||||
addObjectsIncludeFiles(project);
|
||||
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
|
||||
gd::Layout &layout = project.GetLayout(i);
|
||||
addObjectsIncludeFiles(layout);
|
||||
}
|
||||
}
|
||||
|
||||
void ExporterHelper::ExportResources(gd::AbstractFileSystem &fs,
|
||||
gd::Project &project,
|
||||
gd::String exportDir) {
|
||||
|
@@ -5,14 +5,17 @@
|
||||
*/
|
||||
#ifndef EXPORTER_HELPER_H
|
||||
#define EXPORTER_HELPER_H
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
class ExternalLayout;
|
||||
class SerializerElement;
|
||||
class AbstractFileSystem;
|
||||
class ResourcesManager;
|
||||
} // namespace gd
|
||||
@@ -20,6 +23,74 @@ class wxProgressDialog;
|
||||
|
||||
namespace gdjs {
|
||||
|
||||
/**
|
||||
* \brief The options used to export a project for a preview.
|
||||
*/
|
||||
struct PreviewExportOptions {
|
||||
/**
|
||||
* \param project_ The project to export
|
||||
* \param exportPath_ The path in the filesystem where to export the files
|
||||
*/
|
||||
PreviewExportOptions(gd::Project &project_, const gd::String &exportPath_)
|
||||
: project(project_), exportPath(exportPath_), projectDataOnlyExport(false) {};
|
||||
|
||||
/**
|
||||
* \brief Set the address of the debugger server that the game should reach out to,
|
||||
* using WebSockets.
|
||||
*/
|
||||
PreviewExportOptions &SetDebuggerServerAddress(const gd::String& address, const gd::String& port) {
|
||||
debuggerServerAddress = address;
|
||||
debuggerServerPort = port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the layout to be run first in the previewed game
|
||||
*/
|
||||
PreviewExportOptions &SetLayoutName(const gd::String &layoutName_) {
|
||||
layoutName = layoutName_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the (optional) external layout to be instanciated in the scene
|
||||
* at the beginning of the previewed game.
|
||||
*/
|
||||
PreviewExportOptions &SetExternalLayoutName(
|
||||
const gd::String &externalLayoutName_) {
|
||||
externalLayoutName = externalLayoutName_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the hash associated to an include file. Useful for the preview
|
||||
* hot-reload, to know if a file changed.
|
||||
*/
|
||||
PreviewExportOptions &SetIncludeFileHash(const gd::String &includeFile,
|
||||
int hash) {
|
||||
includeFileHashes[includeFile] = hash;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the export should only export the project data, not
|
||||
* exporting events code.
|
||||
*/
|
||||
PreviewExportOptions& SetProjectDataOnlyExport(bool enable) {
|
||||
projectDataOnlyExport = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
gd::Project &project;
|
||||
gd::String exportPath;
|
||||
gd::String debuggerServerAddress;
|
||||
gd::String debuggerServerPort;
|
||||
gd::String layoutName;
|
||||
gd::String externalLayoutName;
|
||||
std::map<gd::String, int> includeFileHashes;
|
||||
bool projectDataOnlyExport;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Export a project or a layout to a playable HTML5/Javascript based
|
||||
* game.
|
||||
@@ -42,15 +113,15 @@ class ExporterHelper {
|
||||
* \param fs The abstract file system to use to write the file
|
||||
* \param project The project to be exported.
|
||||
* \param filename The filename where export the project
|
||||
* \param wrapIntoVariable If not empty, the resulting json will be wrapped in
|
||||
* this javascript variable allowing to use it as a classical javascript
|
||||
* object. \return Empty string if everthing is ok, description of the error
|
||||
* otherwise.
|
||||
* \param runtimeGameOptions The content of the extra configuration to store
|
||||
* in gdjs.runtimeGameOptions \return Empty string if everthing is ok,
|
||||
* description of the error otherwise.
|
||||
*/
|
||||
static gd::String ExportToJSON(gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
gd::String filename,
|
||||
gd::String wrapIntoVariable);
|
||||
static gd::String ExportProjectData(
|
||||
gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions);
|
||||
|
||||
/**
|
||||
* \brief Copy all the resources of the project to to the export directory,
|
||||
@@ -82,22 +153,14 @@ class ExporterHelper {
|
||||
std::vector<gd::String> &includesFiles);
|
||||
|
||||
/**
|
||||
* \brief Copy all the includes files and the standard libraries files to the
|
||||
* export directory.
|
||||
*
|
||||
* The includes files are also modified so as to be relative to the export
|
||||
* directory ( Files with absolute filenames are copied into the export
|
||||
* directory and their path are stripped ).
|
||||
* \brief Copy all the specified files to the
|
||||
* export directory. Relative files are copied from "<GDJS root>/Runtime" directory.
|
||||
*
|
||||
* \param includesFiles A vector with filenames to be copied.
|
||||
* \param exportDir The directory where the preview must be created.
|
||||
* \param minify If true, the includes files must be merged into one file
|
||||
* using Google Closure Compiler. ( includesFiles parameter will be updated
|
||||
* with the new filename )
|
||||
* \param exportDir The directory where the files mus tbe copied.
|
||||
*/
|
||||
bool ExportIncludesAndLibs(std::vector<gd::String> &includesFiles,
|
||||
gd::String exportDir,
|
||||
bool minify);
|
||||
bool ExportIncludesAndLibs(const std::vector<gd::String> &includesFiles,
|
||||
gd::String exportDir);
|
||||
|
||||
/**
|
||||
* \brief Generate the events JS code, and save them to the export directory.
|
||||
@@ -119,6 +182,13 @@ class ExporterHelper {
|
||||
bool ExportEffectIncludes(gd::Project &project,
|
||||
std::vector<gd::String> &includesFiles);
|
||||
|
||||
/**
|
||||
* \brief Add the include files for all the objects of the project
|
||||
* and their behaviors.
|
||||
*/
|
||||
void ExportObjectAndBehaviorsIncludes(
|
||||
gd::Project &project, std::vector<gd::String> &includesFiles);
|
||||
|
||||
/**
|
||||
* \brief Copy the external source files used by the game into the export
|
||||
* directory, and add them into files to be included.
|
||||
@@ -208,18 +278,19 @@ class ExporterHelper {
|
||||
gd::String exportDir);
|
||||
|
||||
/**
|
||||
* \brief Launch all export methods to generate a complete, stand-alone game
|
||||
* for previewing.
|
||||
* \brief Create a preview for the specified options.
|
||||
* \note The preview is not launched, it is the caller responsibility to open
|
||||
* a browser pointing to the preview.
|
||||
*
|
||||
* \param layout The layout to be previewed.
|
||||
* \param exportDir The directory where the preview must be created.
|
||||
* \param additionalSpec Any additional parameters to be passed to the
|
||||
* gdjs.RuntimeGame. \return true if export was successful.
|
||||
* \param options The options to generate the preview.
|
||||
*/
|
||||
bool ExportLayoutForPixiPreview(gd::Project &project,
|
||||
gd::Layout &layout,
|
||||
gd::String exportDir,
|
||||
gd::String additionalSpec);
|
||||
bool ExportProjectForPixiPreview(const PreviewExportOptions &options);
|
||||
|
||||
/**
|
||||
* \brief Given an include file, returns the name of the file to reference
|
||||
* in the exported game.
|
||||
*/
|
||||
gd::String GetExportedIncludeFilename(const gd::String& include);
|
||||
|
||||
/**
|
||||
* \brief Change the directory where code files are generated.
|
||||
|
@@ -28,7 +28,7 @@
|
||||
<preference name="Fullscreen" value="true" />
|
||||
|
||||
<!-- Cordova/Phonegap version -->
|
||||
<preference name="phonegap-version" value="cli-8.0.0" />
|
||||
<preference name="phonegap-version" value="cli-9.0.0" />
|
||||
|
||||
<!-- GDJS_ADMOB_PLUGIN_AND_APPLICATION_ID -->
|
||||
<!-- GDJS_EXTENSION_CORDOVA_DEPENDENCY -->
|
||||
</widget>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* running in Electron Runtime.
|
||||
*/
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, shell } = require("electron");
|
||||
const { app, BrowserWindow, shell, Menu } = require("electron");
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
@@ -31,6 +31,8 @@ function createWindow() {
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile("app/index.html");
|
||||
|
||||
Menu.setApplicationMenu(null);
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
|
@@ -5,7 +5,9 @@
|
||||
"description": "GDJS_GAME_NAME",
|
||||
"author": "GDJS_GAME_AUTHOR",
|
||||
"version": "GDJS_GAME_VERSION",
|
||||
"dependencies": {},
|
||||
"dependencies": {
|
||||
"GDJS_EXTENSION_NPM_DEPENDENCY": "0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "8.2.5"
|
||||
},
|
||||
|
@@ -26,6 +26,15 @@ gdjs.CocosImageManager = function(resources)
|
||||
|
||||
gdjs.ImageManager = gdjs.CocosImageManager; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param {ResourceData[]} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.CocosImageManager.prototype.setResources = function(resources) {
|
||||
this._resources = resources;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the texture associated to the specified name.
|
||||
* Returns a placeholder texture if not found.
|
||||
|
@@ -16,7 +16,9 @@ gdjs.LayerCocosRenderer = function(layer, runtimeSceneRenderer)
|
||||
this._layer = layer;
|
||||
this.convertYPosition = runtimeSceneRenderer.convertYPosition;
|
||||
|
||||
var effects = this._layer.getEffectsData();
|
||||
// Read effects from the layer as we can't dynamically add effects
|
||||
// in Cocos2d-JS.
|
||||
var effects = this._layer.getInitialEffectsData();
|
||||
if (effects.length === 0) {
|
||||
this._cocosLayer = new CocosLayer();
|
||||
runtimeSceneRenderer.getCocosScene().addChild(this._cocosLayer);
|
||||
@@ -60,7 +62,7 @@ gdjs.LayerCocosRenderer.prototype._makeShaders = function() {
|
||||
return;
|
||||
}
|
||||
|
||||
var effects = this._layer.getEffectsData();
|
||||
var effects = this._layer.getInitialEffectsData();
|
||||
if (effects.length === 0) {
|
||||
return;
|
||||
} else if (effects.length > 1) {
|
||||
@@ -127,7 +129,11 @@ gdjs.LayerCocosRenderer.prototype.updateVisibility = function(visible) {
|
||||
this._cocosLayer.setVisible(visible);
|
||||
}
|
||||
|
||||
gdjs.LayerCocosRenderer.prototype.updateTime = function() {
|
||||
gdjs.LayerCocosRenderer.prototype.update = function() {
|
||||
// Unimplemented
|
||||
}
|
||||
|
||||
gdjs.LayerCocosRenderer.prototype.updateClearColor = function() {
|
||||
// Unimplemented
|
||||
}
|
||||
|
||||
@@ -180,7 +186,8 @@ gdjs.LayerCocosRenderer.prototype.enableEffect = function(name, value) {
|
||||
};
|
||||
|
||||
gdjs.LayerCocosRenderer.prototype.addEffect = function(effectData) {
|
||||
// Unimplemented
|
||||
// Unimplemented - adding effects is not supported in Cocos2d-JS.
|
||||
// All effects are supposed to be added to the layer at its creation.
|
||||
};
|
||||
|
||||
gdjs.LayerCocosRenderer.prototype.removeEffect = function(effect) {
|
||||
@@ -190,3 +197,8 @@ gdjs.LayerCocosRenderer.prototype.removeEffect = function(effect) {
|
||||
gdjs.LayerCocosRenderer.prototype.isEffectEnabled = function(name) {
|
||||
return this.hasEffect(name);
|
||||
};
|
||||
|
||||
|
||||
gdjs.LayerCocosRenderer.prototype.setLayerIndex = function(layer, index) {
|
||||
// Unimplemented
|
||||
};
|
||||
|
@@ -187,3 +187,14 @@ gdjs.RuntimeSceneCocosRenderer.prototype.showCursor = function() {
|
||||
gdjs.RuntimeSceneCocosRenderer.prototype.getCocosScene = function() {
|
||||
return this._cocosScene;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {gdjs.Layer} layer
|
||||
* @param {number} index
|
||||
*/
|
||||
gdjs.RuntimeSceneCocosRenderer.prototype.setLayerIndex = function (
|
||||
layer,
|
||||
index
|
||||
) {
|
||||
// Not implemented
|
||||
}
|
||||
|
@@ -66,6 +66,15 @@ gdjs.CocosSoundManager = function(resources)
|
||||
|
||||
gdjs.SoundManager = gdjs.CocosSoundManager; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param {ResourceData[]} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.CocosSoundManager.prototype.setResources = function(resources) {
|
||||
this._resources = resources;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the file associated to the given sound name.
|
||||
*
|
||||
|
@@ -10,96 +10,157 @@
|
||||
*/
|
||||
gdjs.evtTools.network = gdjs.evtTools.network || {};
|
||||
|
||||
gdjs.evtTools.network.sendHttpRequest = function(host, uri, body, method, contentType, responseVar)
|
||||
{
|
||||
try {
|
||||
var xhr;
|
||||
if (typeof XMLHttpRequest !== 'undefined')
|
||||
xhr = new XMLHttpRequest();
|
||||
else {
|
||||
var versions = ["MSXML2.XmlHttp.5.0",
|
||||
"MSXML2.XmlHttp.4.0",
|
||||
"MSXML2.XmlHttp.3.0",
|
||||
"MSXML2.XmlHttp.2.0",
|
||||
"Microsoft.XmlHttp"]
|
||||
/**
|
||||
* Send an asynchronous request to the specified URL, with the specified (text)
|
||||
* body, method and contentType (defaults to `application/x-www-form-urlencoded`).
|
||||
* The result is stored in the specified response variable. Any error is stored in
|
||||
* the specified error variable.
|
||||
*
|
||||
* @param {string} url The URL to send the request to.
|
||||
* @param {string} body The content to be sent.
|
||||
* @param {string} method The method to use ("GET", "POST", "PUT", "HEAD", "DELETE", "PATCH", "OPTIONS")
|
||||
* @param {string} contentType The content type. Defaults to `application/x-www-form-urlencoded` if empty.
|
||||
* @param {gdjs.Variable} responseVar The variable where to store the response text.
|
||||
* @param {gdjs.Variable} errorVar The variable where to store the error message or status code (if status >= 400).
|
||||
*/
|
||||
gdjs.evtTools.network.sendAsyncRequest = function (
|
||||
url,
|
||||
body,
|
||||
method,
|
||||
contentType,
|
||||
responseVar,
|
||||
errorVar
|
||||
) {
|
||||
const onError = (err) => {
|
||||
errorVar.setString('' + err);
|
||||
};
|
||||
|
||||
for(var i = 0, len = versions.length; i < len; i++) {
|
||||
try {
|
||||
xhr = new ActiveXObject(versions[i]);
|
||||
break;
|
||||
}
|
||||
catch(e){}
|
||||
} // end for
|
||||
}
|
||||
try {
|
||||
const request = new XMLHttpRequest();
|
||||
request.onerror = onError;
|
||||
request.ontimeout = onError;
|
||||
request.onabort = onError;
|
||||
request.onreadystatechange = () => {
|
||||
if (request.readyState === 4 /* "DONE" */) {
|
||||
if (request.status >= 400) {
|
||||
onError('' + request.status);
|
||||
}
|
||||
|
||||
if ( xhr === undefined ) return;
|
||||
responseVar.setString(request.responseText);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open(method, host+uri, false);
|
||||
xhr.setRequestHeader( "Content-Type", contentType === "" ? "application/x-www-form-urlencoded" : contentType );
|
||||
xhr.send(body);
|
||||
responseVar.setString(xhr.responseText);
|
||||
}
|
||||
catch(e){}
|
||||
request.open(method, url);
|
||||
request.setRequestHeader(
|
||||
'Content-Type',
|
||||
contentType === '' ? 'application/x-www-form-urlencoded' : contentType
|
||||
);
|
||||
request.send(body);
|
||||
} catch (err) {
|
||||
onError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/** @deprecated */
|
||||
gdjs.evtTools.network.sendDeprecatedSynchronousRequest = function (
|
||||
host,
|
||||
uri,
|
||||
body,
|
||||
method,
|
||||
contentType,
|
||||
responseVar
|
||||
) {
|
||||
try {
|
||||
var xhr;
|
||||
if (typeof XMLHttpRequest !== 'undefined') xhr = new XMLHttpRequest();
|
||||
else {
|
||||
var versions = [
|
||||
'MSXML2.XmlHttp.5.0',
|
||||
'MSXML2.XmlHttp.4.0',
|
||||
'MSXML2.XmlHttp.3.0',
|
||||
'MSXML2.XmlHttp.2.0',
|
||||
'Microsoft.XmlHttp',
|
||||
];
|
||||
|
||||
for (var i = 0, len = versions.length; i < len; i++) {
|
||||
try {
|
||||
xhr = new ActiveXObject(versions[i]);
|
||||
break;
|
||||
} catch (e) {}
|
||||
} // end for
|
||||
}
|
||||
|
||||
if (xhr === undefined) return;
|
||||
|
||||
xhr.open(method, host + uri, false);
|
||||
xhr.setRequestHeader(
|
||||
'Content-Type',
|
||||
contentType === '' ? 'application/x-www-form-urlencoded' : contentType
|
||||
);
|
||||
xhr.send(body);
|
||||
responseVar.setString(xhr.responseText);
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a variable to JSON.
|
||||
* TODO: Move to gdjs.Variable static
|
||||
* @param {gdjs.Variable} variable The variable to convert to JSON
|
||||
* @returns {string} The JSON string representing the variable
|
||||
* @returns {string} The JSON string representing the variable
|
||||
*/
|
||||
gdjs.evtTools.network.variableStructureToJSON = function(variable)
|
||||
{
|
||||
if ( !variable.isStructure() ) {
|
||||
if ( variable.isNumber() )
|
||||
return JSON.stringify(variable.getAsNumber());
|
||||
else
|
||||
return JSON.stringify(variable.getAsString());
|
||||
gdjs.evtTools.network.variableStructureToJSON = function (variable) {
|
||||
if (!variable.isStructure()) {
|
||||
if (variable.isNumber()) return JSON.stringify(variable.getAsNumber());
|
||||
else return JSON.stringify(variable.getAsString());
|
||||
}
|
||||
|
||||
var str = '{';
|
||||
var firstChild = true;
|
||||
var children = variable.getAllChildren();
|
||||
for (var p in children) {
|
||||
if (children.hasOwnProperty(p)) {
|
||||
if (!firstChild) str += ',';
|
||||
str +=
|
||||
JSON.stringify(p) +
|
||||
': ' +
|
||||
gdjs.evtTools.network.variableStructureToJSON(children[p]);
|
||||
|
||||
firstChild = false;
|
||||
}
|
||||
}
|
||||
|
||||
var str = "{";
|
||||
var firstChild = true;
|
||||
var children = variable.getAllChildren();
|
||||
for(var p in children) {
|
||||
if (children.hasOwnProperty(p)) {
|
||||
if ( !firstChild ) str += ",";
|
||||
str += JSON.stringify(p) + ": " + gdjs.evtTools.network.variableStructureToJSON(children[p]);
|
||||
|
||||
firstChild = false;
|
||||
}
|
||||
}
|
||||
|
||||
str += "}";
|
||||
return str;
|
||||
str += '}';
|
||||
return str;
|
||||
};
|
||||
|
||||
gdjs.evtTools.network.objectVariableStructureToJSON = function(object, variable)
|
||||
{
|
||||
return gdjs.evtTools.network.variableStructureToJSON(variable);
|
||||
}
|
||||
gdjs.evtTools.network.objectVariableStructureToJSON = function (
|
||||
object,
|
||||
variable
|
||||
) {
|
||||
return gdjs.evtTools.network.variableStructureToJSON(variable);
|
||||
};
|
||||
|
||||
gdjs.evtTools.network._objectToVariable = function(obj, variable)
|
||||
{
|
||||
if(!isNaN(obj)) { //Number
|
||||
variable.setNumber(obj);
|
||||
}
|
||||
else if (typeof obj == 'string' || obj instanceof String) {
|
||||
variable.setString(obj);
|
||||
}
|
||||
else if ( Array.isArray(obj) ) {
|
||||
for(var i = 0;i<obj.length;++i) {
|
||||
gdjs.evtTools.network._objectToVariable(obj[i], variable.getChild(i.toString()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(var p in obj) {
|
||||
if (obj.hasOwnProperty(p)) {
|
||||
gdjs.evtTools.network._objectToVariable(obj[p], variable.getChild(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
gdjs.evtTools.network._objectToVariable = function (obj, variable) {
|
||||
if (!isNaN(obj)) {
|
||||
//Number
|
||||
variable.setNumber(obj);
|
||||
} else if (typeof obj == 'string' || obj instanceof String) {
|
||||
variable.setString(obj);
|
||||
} else if (Array.isArray(obj)) {
|
||||
for (var i = 0; i < obj.length; ++i) {
|
||||
gdjs.evtTools.network._objectToVariable(
|
||||
obj[i],
|
||||
variable.getChild(i.toString())
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for (var p in obj) {
|
||||
if (obj.hasOwnProperty(p)) {
|
||||
gdjs.evtTools.network._objectToVariable(obj[p], variable.getChild(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the given JSON and fill the content of the variable with it
|
||||
@@ -108,20 +169,22 @@ gdjs.evtTools.network._objectToVariable = function(obj, variable)
|
||||
* @param {gdjs.Variable} variable The variable where to put the parsed JSON
|
||||
* @returns {boolean} true if JSON was properly parsed
|
||||
*/
|
||||
gdjs.evtTools.network.jsonToVariableStructure = function(jsonStr, variable)
|
||||
{
|
||||
if ( jsonStr.length === 0 ) return false;
|
||||
try {
|
||||
var obj = JSON.parse(jsonStr);
|
||||
gdjs.evtTools.network._objectToVariable(obj, variable);
|
||||
return true;
|
||||
} catch(e) {
|
||||
//Do nothing iF JSON was not properly parsed;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
gdjs.evtTools.network.jsonToVariableStructure = function (jsonStr, variable) {
|
||||
if (jsonStr.length === 0) return false;
|
||||
try {
|
||||
var obj = JSON.parse(jsonStr);
|
||||
gdjs.evtTools.network._objectToVariable(obj, variable);
|
||||
return true;
|
||||
} catch (e) {
|
||||
//Do nothing iF JSON was not properly parsed;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.evtTools.network.jsonToObjectVariableStructure = function(jsonStr, object, variable)
|
||||
{
|
||||
gdjs.evtTools.network.jsonToVariableStructure(jsonStr, variable);
|
||||
}
|
||||
gdjs.evtTools.network.jsonToObjectVariableStructure = function (
|
||||
jsonStr,
|
||||
object,
|
||||
variable
|
||||
) {
|
||||
gdjs.evtTools.network.jsonToVariableStructure(jsonStr, variable);
|
||||
};
|
||||
|
@@ -173,44 +173,48 @@ gdjs.evtTools.object.pickObjectsIf = function(predicate, objectsLists, negatePre
|
||||
var lists = gdjs.staticArray(gdjs.evtTools.object.pickObjectsIf);
|
||||
objectsLists.values(lists);
|
||||
|
||||
//Create a boolean for each object
|
||||
for(var i = 0, leni = lists.length;i<leni;++i) {
|
||||
var arr = lists[i];
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
arr[k].pick = false;
|
||||
}
|
||||
}
|
||||
|
||||
//Pick only objects that are fulfilling the predicate
|
||||
// Pick only objects that are fulfilling the predicate.
|
||||
for(var i = 0, leni = lists.length;i<leni;++i) {
|
||||
var arr = lists[i];
|
||||
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
if (negatePredicate ^ predicate(arr[k], extraArg)) {
|
||||
var object = arr[k];
|
||||
if (negatePredicate ^ predicate(object, extraArg)) {
|
||||
isTrue = true;
|
||||
arr[k].pick = true; //Pick the objects
|
||||
object.pick = true;
|
||||
} else {
|
||||
object.pick = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Trim not picked objects from lists.
|
||||
// Trim not picked objects from lists.
|
||||
for(var i = 0, leni = lists.length;i<leni;++i) {
|
||||
var arr = lists[i];
|
||||
var finalSize = 0;
|
||||
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
var obj = arr[k];
|
||||
if ( arr[k].pick ) {
|
||||
arr[finalSize] = obj;
|
||||
finalSize++;
|
||||
}
|
||||
}
|
||||
arr.length = finalSize;
|
||||
gdjs.evtTools.object.filterPickedObjectsList(lists[i]);
|
||||
}
|
||||
|
||||
return isTrue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter in-place the specified array to remove objects for which
|
||||
* `pick` property is set to false.
|
||||
* @param {gdjs.RuntimeObject[]} arr
|
||||
*/
|
||||
gdjs.evtTools.object.filterPickedObjectsList = function (arr) {
|
||||
var finalSize = 0;
|
||||
|
||||
for (var k = 0, lenk = arr.length; k < lenk; ++k) {
|
||||
var obj = arr[k];
|
||||
if (obj.pick) {
|
||||
arr[finalSize] = obj;
|
||||
finalSize++;
|
||||
}
|
||||
}
|
||||
|
||||
arr.length = finalSize;
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.hitBoxesCollisionTest = function(objectsLists1, objectsLists2, inverted, runtimeScene, ignoreTouchingEdges) {
|
||||
return gdjs.evtTools.object.twoListsTest(gdjs.RuntimeObject.collisionTest,
|
||||
objectsLists1, objectsLists2, inverted, ignoreTouchingEdges);
|
||||
|
@@ -159,5 +159,7 @@ gdjs.evtTools.runtimeScene.createObjectsFromExternalLayout = function(scene, ext
|
||||
var externalLayoutData = scene.getGame().getExternalLayoutData(externalLayout);
|
||||
if ( externalLayoutData === null ) return;
|
||||
|
||||
scene.createObjectsFrom(externalLayoutData.instances, xPos, yPos);
|
||||
// trackByPersistentUuid is set to false as we don't want external layouts
|
||||
// instantiated at runtime to be hot-reloaded.
|
||||
scene.createObjectsFrom(externalLayoutData.instances, xPos, yPos, /*trackByPersistentUuid=*/ false);
|
||||
};
|
||||
|
@@ -29,6 +29,15 @@ gdjs.FontFaceObserverFontManager = function (resources) {
|
||||
|
||||
gdjs.FontManager = gdjs.FontFaceObserverFontManager; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param {ResourceData[]} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager.prototype.setResources = function(resources) {
|
||||
this._resources = resources;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the font family associated to the specified font resource name.
|
||||
* The font resource must have been loaded before. If that's not the case,
|
||||
@@ -163,6 +172,10 @@ gdjs.FontFaceObserverFontManager.prototype.loadFonts = function (
|
||||
for (var i = 0, len = resources.length; i < len; ++i) {
|
||||
var res = resources[i];
|
||||
if (res.file && res.kind === 'font') {
|
||||
if (!!this._loadedFonts[res.name]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
filesResources[res.file] = filesResources[res.file]
|
||||
? filesResources[res.file].concat(res)
|
||||
: [res];
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// @ts-check
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
@@ -8,26 +9,29 @@
|
||||
* The `gdjs` namespace contains all classes and objects of the game engine.
|
||||
* @namespace
|
||||
*/
|
||||
window.gdjs = {
|
||||
objectsTypes: new Hashtable(),
|
||||
behaviorsTypes: new Hashtable(),
|
||||
/**
|
||||
* Contains functions used by events (this is a convention only, functions can actually
|
||||
* be anywhere).
|
||||
* @namespace
|
||||
* @memberOf gdjs
|
||||
*/
|
||||
evtTools: {},
|
||||
callbacksFirstRuntimeSceneLoaded: [],
|
||||
callbacksRuntimeSceneLoaded: [],
|
||||
callbacksRuntimeScenePreEvents: [],
|
||||
callbacksRuntimeScenePostEvents: [],
|
||||
callbacksRuntimeScenePaused: [],
|
||||
callbacksRuntimeSceneResumed: [],
|
||||
callbacksRuntimeSceneUnloading: [],
|
||||
callbacksRuntimeSceneUnloaded: [],
|
||||
callbacksObjectDeletedFromScene: [],
|
||||
};
|
||||
// @ts-ignore - creating the global object acting as a namespace
|
||||
window.gdjs = {};
|
||||
|
||||
/**
|
||||
* Contains functions used by events (this is a convention only, functions can actually
|
||||
* be anywhere).
|
||||
* @namespace
|
||||
* @memberOf gdjs
|
||||
*/
|
||||
gdjs.evtTools = {};
|
||||
|
||||
gdjs.objectsTypes = new Hashtable();
|
||||
gdjs.behaviorsTypes = new Hashtable();
|
||||
|
||||
/** @type {Function[]} */ gdjs.callbacksFirstRuntimeSceneLoaded = [];
|
||||
/** @type {Function[]} */ gdjs.callbacksRuntimeSceneLoaded = [];
|
||||
/** @type {Function[]} */ gdjs.callbacksRuntimeScenePreEvents = [];
|
||||
/** @type {Function[]} */ gdjs.callbacksRuntimeScenePostEvents = [];
|
||||
/** @type {Function[]} */ gdjs.callbacksRuntimeScenePaused = [];
|
||||
/** @type {Function[]} */ gdjs.callbacksRuntimeSceneResumed = [];
|
||||
/** @type {Function[]} */ gdjs.callbacksRuntimeSceneUnloading = [];
|
||||
/** @type {Function[]} */ gdjs.callbacksRuntimeSceneUnloaded = [];
|
||||
/** @type {Function[]} */ gdjs.callbacksObjectDeletedFromScene = [];
|
||||
|
||||
/**
|
||||
* Convert a rgb color value to a hex string.
|
||||
@@ -38,7 +42,7 @@ window.gdjs = {
|
||||
* @param {number} b Blue
|
||||
* @returns {string}
|
||||
*/
|
||||
gdjs.rgbToHex = function(r, g, b) {
|
||||
gdjs.rgbToHex = function (r, g, b) {
|
||||
return '' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
};
|
||||
|
||||
@@ -49,7 +53,7 @@ gdjs.rgbToHex = function(r, g, b) {
|
||||
* @param {number} b Blue
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.rgbToHexNumber = function(r, g, b) {
|
||||
gdjs.rgbToHexNumber = function (r, g, b) {
|
||||
return (r << 16) + (g << 8) + b;
|
||||
};
|
||||
|
||||
@@ -58,7 +62,7 @@ gdjs.rgbToHexNumber = function(r, g, b) {
|
||||
* @param {number} max The maximum value (inclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.random = function(max) {
|
||||
gdjs.random = function (max) {
|
||||
if (max <= 0) return 0;
|
||||
return Math.floor(Math.random() * (max + 1));
|
||||
};
|
||||
@@ -69,7 +73,7 @@ gdjs.random = function(max) {
|
||||
* @param {number} max The maximum value (inclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.randomInRange = function(min, max) {
|
||||
gdjs.randomInRange = function (min, max) {
|
||||
return min + gdjs.random(max - min); // return min if min >= max
|
||||
};
|
||||
|
||||
@@ -78,7 +82,7 @@ gdjs.randomInRange = function(min, max) {
|
||||
* @param {number} max The maximum value (exclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.randomFloat = function(max) {
|
||||
gdjs.randomFloat = function (max) {
|
||||
if (max <= 0) return 0;
|
||||
return Math.random() * max;
|
||||
};
|
||||
@@ -89,7 +93,7 @@ gdjs.randomFloat = function(max) {
|
||||
* @param {number} max The maximum value (exclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.randomFloatInRange = function(min, max) {
|
||||
gdjs.randomFloatInRange = function (min, max) {
|
||||
return min + gdjs.randomFloat(max - min); // return min if min >= max
|
||||
};
|
||||
|
||||
@@ -100,7 +104,7 @@ gdjs.randomFloatInRange = function(min, max) {
|
||||
* @param {number} step The interval between each value.
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.randomWithStep = function(min, max, step) {
|
||||
gdjs.randomWithStep = function (min, max, step) {
|
||||
if (step <= 0) return min + gdjs.random(max - min);
|
||||
return min + gdjs.random(Math.floor((max - min) / step)) * step; // return min if min >= max
|
||||
};
|
||||
@@ -110,7 +114,7 @@ gdjs.randomWithStep = function(min, max, step) {
|
||||
* @param {number} angleInDegrees The angle in degrees.
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.toRad = function(angleInDegrees) {
|
||||
gdjs.toRad = function (angleInDegrees) {
|
||||
return (angleInDegrees / 180) * 3.14159;
|
||||
};
|
||||
|
||||
@@ -119,18 +123,10 @@ gdjs.toRad = function(angleInDegrees) {
|
||||
* @param {number} angleInRadians The angle in radians.
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.toDegrees = function(angleInRadians) {
|
||||
gdjs.toDegrees = function (angleInRadians) {
|
||||
return (angleInRadians * 180) / 3.14159;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Constructor for a {@link gdjs.RuntimeObject}.
|
||||
* @name RuntimeObjectConstructor
|
||||
* @function
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The {@link gdjs.RuntimeScene} the object belongs to.
|
||||
* @param {ObjectData} objectData The initial properties of the object.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register a runtime object (class extending {@link gdjs.RuntimeObject}) that can be used in a scene.
|
||||
*
|
||||
@@ -139,21 +135,12 @@ gdjs.toDegrees = function(angleInRadians) {
|
||||
* of the type of the object is "TextObject::Text".
|
||||
*
|
||||
* @param {string} objectTypeName The name of the type of the Object.
|
||||
* @param {RuntimeObjectConstructor} Ctor The constructor of the Object.
|
||||
* @param {typeof gdjs.RuntimeObject} Ctor The constructor of the Object.
|
||||
*/
|
||||
gdjs.registerObject = function(objectTypeName, Ctor) {
|
||||
gdjs.registerObject = function (objectTypeName, Ctor) {
|
||||
gdjs.objectsTypes.put(objectTypeName, Ctor);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Constructor for a {@link gdjs.RuntimeBehavior}.
|
||||
* @name RuntimeBehaviorConstructor
|
||||
* @function
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene owning the object of the behavior
|
||||
* @param {BehaviorData} behaviorData The properties used to setup the behavior
|
||||
* @param {gdjs.RuntimeObject} owner The object owning the behavior
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register a runtime behavior (class extending {@link gdjs.RuntimeBehavior}) that can be used by a
|
||||
* {@link gdjs.RuntimeObject}.
|
||||
@@ -163,9 +150,9 @@ gdjs.registerObject = function(objectTypeName, Ctor) {
|
||||
* the full name of the type of the behavior is "DraggableBehavior::Draggable".
|
||||
*
|
||||
* @param {string} behaviorTypeName The name of the type of the behavior.
|
||||
* @param {RuntimeBehaviorConstructor} Ctor The constructor of the Object.
|
||||
* @param {typeof gdjs.RuntimeBehavior} Ctor The constructor of the Object.
|
||||
*/
|
||||
gdjs.registerBehavior = function(behaviorTypeName, Ctor) {
|
||||
gdjs.registerBehavior = function (behaviorTypeName, Ctor) {
|
||||
gdjs.behaviorsTypes.put(behaviorTypeName, Ctor);
|
||||
};
|
||||
|
||||
@@ -175,7 +162,7 @@ gdjs.registerBehavior = function(behaviorTypeName, Ctor) {
|
||||
*
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerFirstRuntimeSceneLoadedCallback = function(callback) {
|
||||
gdjs.registerFirstRuntimeSceneLoadedCallback = function (callback) {
|
||||
gdjs.callbacksFirstRuntimeSceneLoaded.push(callback);
|
||||
};
|
||||
|
||||
@@ -183,7 +170,7 @@ gdjs.registerFirstRuntimeSceneLoadedCallback = function(callback) {
|
||||
* Register a function to be called when a scene is loaded.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeSceneLoadedCallback = function(callback) {
|
||||
gdjs.registerRuntimeSceneLoadedCallback = function (callback) {
|
||||
gdjs.callbacksRuntimeSceneLoaded.push(callback);
|
||||
};
|
||||
|
||||
@@ -192,7 +179,7 @@ gdjs.registerRuntimeSceneLoadedCallback = function(callback) {
|
||||
* before events are run.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeScenePreEventsCallback = function(callback) {
|
||||
gdjs.registerRuntimeScenePreEventsCallback = function (callback) {
|
||||
gdjs.callbacksRuntimeScenePreEvents.push(callback);
|
||||
};
|
||||
|
||||
@@ -201,7 +188,7 @@ gdjs.registerRuntimeScenePreEventsCallback = function(callback) {
|
||||
* after events are run and before rendering.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeScenePostEventsCallback = function(callback) {
|
||||
gdjs.registerRuntimeScenePostEventsCallback = function (callback) {
|
||||
gdjs.callbacksRuntimeScenePostEvents.push(callback);
|
||||
};
|
||||
|
||||
@@ -209,7 +196,7 @@ gdjs.registerRuntimeScenePostEventsCallback = function(callback) {
|
||||
* Register a function to be called when a scene is paused.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeScenePausedCallback = function(callback) {
|
||||
gdjs.registerRuntimeScenePausedCallback = function (callback) {
|
||||
gdjs.callbacksRuntimeScenePaused.push(callback);
|
||||
};
|
||||
|
||||
@@ -217,7 +204,7 @@ gdjs.registerRuntimeScenePausedCallback = function(callback) {
|
||||
* Register a function to be called when a scene is resumed.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeSceneResumedCallback = function(callback) {
|
||||
gdjs.registerRuntimeSceneResumedCallback = function (callback) {
|
||||
gdjs.callbacksRuntimeSceneResumed.push(callback);
|
||||
};
|
||||
|
||||
@@ -229,7 +216,7 @@ gdjs.registerRuntimeSceneResumedCallback = function(callback) {
|
||||
*
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeSceneUnloadingCallback = function(callback) {
|
||||
gdjs.registerRuntimeSceneUnloadingCallback = function (callback) {
|
||||
gdjs.callbacksRuntimeSceneUnloading.push(callback);
|
||||
};
|
||||
|
||||
@@ -240,7 +227,7 @@ gdjs.registerRuntimeSceneUnloadingCallback = function(callback) {
|
||||
*
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeSceneUnloadedCallback = function(callback) {
|
||||
gdjs.registerRuntimeSceneUnloadedCallback = function (callback) {
|
||||
gdjs.callbacksRuntimeSceneUnloaded.push(callback);
|
||||
};
|
||||
|
||||
@@ -248,7 +235,7 @@ gdjs.registerRuntimeSceneUnloadedCallback = function(callback) {
|
||||
* Register a function to be called when an object is deleted from a scene.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerObjectDeletedFromSceneCallback = function(callback) {
|
||||
gdjs.registerObjectDeletedFromSceneCallback = function (callback) {
|
||||
gdjs.callbacksObjectDeletedFromScene.push(callback);
|
||||
};
|
||||
|
||||
@@ -257,8 +244,8 @@ gdjs.registerObjectDeletedFromSceneCallback = function(callback) {
|
||||
* @deprecated
|
||||
* @private
|
||||
*/
|
||||
gdjs.registerGlobalCallbacks = function() {
|
||||
console.warning(
|
||||
gdjs.registerGlobalCallbacks = function () {
|
||||
console.warn(
|
||||
"You're calling gdjs.registerGlobalCallbacks. This method is now useless and you must not call it anymore."
|
||||
);
|
||||
};
|
||||
@@ -268,7 +255,7 @@ gdjs.registerGlobalCallbacks = function() {
|
||||
*
|
||||
* Should only be used for testing - this should never be used at runtime.
|
||||
*/
|
||||
gdjs.clearGlobalCallbacks = function() {
|
||||
gdjs.clearGlobalCallbacks = function () {
|
||||
gdjs.callbacksFirstRuntimeSceneLoaded = [];
|
||||
gdjs.callbacksRuntimeSceneLoaded = [];
|
||||
gdjs.callbacksRuntimeScenePreEvents = [];
|
||||
@@ -284,9 +271,9 @@ gdjs.clearGlobalCallbacks = function() {
|
||||
* Get the constructor of an object.
|
||||
*
|
||||
* @param {string} name The name of the type of the object.
|
||||
* @returns {ObjectCtor}
|
||||
* @returns {typeof gdjs.RuntimeObject}
|
||||
*/
|
||||
gdjs.getObjectConstructor = function(name) {
|
||||
gdjs.getObjectConstructor = function (name) {
|
||||
if (name !== undefined && gdjs.objectsTypes.containsKey(name))
|
||||
return gdjs.objectsTypes.get(name);
|
||||
|
||||
@@ -298,9 +285,9 @@ gdjs.getObjectConstructor = function(name) {
|
||||
* Get the constructor of a behavior.
|
||||
*
|
||||
* @param {string} name The name of the type of the behavior.
|
||||
* @returns {BehaviorCtor}
|
||||
* @returns {typeof gdjs.RuntimeBehavior}
|
||||
*/
|
||||
gdjs.getBehaviorConstructor = function(name) {
|
||||
gdjs.getBehaviorConstructor = function (name) {
|
||||
if (name !== undefined && gdjs.behaviorsTypes.containsKey(name))
|
||||
return gdjs.behaviorsTypes.get(name);
|
||||
|
||||
@@ -313,7 +300,7 @@ gdjs.getBehaviorConstructor = function(name) {
|
||||
* @param {any} owner The owner of the Array.
|
||||
* @returns {Array<any>}
|
||||
*/
|
||||
gdjs.staticArray = function(owner) {
|
||||
gdjs.staticArray = function (owner) {
|
||||
owner._staticArray = owner._staticArray || [];
|
||||
return owner._staticArray;
|
||||
};
|
||||
@@ -323,7 +310,7 @@ gdjs.staticArray = function(owner) {
|
||||
* @param {any} owner The owner of the Array.
|
||||
* @returns {Array<any>}
|
||||
*/
|
||||
gdjs.staticArray2 = function(owner) {
|
||||
gdjs.staticArray2 = function (owner) {
|
||||
owner._staticArray2 = owner._staticArray2 || [];
|
||||
return owner._staticArray2;
|
||||
};
|
||||
@@ -333,7 +320,7 @@ gdjs.staticArray2 = function(owner) {
|
||||
* @param {any} owner The owner of the Array.
|
||||
* @returns {Object}
|
||||
*/
|
||||
gdjs.staticObject = function(owner) {
|
||||
gdjs.staticObject = function (owner) {
|
||||
owner._staticObject = owner._staticObject || {};
|
||||
return owner._staticObject;
|
||||
};
|
||||
@@ -344,7 +331,7 @@ gdjs.staticObject = function(owner) {
|
||||
* @param objectsLists
|
||||
* @returns {Array}
|
||||
*/
|
||||
gdjs.objectsListsToArray = function(objectsLists) {
|
||||
gdjs.objectsListsToArray = function (objectsLists) {
|
||||
var lists = gdjs.staticArray(gdjs.objectsListsToArray);
|
||||
objectsLists.values(lists);
|
||||
|
||||
@@ -358,20 +345,18 @@ gdjs.objectsListsToArray = function(objectsLists) {
|
||||
return result;
|
||||
};
|
||||
|
||||
Array.prototype.remove = function(from) {
|
||||
//Adapted from the nice article available at
|
||||
//https://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascript
|
||||
for (var i = from, len = this.length - 1; i < len; i++) this[i] = this[i + 1];
|
||||
|
||||
this.length = len;
|
||||
};
|
||||
|
||||
Array.prototype.createFrom = function(arr) {
|
||||
var len = arr.length;
|
||||
/**
|
||||
* Copy the element for the first array into the second array, so that
|
||||
* both array contains the same elements.
|
||||
* @param {Array<any>} src The source array
|
||||
* @param {Array<any>} dst The destination array
|
||||
*/
|
||||
gdjs.copyArray = function (src, dst) {
|
||||
var len = src.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
this[i] = arr[i];
|
||||
dst[i] = src[i];
|
||||
}
|
||||
this.length = len;
|
||||
dst.length = len;
|
||||
};
|
||||
|
||||
//Make sure console.warn and console.error are available.
|
||||
|
@@ -14,7 +14,7 @@
|
||||
* @memberof gdjs
|
||||
* @class HowlerSound
|
||||
*/
|
||||
gdjs.HowlerSound = function(o) {
|
||||
gdjs.HowlerSound = function (o) {
|
||||
Howl.call(this, o);
|
||||
this._paused = false;
|
||||
this._stopped = true;
|
||||
@@ -24,14 +24,14 @@ gdjs.HowlerSound = function(o) {
|
||||
//Add custom events listener to keep
|
||||
//track of the sound status.
|
||||
var that = this;
|
||||
this.on('end', function() {
|
||||
this.on('end', function () {
|
||||
if (!that.loop()) {
|
||||
that._canBeDestroyed = true;
|
||||
that._paused = false;
|
||||
that._stopped = true;
|
||||
}
|
||||
});
|
||||
this.on('playerror', function(id, error) {
|
||||
this.on('playerror', function (id, error) {
|
||||
console.error(
|
||||
"Can't play a sound, considering it as stopped. Error is:",
|
||||
error
|
||||
@@ -44,11 +44,11 @@ gdjs.HowlerSound = function(o) {
|
||||
// sync'ed with the sound - though this should be redundant
|
||||
// with `play`/`pause` methods already doing that. Keeping
|
||||
// that to be sure that the status is always correct.
|
||||
this.on('play', function() {
|
||||
this.on('play', function () {
|
||||
that._paused = false;
|
||||
that._stopped = false;
|
||||
});
|
||||
this.on('pause', function() {
|
||||
this.on('pause', function () {
|
||||
that._paused = true;
|
||||
that._stopped = false;
|
||||
});
|
||||
@@ -59,17 +59,17 @@ gdjs.HowlerSound.prototype = Object.create(Howl.prototype);
|
||||
// is immediately updated (so that calling `stopped` just after
|
||||
// `play` will return false).
|
||||
|
||||
gdjs.HowlerSound.prototype.stop = function() {
|
||||
gdjs.HowlerSound.prototype.stop = function () {
|
||||
this._paused = false;
|
||||
this._stopped = true;
|
||||
return Howl.prototype.stop.call(this);
|
||||
};
|
||||
gdjs.HowlerSound.prototype.play = function() {
|
||||
gdjs.HowlerSound.prototype.play = function () {
|
||||
this._paused = false;
|
||||
this._stopped = false;
|
||||
return Howl.prototype.play.call(this);
|
||||
};
|
||||
gdjs.HowlerSound.prototype.pause = function() {
|
||||
gdjs.HowlerSound.prototype.pause = function () {
|
||||
this._paused = true;
|
||||
this._stopped = false;
|
||||
return Howl.prototype.pause.call(this);
|
||||
@@ -77,22 +77,22 @@ gdjs.HowlerSound.prototype.pause = function() {
|
||||
|
||||
// Add methods to query the status of the sound:
|
||||
|
||||
gdjs.HowlerSound.prototype.paused = function() {
|
||||
gdjs.HowlerSound.prototype.paused = function () {
|
||||
return this._paused;
|
||||
};
|
||||
gdjs.HowlerSound.prototype.stopped = function() {
|
||||
gdjs.HowlerSound.prototype.stopped = function () {
|
||||
return this._stopped;
|
||||
};
|
||||
gdjs.HowlerSound.prototype.canBeDestroyed = function() {
|
||||
gdjs.HowlerSound.prototype.canBeDestroyed = function () {
|
||||
return this._canBeDestroyed;
|
||||
};
|
||||
|
||||
// Methods to safely update the rate of the sound:
|
||||
|
||||
gdjs.HowlerSound.prototype.getRate = function() {
|
||||
gdjs.HowlerSound.prototype.getRate = function () {
|
||||
return this._rate;
|
||||
};
|
||||
gdjs.HowlerSound.prototype.setRate = function(rate) {
|
||||
gdjs.HowlerSound.prototype.setRate = function (rate) {
|
||||
this._rate = gdjs.HowlerSoundManager.clampRate(rate);
|
||||
this.rate(this._rate);
|
||||
};
|
||||
@@ -106,7 +106,7 @@ gdjs.HowlerSound.prototype.setRate = function(rate) {
|
||||
* @memberof gdjs
|
||||
* @class HowlerSoundManager
|
||||
*/
|
||||
gdjs.HowlerSoundManager = function(resources) {
|
||||
gdjs.HowlerSoundManager = function (resources) {
|
||||
this._resources = resources;
|
||||
this._availableResources = {}; //Map storing "audio" resources for faster access.
|
||||
|
||||
@@ -121,18 +121,18 @@ gdjs.HowlerSoundManager = function(resources) {
|
||||
this._paused = false;
|
||||
|
||||
var that = this;
|
||||
this._checkForPause = function() {
|
||||
this._checkForPause = function () {
|
||||
if (that._paused) {
|
||||
this.pause();
|
||||
that._pausedSounds.push(this);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('deviceready', function() {
|
||||
document.addEventListener('deviceready', function () {
|
||||
// pause/resume sounds in Cordova when the app is being paused/resumed
|
||||
document.addEventListener(
|
||||
'pause',
|
||||
function() {
|
||||
function () {
|
||||
var soundList = that._freeSounds.concat(that._freeMusics);
|
||||
for (var key in that._sounds) {
|
||||
if (that._sounds.hasOwnProperty(key)) {
|
||||
@@ -157,7 +157,7 @@ gdjs.HowlerSoundManager = function(resources) {
|
||||
);
|
||||
document.addEventListener(
|
||||
'resume',
|
||||
function() {
|
||||
function () {
|
||||
for (var i = 0; i < that._pausedSounds.length; i++) {
|
||||
var sound = that._pausedSounds[i];
|
||||
if (!sound.stopped()) {
|
||||
@@ -174,12 +174,21 @@ gdjs.HowlerSoundManager = function(resources) {
|
||||
|
||||
gdjs.SoundManager = gdjs.HowlerSoundManager; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param {ResourceData[]} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.HowlerSoundManager.prototype.setResources = function (resources) {
|
||||
this._resources = resources;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure rate is in a range valid for Howler.js
|
||||
* @return The clamped rate
|
||||
* @private
|
||||
*/
|
||||
gdjs.HowlerSoundManager.clampRate = function(rate) {
|
||||
gdjs.HowlerSoundManager.clampRate = function (rate) {
|
||||
if (rate > 4.0) return 4.0;
|
||||
if (rate < 0.5) return 0.5;
|
||||
|
||||
@@ -196,7 +205,7 @@ gdjs.HowlerSoundManager.clampRate = function(rate) {
|
||||
* @private
|
||||
* @return The associated filename
|
||||
*/
|
||||
gdjs.HowlerSoundManager.prototype._getFileFromSoundName = function(soundName) {
|
||||
gdjs.HowlerSoundManager.prototype._getFileFromSoundName = function (soundName) {
|
||||
if (
|
||||
this._availableResources.hasOwnProperty(soundName) &&
|
||||
this._availableResources[soundName].file
|
||||
@@ -217,7 +226,7 @@ gdjs.HowlerSoundManager.prototype._getFileFromSoundName = function(soundName) {
|
||||
* @return The gdjs.HowlerSound that have been added (i.e: the second parameter).
|
||||
* @private
|
||||
*/
|
||||
gdjs.HowlerSoundManager.prototype._storeSoundInArray = function(arr, sound) {
|
||||
gdjs.HowlerSoundManager.prototype._storeSoundInArray = function (arr, sound) {
|
||||
//Try to recycle an old sound.
|
||||
var index = null;
|
||||
for (var i = 0, len = arr.length; i < len; ++i) {
|
||||
@@ -231,7 +240,7 @@ gdjs.HowlerSoundManager.prototype._storeSoundInArray = function(arr, sound) {
|
||||
return sound;
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.playSound = function(
|
||||
gdjs.HowlerSoundManager.prototype.playSound = function (
|
||||
soundName,
|
||||
loop,
|
||||
volume,
|
||||
@@ -251,7 +260,7 @@ gdjs.HowlerSoundManager.prototype.playSound = function(
|
||||
sound.on('play', this._checkForPause);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.playSoundOnChannel = function(
|
||||
gdjs.HowlerSoundManager.prototype.playSoundOnChannel = function (
|
||||
soundName,
|
||||
channel,
|
||||
loop,
|
||||
@@ -278,11 +287,11 @@ gdjs.HowlerSoundManager.prototype.playSoundOnChannel = function(
|
||||
sound.on('play', this._checkForPause);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.getSoundOnChannel = function(channel) {
|
||||
gdjs.HowlerSoundManager.prototype.getSoundOnChannel = function (channel) {
|
||||
return this._sounds[channel];
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.playMusic = function(
|
||||
gdjs.HowlerSoundManager.prototype.playMusic = function (
|
||||
soundName,
|
||||
loop,
|
||||
volume,
|
||||
@@ -303,7 +312,7 @@ gdjs.HowlerSoundManager.prototype.playMusic = function(
|
||||
sound.on('play', this._checkForPause);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.playMusicOnChannel = function(
|
||||
gdjs.HowlerSoundManager.prototype.playMusicOnChannel = function (
|
||||
soundName,
|
||||
channel,
|
||||
loop,
|
||||
@@ -331,22 +340,22 @@ gdjs.HowlerSoundManager.prototype.playMusicOnChannel = function(
|
||||
music.on('play', this._checkForPause);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.getMusicOnChannel = function(channel) {
|
||||
gdjs.HowlerSoundManager.prototype.getMusicOnChannel = function (channel) {
|
||||
return this._musics[channel];
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.setGlobalVolume = function(volume) {
|
||||
gdjs.HowlerSoundManager.prototype.setGlobalVolume = function (volume) {
|
||||
this._globalVolume = volume;
|
||||
if (this._globalVolume > 100) this._globalVolume = 100;
|
||||
if (this._globalVolume < 0) this._globalVolume = 0;
|
||||
Howler.volume(this._globalVolume / 100);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.getGlobalVolume = function() {
|
||||
gdjs.HowlerSoundManager.prototype.getGlobalVolume = function () {
|
||||
return this._globalVolume;
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.clearAll = function() {
|
||||
gdjs.HowlerSoundManager.prototype.clearAll = function () {
|
||||
for (var i = 0; i < this._freeSounds.length; ++i) {
|
||||
if (this._freeSounds[i]) this._freeSounds[i].unload();
|
||||
}
|
||||
@@ -371,7 +380,7 @@ gdjs.HowlerSoundManager.prototype.clearAll = function() {
|
||||
this._pausedSounds.length = 0;
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.preloadAudio = function(
|
||||
gdjs.HowlerSoundManager.prototype.preloadAudio = function (
|
||||
onProgress,
|
||||
onComplete,
|
||||
resources
|
||||
@@ -381,39 +390,42 @@ gdjs.HowlerSoundManager.prototype.preloadAudio = function(
|
||||
//Construct the list of files to be loaded.
|
||||
//For one loaded file, it can have one or more resources
|
||||
//that use it.
|
||||
var files = [];
|
||||
var files = {};
|
||||
for (var i = 0, len = resources.length; i < len; ++i) {
|
||||
var res = resources[i];
|
||||
if (res.file && res.kind === 'audio') {
|
||||
if (!!this._availableResources[res.name]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this._availableResources[res.name] = res;
|
||||
|
||||
if (files.indexOf(res.file) === -1) {
|
||||
files.push(res.file);
|
||||
}
|
||||
files[res.file] = (files[res.file] || []).concat(res);
|
||||
}
|
||||
}
|
||||
|
||||
if (files.length === 0) return onComplete(files.length);
|
||||
|
||||
var loaded = 0;
|
||||
function onLoad(audioFile) {
|
||||
loaded++;
|
||||
if (loaded === files.length) {
|
||||
return onComplete(files.length);
|
||||
}
|
||||
|
||||
onProgress(loaded, files.length);
|
||||
}
|
||||
var totalCount = Object.keys(files).length;
|
||||
if (totalCount === 0) return onComplete(totalCount); //Nothing to load.
|
||||
|
||||
var loadedCount = 0;
|
||||
var that = this;
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
(function(audioFile) {
|
||||
var sound = new XMLHttpRequest();
|
||||
sound.addEventListener('load', onLoad.bind(that, audioFile));
|
||||
sound.addEventListener('error', onLoad.bind(that, audioFile));
|
||||
sound.addEventListener('abort', onLoad.bind(that, audioFile));
|
||||
sound.open('GET', audioFile);
|
||||
sound.send();
|
||||
})(files[i]);
|
||||
function onLoad() {
|
||||
loadedCount++;
|
||||
if (loadedCount === totalCount) {
|
||||
return onComplete(totalCount);
|
||||
}
|
||||
|
||||
onProgress(loadedCount, totalCount);
|
||||
}
|
||||
|
||||
for (var file in files) {
|
||||
if (files.hasOwnProperty(file)) {
|
||||
var httpRequest = new XMLHttpRequest();
|
||||
httpRequest.addEventListener('load', onLoad);
|
||||
httpRequest.addEventListener('error', onLoad);
|
||||
httpRequest.addEventListener('abort', onLoad);
|
||||
httpRequest.open('GET', file);
|
||||
httpRequest.send();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// @ts-check
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-present Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
@@ -16,13 +17,22 @@
|
||||
* @memberof gdjs
|
||||
* @param {ResourceData[]} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.JsonManager = function(resources) {
|
||||
gdjs.JsonManager = function (resources) {
|
||||
this._resources = resources;
|
||||
|
||||
/** @type Object.<string, Object> */
|
||||
this._loadedJsons = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param {ResourceData[]} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.setResources = function (resources) {
|
||||
this._resources = resources;
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback called when a json is preloaded
|
||||
* @callback JsonManagerOnProgressCallback
|
||||
@@ -41,30 +51,33 @@ gdjs.JsonManager = function(resources) {
|
||||
/**
|
||||
* Request all the json resources to be preloaded (unless they are marked as not preloaded).
|
||||
*
|
||||
* Note that even if a JSON is already loaded, it will be reloaded (useful for hot-reloading,
|
||||
* as JSON files can have been modified without the editor knowing).
|
||||
*
|
||||
* @param {JsonManagerOnProgressCallback} onProgress The function called after each json is loaded.
|
||||
* @param {JsonManagerOnCompleteCallback} onComplete The function called when all jsons are loaded.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.preloadJsons = function(onProgress, onComplete) {
|
||||
gdjs.JsonManager.prototype.preloadJsons = function (onProgress, onComplete) {
|
||||
var resources = this._resources;
|
||||
|
||||
var jsonResources = resources.filter(function(resource) {
|
||||
var jsonResources = resources.filter(function (resource) {
|
||||
return resource.kind === 'json' && !resource.disablePreload;
|
||||
});
|
||||
if (jsonResources.length === 0) return onComplete(jsonResources.length);
|
||||
|
||||
var loaded = 0;
|
||||
/** @type JsonManagerRequestCallback */
|
||||
var onLoad = function(error, jsonContent) {
|
||||
var onLoad = function (error) {
|
||||
if (error) {
|
||||
console.error('Error while preloading a json resource:' + error);
|
||||
}
|
||||
|
||||
loaded++;
|
||||
if (loaded === jsonResources.length) {
|
||||
return onComplete(jsonResources.length);
|
||||
onComplete(jsonResources.length);
|
||||
} else {
|
||||
onProgress(loaded, jsonResources.length);
|
||||
}
|
||||
|
||||
onProgress(loaded, jsonResources.length);
|
||||
};
|
||||
|
||||
for (var i = 0; i < jsonResources.length; ++i) {
|
||||
@@ -77,7 +90,7 @@ gdjs.JsonManager.prototype.preloadJsons = function(onProgress, onComplete) {
|
||||
* @callback JsonManagerRequestCallback
|
||||
* @param {?Error} error The error, if any. `null` otherwise.
|
||||
* @param {?Object} jsonContent The content of the json file (or null if an error occured).
|
||||
* @returns {undefined} Nothing
|
||||
* @returns {void} Nothing
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -88,8 +101,9 @@ gdjs.JsonManager.prototype.preloadJsons = function(onProgress, onComplete) {
|
||||
* @param {string} resourceName The resource pointing to the json file to load.
|
||||
* @param {JsonManagerRequestCallback} callback The callback function called when json is loaded (or an error occured).
|
||||
*/
|
||||
gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
|
||||
var resource = this._resources.find(function(resource) {
|
||||
gdjs.JsonManager.prototype.loadJson = function (resourceName, callback) {
|
||||
// @ts-ignore - find is not ES5
|
||||
var resource = this._resources.find(function (resource) {
|
||||
return resource.kind === 'json' && resource.name === resourceName;
|
||||
});
|
||||
if (!resource) {
|
||||
@@ -114,7 +128,7 @@ gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
xhr.open('GET', resource.file);
|
||||
xhr.onload = function() {
|
||||
xhr.onload = function () {
|
||||
if (xhr.status !== 200) {
|
||||
callback(
|
||||
new Error('HTTP error: ' + xhr.status + '(' + xhr.statusText + ')'),
|
||||
@@ -128,10 +142,10 @@ gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
|
||||
|
||||
callback(null, xhr.response);
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
xhr.onerror = function () {
|
||||
callback(new Error('Network error'), null);
|
||||
};
|
||||
xhr.onabort = function() {
|
||||
xhr.onabort = function () {
|
||||
callback(new Error('Request aborted'), null);
|
||||
};
|
||||
xhr.send();
|
||||
@@ -142,7 +156,7 @@ gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
|
||||
* @param {string} resourceName The name of the json resource.
|
||||
* @returns {boolean} true if the content of the json resource is loaded. false otherwise.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.isJsonLoaded = function(resourceName) {
|
||||
gdjs.JsonManager.prototype.isJsonLoaded = function (resourceName) {
|
||||
return !!this._loadedJsons[resourceName];
|
||||
};
|
||||
|
||||
@@ -153,6 +167,6 @@ gdjs.JsonManager.prototype.isJsonLoaded = function(resourceName) {
|
||||
* @param {string} resourceName The name of the json resource.
|
||||
* @returns {?Object} the content of the json resource, if loaded. `null` otherwise.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.getLoadedJson = function(resourceName) {
|
||||
gdjs.JsonManager.prototype.getLoadedJson = function (resourceName) {
|
||||
return this._loadedJsons[resourceName] || null;
|
||||
};
|
||||
|
@@ -15,13 +15,13 @@
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene in which the layer is used
|
||||
* @memberof gdjs
|
||||
*/
|
||||
gdjs.Layer = function(layerData, runtimeScene) {
|
||||
gdjs.Layer = function (layerData, runtimeScene) {
|
||||
this._name = layerData.name;
|
||||
this._cameraRotation = 0;
|
||||
this._zoomFactor = 1;
|
||||
this._timeScale = 1;
|
||||
this._hidden = !layerData.visibility;
|
||||
this._effectsData = layerData.effects || [];
|
||||
this._initialEffectsData = layerData.effects || [];
|
||||
this._cameraX = runtimeScene.getGame().getGameResolutionWidth() / 2;
|
||||
this._cameraY = runtimeScene.getGame().getGameResolutionHeight() / 2;
|
||||
this._cachedGameResolutionWidth = runtimeScene
|
||||
@@ -32,13 +32,26 @@ gdjs.Layer = function(layerData, runtimeScene) {
|
||||
.getGameResolutionHeight();
|
||||
this._runtimeScene = runtimeScene;
|
||||
|
||||
// Lighting layer properties.
|
||||
this._isLightingLayer = layerData.isLightingLayer;
|
||||
this._followBaseLayerCamera = layerData.followBaseLayerCamera;
|
||||
this._clearColor = [
|
||||
layerData.ambientLightColorR / 255,
|
||||
layerData.ambientLightColorG / 255,
|
||||
layerData.ambientLightColorB / 255,
|
||||
1.0,
|
||||
];
|
||||
|
||||
// @ts-ignore - assume the proper renderer is passed
|
||||
this._renderer = new gdjs.LayerRenderer(this, runtimeScene.getRenderer());
|
||||
this.show(!this._hidden);
|
||||
this._setEffectsDefaultParameters();
|
||||
|
||||
for (var i = 0; i < layerData.effects.length; ++i) {
|
||||
this.addEffect(layerData.effects[i]);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.getRenderer = function() {
|
||||
gdjs.Layer.prototype.getRenderer = function () {
|
||||
return this._renderer;
|
||||
};
|
||||
|
||||
@@ -46,7 +59,7 @@ gdjs.Layer.prototype.getRenderer = function() {
|
||||
* Called by the RuntimeScene whenever the game resolution size is changed.
|
||||
* Updates the layer width/height and position.
|
||||
*/
|
||||
gdjs.Layer.prototype.onGameResolutionResized = function() {
|
||||
gdjs.Layer.prototype.onGameResolutionResized = function () {
|
||||
var oldGameResolutionWidth = this._cachedGameResolutionWidth;
|
||||
var oldGameResolutionHeight = this._cachedGameResolutionHeight;
|
||||
this._cachedGameResolutionWidth = this._runtimeScene
|
||||
@@ -74,7 +87,7 @@ gdjs.Layer.prototype.onGameResolutionResized = function() {
|
||||
* Returns the scene the layer belongs to
|
||||
* @returns {gdjs.RuntimeScene} the scene the layer belongs to
|
||||
*/
|
||||
gdjs.Layer.prototype.getRuntimeScene = function() {
|
||||
gdjs.Layer.prototype.getRuntimeScene = function () {
|
||||
return this._runtimeScene;
|
||||
};
|
||||
|
||||
@@ -82,15 +95,16 @@ gdjs.Layer.prototype.getRuntimeScene = function() {
|
||||
* Called at each frame, after events are run and before rendering.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene the layer belongs to.
|
||||
*/
|
||||
gdjs.Layer.prototype.update = function(runtimeScene) {
|
||||
return this._renderer.updateTime();
|
||||
gdjs.Layer.prototype.update = function (runtimeScene) {
|
||||
if (this._followBaseLayerCamera) this.followBaseLayer();
|
||||
return this._renderer.update();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of the layer
|
||||
* @return {String} The name of the layer
|
||||
*/
|
||||
gdjs.Layer.prototype.getName = function() {
|
||||
gdjs.Layer.prototype.getName = function () {
|
||||
return this._name;
|
||||
};
|
||||
|
||||
@@ -100,7 +114,7 @@ gdjs.Layer.prototype.getName = function() {
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return The x position of the camera
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraX = function(cameraId) {
|
||||
gdjs.Layer.prototype.getCameraX = function (cameraId) {
|
||||
return this._cameraX;
|
||||
};
|
||||
|
||||
@@ -110,7 +124,7 @@ gdjs.Layer.prototype.getCameraX = function(cameraId) {
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return The y position of the camera
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraY = function(cameraId) {
|
||||
gdjs.Layer.prototype.getCameraY = function (cameraId) {
|
||||
return this._cameraY;
|
||||
};
|
||||
|
||||
@@ -120,7 +134,7 @@ gdjs.Layer.prototype.getCameraY = function(cameraId) {
|
||||
* @param {number} x The new x position
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.setCameraX = function(x, cameraId) {
|
||||
gdjs.Layer.prototype.setCameraX = function (x, cameraId) {
|
||||
this._cameraX = x;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
@@ -131,7 +145,7 @@ gdjs.Layer.prototype.setCameraX = function(x, cameraId) {
|
||||
* @param {number} y The new y position
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.setCameraY = function(y, cameraId) {
|
||||
gdjs.Layer.prototype.setCameraY = function (y, cameraId) {
|
||||
this._cameraY = y;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
@@ -143,7 +157,7 @@ gdjs.Layer.prototype.setCameraY = function(y, cameraId) {
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return {number} The width of the camera
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraWidth = function(cameraId) {
|
||||
gdjs.Layer.prototype.getCameraWidth = function (cameraId) {
|
||||
return (+this._cachedGameResolutionWidth * 1) / this._zoomFactor;
|
||||
};
|
||||
|
||||
@@ -154,7 +168,7 @@ gdjs.Layer.prototype.getCameraWidth = function(cameraId) {
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return {number} The height of the camera
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraHeight = function(cameraId) {
|
||||
gdjs.Layer.prototype.getCameraHeight = function (cameraId) {
|
||||
return (+this._cachedGameResolutionHeight * 1) / this._zoomFactor;
|
||||
};
|
||||
|
||||
@@ -162,7 +176,7 @@ gdjs.Layer.prototype.getCameraHeight = function(cameraId) {
|
||||
* Show (or hide) the layer.
|
||||
* @param {boolean} enable true to show the layer, false to hide it.
|
||||
*/
|
||||
gdjs.Layer.prototype.show = function(enable) {
|
||||
gdjs.Layer.prototype.show = function (enable) {
|
||||
this._hidden = !enable;
|
||||
this._renderer.updateVisibility(enable);
|
||||
};
|
||||
@@ -172,7 +186,7 @@ gdjs.Layer.prototype.show = function(enable) {
|
||||
*
|
||||
* @return true if the layer is visible.
|
||||
*/
|
||||
gdjs.Layer.prototype.isVisible = function() {
|
||||
gdjs.Layer.prototype.isVisible = function () {
|
||||
return !this._hidden;
|
||||
};
|
||||
|
||||
@@ -182,7 +196,7 @@ gdjs.Layer.prototype.isVisible = function() {
|
||||
* @param {number} newZoom The new zoom. Must be superior to 0. 1 is the default zoom.
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.setCameraZoom = function(newZoom, cameraId) {
|
||||
gdjs.Layer.prototype.setCameraZoom = function (newZoom, cameraId) {
|
||||
this._zoomFactor = newZoom;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
@@ -193,7 +207,7 @@ gdjs.Layer.prototype.setCameraZoom = function(newZoom, cameraId) {
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return {number} The zoom.
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraZoom = function(cameraId) {
|
||||
gdjs.Layer.prototype.getCameraZoom = function (cameraId) {
|
||||
return this._zoomFactor;
|
||||
};
|
||||
|
||||
@@ -203,7 +217,7 @@ gdjs.Layer.prototype.getCameraZoom = function(cameraId) {
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return {number} The rotation, in degrees.
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraRotation = function(cameraId) {
|
||||
gdjs.Layer.prototype.getCameraRotation = function (cameraId) {
|
||||
return this._cameraRotation;
|
||||
};
|
||||
|
||||
@@ -214,7 +228,7 @@ gdjs.Layer.prototype.getCameraRotation = function(cameraId) {
|
||||
* @param {number} rotation The new rotation, in degrees.
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.setCameraRotation = function(rotation, cameraId) {
|
||||
gdjs.Layer.prototype.setCameraRotation = function (rotation, cameraId) {
|
||||
this._cameraRotation = rotation;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
@@ -229,7 +243,7 @@ gdjs.Layer.prototype.setCameraRotation = function(rotation, cameraId) {
|
||||
* @param {number} y The y position, in canvas coordinates.
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.convertCoords = function(x, y, cameraId) {
|
||||
gdjs.Layer.prototype.convertCoords = function (x, y, cameraId) {
|
||||
x -= this._cachedGameResolutionWidth / 2;
|
||||
y -= this._cachedGameResolutionHeight / 2;
|
||||
x /= Math.abs(this._zoomFactor);
|
||||
@@ -246,7 +260,7 @@ gdjs.Layer.prototype.convertCoords = function(x, y, cameraId) {
|
||||
return [x + this.getCameraX(cameraId), y + this.getCameraY(cameraId)];
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.convertInverseCoords = function(x, y, cameraId) {
|
||||
gdjs.Layer.prototype.convertInverseCoords = function (x, y, cameraId) {
|
||||
x -= this.getCameraX(cameraId);
|
||||
y -= this.getCameraY(cameraId);
|
||||
|
||||
@@ -267,33 +281,59 @@ gdjs.Layer.prototype.convertInverseCoords = function(x, y, cameraId) {
|
||||
];
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.getWidth = function() {
|
||||
gdjs.Layer.prototype.getWidth = function () {
|
||||
return this._cachedGameResolutionWidth;
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.getHeight = function() {
|
||||
gdjs.Layer.prototype.getHeight = function () {
|
||||
return this._cachedGameResolutionHeight;
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.getEffectsData = function() {
|
||||
return this._effectsData;
|
||||
/**
|
||||
* Return the initial effects data for the layer. Only to
|
||||
* be used by renderers.
|
||||
*/
|
||||
gdjs.Layer.prototype.getInitialEffectsData = function () {
|
||||
return this._initialEffectsData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a new effect, or replace the one with the same name.
|
||||
* @param {EffectData} effectData The data of the effect to add.
|
||||
*/
|
||||
gdjs.Layer.prototype.addEffect = function(effectData) {
|
||||
gdjs.Layer.prototype.addEffect = function (effectData) {
|
||||
this._renderer.addEffect(effectData);
|
||||
}
|
||||
|
||||
for (var name in effectData.doubleParameters) {
|
||||
this.setEffectDoubleParameter(
|
||||
effectData.name,
|
||||
name,
|
||||
effectData.doubleParameters[name]
|
||||
);
|
||||
}
|
||||
for (var name in effectData.stringParameters) {
|
||||
this.setEffectStringParameter(
|
||||
effectData.name,
|
||||
name,
|
||||
effectData.stringParameters[name]
|
||||
);
|
||||
}
|
||||
for (var name in effectData.booleanParameters) {
|
||||
this.setEffectBooleanParameter(
|
||||
effectData.name,
|
||||
name,
|
||||
effectData.booleanParameters[name]
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the effect with the specified name
|
||||
* @param {string} effectName The name of the effect.
|
||||
*/
|
||||
gdjs.Layer.prototype.removeEffect = function(effectName) {
|
||||
gdjs.Layer.prototype.removeEffect = function (effectName) {
|
||||
this._renderer.removeEffect(effectName);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change an effect parameter value (for parameters that are numbers).
|
||||
@@ -301,7 +341,7 @@ gdjs.Layer.prototype.removeEffect = function(effectName) {
|
||||
* @param {string} parameterName The name of the parameter to update.
|
||||
* @param {number} value The new value (number).
|
||||
*/
|
||||
gdjs.Layer.prototype.setEffectDoubleParameter = function(
|
||||
gdjs.Layer.prototype.setEffectDoubleParameter = function (
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
@@ -315,7 +355,7 @@ gdjs.Layer.prototype.setEffectDoubleParameter = function(
|
||||
* @param {string} parameterName The name of the parameter to update.
|
||||
* @param {string} value The new value (string).
|
||||
*/
|
||||
gdjs.Layer.prototype.setEffectStringParameter = function(
|
||||
gdjs.Layer.prototype.setEffectStringParameter = function (
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
@@ -329,7 +369,7 @@ gdjs.Layer.prototype.setEffectStringParameter = function(
|
||||
* @param {string} parameterName The name of the parameter to update.
|
||||
* @param {boolean} value The new value (boolean).
|
||||
*/
|
||||
gdjs.Layer.prototype.setEffectBooleanParameter = function(
|
||||
gdjs.Layer.prototype.setEffectBooleanParameter = function (
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
@@ -342,7 +382,7 @@ gdjs.Layer.prototype.setEffectBooleanParameter = function(
|
||||
* @param {string} name The name of the effect to enable or disable.
|
||||
* @param {boolean} enable true to enable, false to disable
|
||||
*/
|
||||
gdjs.Layer.prototype.enableEffect = function(name, enable) {
|
||||
gdjs.Layer.prototype.enableEffect = function (name, enable) {
|
||||
this._renderer.enableEffect(name, enable);
|
||||
};
|
||||
|
||||
@@ -351,7 +391,7 @@ gdjs.Layer.prototype.enableEffect = function(name, enable) {
|
||||
* @param {string} name The name of the effect
|
||||
* @return {boolean} true if the effect is enabled, false otherwise.
|
||||
*/
|
||||
gdjs.Layer.prototype.isEffectEnabled = function(name) {
|
||||
gdjs.Layer.prototype.isEffectEnabled = function (name) {
|
||||
return this._renderer.isEffectEnabled(name);
|
||||
};
|
||||
|
||||
@@ -360,50 +400,23 @@ gdjs.Layer.prototype.isEffectEnabled = function(name) {
|
||||
* @param {string} name The name of the effect
|
||||
* @return {boolean} true if the effect exists, false otherwise.
|
||||
*/
|
||||
gdjs.Layer.prototype.hasEffect = function(name) {
|
||||
gdjs.Layer.prototype.hasEffect = function (name) {
|
||||
return this._renderer.hasEffect(name);
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype._setEffectsDefaultParameters = function() {
|
||||
for (var i = 0; i < this._effectsData.length; ++i) {
|
||||
var effectData = this._effectsData[i];
|
||||
for (var name in effectData.doubleParameters) {
|
||||
this.setEffectDoubleParameter(
|
||||
effectData.name,
|
||||
name,
|
||||
effectData.doubleParameters[name]
|
||||
);
|
||||
}
|
||||
for (var name in effectData.stringParameters) {
|
||||
this.setEffectStringParameter(
|
||||
effectData.name,
|
||||
name,
|
||||
effectData.stringParameters[name]
|
||||
);
|
||||
}
|
||||
for (var name in effectData.booleanParameters) {
|
||||
this.setEffectBooleanParameter(
|
||||
effectData.name,
|
||||
name,
|
||||
effectData.booleanParameters[name]
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the time scale for the objects on the layer:
|
||||
* time will be slower if time scale is < 1, faster if > 1.
|
||||
* @param {number} timeScale The new time scale (must be positive).
|
||||
*/
|
||||
gdjs.Layer.prototype.setTimeScale = function(timeScale) {
|
||||
gdjs.Layer.prototype.setTimeScale = function (timeScale) {
|
||||
if (timeScale >= 0) this._timeScale = timeScale;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the time scale for the objects on the layer.
|
||||
*/
|
||||
gdjs.Layer.prototype.getTimeScale = function() {
|
||||
gdjs.Layer.prototype.getTimeScale = function () {
|
||||
return this._timeScale;
|
||||
};
|
||||
|
||||
@@ -411,6 +424,54 @@ gdjs.Layer.prototype.getTimeScale = function() {
|
||||
* Return the time elapsed since the last frame,
|
||||
* in milliseconds, for objects on the layer.
|
||||
*/
|
||||
gdjs.Layer.prototype.getElapsedTime = function() {
|
||||
gdjs.Layer.prototype.getElapsedTime = function () {
|
||||
return this._runtimeScene.getTimeManager().getElapsedTime() * this._timeScale;
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the position, rotation and scale (zoom) of the layer camera to be the same as the base layer camera.
|
||||
*/
|
||||
gdjs.Layer.prototype.followBaseLayer = function () {
|
||||
var baseLayer = this._runtimeScene.getLayer('');
|
||||
this.setCameraX(baseLayer.getCameraX());
|
||||
this.setCameraY(baseLayer.getCameraY());
|
||||
this.setCameraRotation(baseLayer.getCameraRotation());
|
||||
this.setCameraZoom(baseLayer.getCameraZoom());
|
||||
};
|
||||
|
||||
/**
|
||||
* The clear color is defined in the format [r, g, b], with components in the range of 0 to 1.
|
||||
* @return {number[]} the clear color of layer in the range of [0, 1].
|
||||
*/
|
||||
gdjs.Layer.prototype.getClearColor = function () {
|
||||
return this._clearColor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the clear color in format [r, g, b], with components in the range of 0 to 1.;
|
||||
* @param {?number} r Red color component in the range 0-255.
|
||||
* @param {?number} g Green color component in the range 0-255.
|
||||
* @param {?number} b Blue color component in the range 0-255.
|
||||
*/
|
||||
gdjs.Layer.prototype.setClearColor = function (r, g, b) {
|
||||
if (r) this._clearColor[0] = r / 255;
|
||||
if (g) this._clearColor[1] = g / 255;
|
||||
if (b) this._clearColor[2] = b / 255;
|
||||
this._renderer.updateClearColor();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set whether layer's camera follows base layer's camera or not.
|
||||
* @param {boolean} follow
|
||||
*/
|
||||
gdjs.Layer.prototype.setFollowBaseLayerCamera = function (follow) {
|
||||
this._followBaseLayerCamera = follow;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the layer is a lighting layer, false otherwise.
|
||||
* @return {boolean} true if it is a lighting layer, false otherwise.
|
||||
*/
|
||||
gdjs.Layer.prototype.isLightingLayer = function () {
|
||||
return this._isLightingLayer;
|
||||
};
|
||||
|
@@ -13,25 +13,50 @@
|
||||
* @param {gdjs.Layer} layer The layer
|
||||
* @param {gdjs.RuntimeScenePixiRenderer} runtimeSceneRenderer The scene renderer
|
||||
*/
|
||||
gdjs.LayerPixiRenderer = function(layer, runtimeSceneRenderer) {
|
||||
// @ts-ignore
|
||||
gdjs.LayerPixiRenderer = function (layer, runtimeSceneRenderer) {
|
||||
this._pixiContainer = new PIXI.Container();
|
||||
/** @type Object.<string, gdjsPixiFiltersToolsFilter> */
|
||||
this._filters = {};
|
||||
this._layer = layer;
|
||||
runtimeSceneRenderer.getPIXIContainer().addChild(this._pixiContainer);
|
||||
|
||||
this._setupFilters();
|
||||
/** @type {?PIXI.RenderTexture} */
|
||||
this._renderTexture = null;
|
||||
|
||||
/** @type {?PIXI.Sprite} */
|
||||
this._lightingSprite = null;
|
||||
|
||||
this._runtimeSceneRenderer = runtimeSceneRenderer;
|
||||
this._pixiRenderer = runtimeSceneRenderer.getPIXIRenderer();
|
||||
// Width and height are tracked when a render texture is used.
|
||||
this._oldWidth = null;
|
||||
this._oldHeight = null;
|
||||
this._isLightingLayer = layer.isLightingLayer();
|
||||
this._clearColor = layer.getClearColor();
|
||||
|
||||
runtimeSceneRenderer.getPIXIContainer().addChild(this._pixiContainer);
|
||||
this._pixiContainer.filters = [];
|
||||
|
||||
if (this._isLightingLayer) {
|
||||
this._replaceContainerWithSprite();
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.LayerRenderer = gdjs.LayerPixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.LayerPixiRenderer.prototype.getRendererObject = function () {
|
||||
return this._pixiContainer;
|
||||
};
|
||||
|
||||
gdjs.LayerPixiRenderer.prototype.getLightingSprite = function () {
|
||||
return this._lightingSprite;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the position of the PIXI container. To be called after each change
|
||||
* made to position, zoom or rotation of the camera.
|
||||
* @private
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.updatePosition = function() {
|
||||
gdjs.LayerPixiRenderer.prototype.updatePosition = function () {
|
||||
var angle = -gdjs.toRad(this._layer.getCameraRotation());
|
||||
var zoomFactor = this._layer.getCameraZoom();
|
||||
|
||||
@@ -54,34 +79,26 @@ gdjs.LayerPixiRenderer.prototype.updatePosition = function() {
|
||||
this._pixiContainer.position.y += this._layer.getHeight() / 2;
|
||||
};
|
||||
|
||||
gdjs.LayerPixiRenderer.prototype.updateVisibility = function(visible) {
|
||||
gdjs.LayerPixiRenderer.prototype.updateVisibility = function (visible) {
|
||||
this._pixiContainer.visible = !!visible;
|
||||
};
|
||||
|
||||
gdjs.LayerPixiRenderer.prototype.updateTime = function() {
|
||||
for(var filterName in this._filters) {
|
||||
gdjs.LayerPixiRenderer.prototype.update = function () {
|
||||
if (this._renderTexture) {
|
||||
this._updateRenderTexture();
|
||||
}
|
||||
|
||||
for (var filterName in this._filters) {
|
||||
var filter = this._filters[filterName];
|
||||
filter.update(filter.pixiFilter, this._layer);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.LayerPixiRenderer.prototype._setupFilters = function() {
|
||||
var effectsData = this._layer.getEffectsData();
|
||||
if (effectsData.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._pixiContainer.filters = [];
|
||||
for (var i = 0; i < effectsData.length; ++i) {
|
||||
this.addEffect(effectsData[i])
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a new effect, or replace the one with the same name.
|
||||
* @param {EffectData} effectData The data of the effect to add.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.addEffect = function(effectData) {
|
||||
gdjs.LayerPixiRenderer.prototype.addEffect = function (effectData) {
|
||||
var filterCreator = gdjs.PixiFiltersTools.getFilterCreator(
|
||||
effectData.effectType
|
||||
);
|
||||
@@ -105,23 +122,28 @@ gdjs.LayerPixiRenderer.prototype.addEffect = function(effectData) {
|
||||
update: filterCreator.update,
|
||||
};
|
||||
|
||||
this._pixiContainer.filters = (this._pixiContainer.filters || [])
|
||||
.concat(filter.pixiFilter);
|
||||
if (this._isLightingLayer) filter.pixiFilter.blendMode = PIXI.BLEND_MODES.ADD;
|
||||
this._pixiContainer.filters = (this._pixiContainer.filters || []).concat(
|
||||
filter.pixiFilter
|
||||
);
|
||||
this._filters[effectData.name] = filter;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the effect with the specified name
|
||||
* @param {string} effectName The name of the effect.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.removeEffect = function(effectName) {
|
||||
gdjs.LayerPixiRenderer.prototype.removeEffect = function (effectName) {
|
||||
var filter = this._filters[effectName];
|
||||
if (!filter) return;
|
||||
|
||||
this._pixiContainer.filters = (this._pixiContainer.filters || [])
|
||||
.filter(function(pixiFilter) { return pixiFilter !== filter.pixiFilter; });
|
||||
this._pixiContainer.filters = (this._pixiContainer.filters || []).filter(
|
||||
function (pixiFilter) {
|
||||
return pixiFilter !== filter.pixiFilter;
|
||||
}
|
||||
);
|
||||
delete this._filters[effectName];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a child to the pixi container associated to the layer.
|
||||
@@ -130,10 +152,11 @@ gdjs.LayerPixiRenderer.prototype.removeEffect = function(effectName) {
|
||||
* @param child The child (PIXI object) to be added.
|
||||
* @param zOrder The z order of the associated object.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.addRendererObject = function(child, zOrder) {
|
||||
gdjs.LayerPixiRenderer.prototype.addRendererObject = function (child, zOrder) {
|
||||
child.zOrder = zOrder; //Extend the pixi object with a z order.
|
||||
|
||||
for (var i = 0, len = this._pixiContainer.children.length; i < len; ++i) {
|
||||
// @ts-ignore
|
||||
if (this._pixiContainer.children[i].zOrder >= zOrder) {
|
||||
//TODO : Dichotomic search
|
||||
this._pixiContainer.addChildAt(child, i);
|
||||
@@ -149,7 +172,7 @@ gdjs.LayerPixiRenderer.prototype.addRendererObject = function(child, zOrder) {
|
||||
* @param child The child (PIXI object) to be modified.
|
||||
* @param newZOrder The z order of the associated object.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.changeRendererObjectZOrder = function(
|
||||
gdjs.LayerPixiRenderer.prototype.changeRendererObjectZOrder = function (
|
||||
child,
|
||||
newZOrder
|
||||
) {
|
||||
@@ -163,7 +186,7 @@ gdjs.LayerPixiRenderer.prototype.changeRendererObjectZOrder = function(
|
||||
*
|
||||
* @param child The child (PIXI object) to be removed.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.removeRendererObject = function(child) {
|
||||
gdjs.LayerPixiRenderer.prototype.removeRendererObject = function (child) {
|
||||
this._pixiContainer.removeChild(child);
|
||||
};
|
||||
|
||||
@@ -173,7 +196,7 @@ gdjs.LayerPixiRenderer.prototype.removeRendererObject = function(child) {
|
||||
* @param {string} parameterName The parameter name
|
||||
* @param {number} value The new value for the parameter
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.setEffectDoubleParameter = function(
|
||||
gdjs.LayerPixiRenderer.prototype.setEffectDoubleParameter = function (
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
@@ -190,7 +213,7 @@ gdjs.LayerPixiRenderer.prototype.setEffectDoubleParameter = function(
|
||||
* @param {string} parameterName The parameter name
|
||||
* @param {string} value The new value for the parameter
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.setEffectStringParameter = function(
|
||||
gdjs.LayerPixiRenderer.prototype.setEffectStringParameter = function (
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
@@ -207,7 +230,7 @@ gdjs.LayerPixiRenderer.prototype.setEffectStringParameter = function(
|
||||
* @param {string} parameterName The parameter name
|
||||
* @param {boolean} value The new value for the parameter
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.setEffectBooleanParameter = function(
|
||||
gdjs.LayerPixiRenderer.prototype.setEffectBooleanParameter = function (
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
@@ -223,7 +246,7 @@ gdjs.LayerPixiRenderer.prototype.setEffectBooleanParameter = function(
|
||||
* @param {string} name The effect name
|
||||
* @returns {boolean} True if the effect exists, false otherwise
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.hasEffect = function(name) {
|
||||
gdjs.LayerPixiRenderer.prototype.hasEffect = function (name) {
|
||||
return !!this._filters[name];
|
||||
};
|
||||
|
||||
@@ -232,7 +255,7 @@ gdjs.LayerPixiRenderer.prototype.hasEffect = function(name) {
|
||||
* @param {string} name The effect name
|
||||
* @param {boolean} value Set to true to enable, false to disable
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.enableEffect = function(name, value) {
|
||||
gdjs.LayerPixiRenderer.prototype.enableEffect = function (name, value) {
|
||||
var filter = this._filters[name];
|
||||
if (!filter) return;
|
||||
|
||||
@@ -244,9 +267,82 @@ gdjs.LayerPixiRenderer.prototype.enableEffect = function(name, value) {
|
||||
* @param {string} name The effect name
|
||||
* @return {boolean} true if the filter is enabled
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.isEffectEnabled = function(name) {
|
||||
gdjs.LayerPixiRenderer.prototype.isEffectEnabled = function (name) {
|
||||
var filter = this._filters[name];
|
||||
if (!filter) return false;
|
||||
|
||||
return gdjs.PixiFiltersTools.isEffectEnabled(filter);
|
||||
};
|
||||
|
||||
gdjs.LayerPixiRenderer.prototype.updateClearColor = function () {
|
||||
this._clearColor = this._layer.getClearColor();
|
||||
this._updateRenderTexture();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the render texture, if it exists.
|
||||
* Also, render texture is cleared with a specified clear color.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype._updateRenderTexture = function () {
|
||||
if (!this._pixiRenderer) return;
|
||||
|
||||
if (!this._renderTexture) {
|
||||
this._oldWidth = this._pixiRenderer.screen.width;
|
||||
this._oldHeight = this._pixiRenderer.screen.height;
|
||||
|
||||
var width = this._oldWidth;
|
||||
var height = this._oldHeight;
|
||||
var resolution = this._pixiRenderer.resolution;
|
||||
this._renderTexture = PIXI.RenderTexture.create({
|
||||
width,
|
||||
height,
|
||||
resolution,
|
||||
});
|
||||
this._renderTexture.baseTexture.scaleMode = PIXI.SCALE_MODES.LINEAR;
|
||||
}
|
||||
|
||||
if (
|
||||
this._oldWidth !== this._pixiRenderer.screen.width ||
|
||||
this._oldHeight !== this._pixiRenderer.screen.height
|
||||
) {
|
||||
this._renderTexture.resize(
|
||||
this._pixiRenderer.screen.width,
|
||||
this._pixiRenderer.screen.height
|
||||
);
|
||||
this._oldWidth = this._pixiRenderer.screen.width;
|
||||
this._oldHeight = this._pixiRenderer.screen.height;
|
||||
}
|
||||
|
||||
var oldRenderTexture = this._pixiRenderer.renderTexture.current;
|
||||
var oldSourceFrame = this._pixiRenderer.renderTexture.sourceFrame;
|
||||
|
||||
this._pixiRenderer.renderTexture.bind(this._renderTexture);
|
||||
this._pixiRenderer.renderTexture.clear(this._clearColor);
|
||||
|
||||
this._pixiRenderer.render(this._pixiContainer, this._renderTexture, false);
|
||||
this._pixiRenderer.renderTexture.bind(
|
||||
oldRenderTexture,
|
||||
oldSourceFrame,
|
||||
undefined
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable the use of a PIXI.RenderTexture to render the PIXI.Container
|
||||
* of the layer and, in the scene PIXI container, replace the container
|
||||
* of the layer by a sprite showing this texture.
|
||||
* @private used only in lighting for now as the sprite could have MULTIPLY blend mode.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype._replaceContainerWithSprite = function () {
|
||||
if (!this._pixiRenderer) return;
|
||||
|
||||
this._updateRenderTexture();
|
||||
if (!this._renderTexture) return;
|
||||
this._lightingSprite = new PIXI.Sprite(this._renderTexture);
|
||||
this._lightingSprite.blendMode = PIXI.BLEND_MODES.MULTIPLY;
|
||||
|
||||
var sceneContainer = this._runtimeSceneRenderer.getPIXIContainer();
|
||||
var index = sceneContainer.getChildIndex(this._pixiContainer);
|
||||
sceneContainer.addChildAt(this._lightingSprite, index);
|
||||
sceneContainer.removeChild(this._pixiContainer);
|
||||
};
|
||||
|
@@ -23,6 +23,15 @@ gdjs.PixiImageManager = function(resources)
|
||||
|
||||
gdjs.ImageManager = gdjs.PixiImageManager; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param {ResourceData[]} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.PixiImageManager.prototype.setResources = function(resources) {
|
||||
this._resources = resources;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the PIXI texture associated to the specified resource name.
|
||||
* Returns a placeholder texture if not found.
|
||||
@@ -124,7 +133,6 @@ gdjs.PixiImageManager.prototype.loadTextures = function(onProgress, onComplete)
|
||||
|
||||
if ( res.file && res.kind === "image" ) {
|
||||
if (this._loadedTextures.containsKey(res.name)) {
|
||||
console.log("Texture \"" + res.name + "\" is already loaded.");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@@ -12,7 +12,7 @@ gdjs.RuntimeGamePixiRenderer = function(game, forceFullscreen) {
|
||||
this._isFullscreen = false; //Used to track if the window is displayed as fullscreen (see setFullscreen method).
|
||||
this._forceFullscreen = forceFullscreen; //If set to true, the canvas will always be displayed as fullscreen, even if _isFullscreen == false.
|
||||
|
||||
/** @type {PIXI.SystemRenderer} */
|
||||
/** @type {?PIXI.Renderer} */
|
||||
this._pixiRenderer = null;
|
||||
this._canvasWidth = 0; // Current width of the canvas (might be scaled down/up compared to renderer)
|
||||
this._canvasHeight = 0; // Current height of the canvas (might be scaled down/up compared to renderer)
|
||||
@@ -41,7 +41,7 @@ gdjs.RuntimeGamePixiRenderer.prototype.createStandardCanvas = function(
|
||||
);
|
||||
parentElement.appendChild(this._pixiRenderer.view); // add the renderer view element to the DOM
|
||||
this._pixiRenderer.view.style['position'] = 'absolute';
|
||||
this._pixiRenderer.view.tabindex = '1'; //Ensure that the canvas has the focus.
|
||||
this._pixiRenderer.view.tabIndex = 1; //Ensure that the canvas has the focus.
|
||||
this._resizeCanvas();
|
||||
|
||||
// Handle scale mode
|
||||
|
@@ -1,14 +1,30 @@
|
||||
gdjs.RuntimeScenePixiRenderer = function(runtimeScene, runtimeGameRenderer) {
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* The renderer for a gdjs.RuntimeScene using Pixi.js.
|
||||
* @class RuntimeScenePixiRenderer
|
||||
* @memberof gdjs
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @param {gdjs.RuntimeGamePixiRenderer} runtimeGameRenderer
|
||||
*/
|
||||
gdjs.RuntimeScenePixiRenderer = function (runtimeScene, runtimeGameRenderer) {
|
||||
this._pixiRenderer = runtimeGameRenderer
|
||||
? runtimeGameRenderer.getPIXIRenderer()
|
||||
: null;
|
||||
this._runtimeScene = runtimeScene;
|
||||
this._pixiContainer = new PIXI.Container(); //The Container meant to contains all pixi objects of the scene.
|
||||
this._pixiContainer = new PIXI.Container(); // Contains the layers of the scene (and, optionally, debug PIXI objects).
|
||||
this._pixiContainer.sortableChildren = true;
|
||||
|
||||
/** @type {?PIXI.Graphics} */
|
||||
this._debugDraw = null;
|
||||
|
||||
/** @type {?PIXI.Text} */
|
||||
this._profilerText = null;
|
||||
};
|
||||
|
||||
gdjs.RuntimeSceneRenderer = gdjs.RuntimeScenePixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.onGameResolutionResized = function() {
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.onGameResolutionResized = function () {
|
||||
if (!this._pixiRenderer) return;
|
||||
|
||||
var runtimeGame = this._runtimeScene.getGame();
|
||||
@@ -18,7 +34,11 @@ gdjs.RuntimeScenePixiRenderer.prototype.onGameResolutionResized = function() {
|
||||
this._pixiRenderer.height / runtimeGame.getGameResolutionHeight();
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.render = function() {
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.onSceneUnloaded = function () {
|
||||
// Nothing to do.
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.render = function () {
|
||||
if (!this._pixiRenderer) return;
|
||||
|
||||
// this._renderProfileText(); //Uncomment to display profiling times
|
||||
@@ -28,28 +48,38 @@ gdjs.RuntimeScenePixiRenderer.prototype.render = function() {
|
||||
this._pixiRenderer.render(this._pixiContainer);
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype._renderProfileText = function() {
|
||||
if (!this._runtimeScene.getProfiler()) return;
|
||||
gdjs.RuntimeScenePixiRenderer.prototype._renderProfileText = function () {
|
||||
var profiler = this._runtimeScene.getProfiler();
|
||||
if (!profiler) return;
|
||||
|
||||
if (!this._profilerText) {
|
||||
this._profilerText = new PIXI.Text(" ", {
|
||||
align: "left",
|
||||
stroke: "#FFF",
|
||||
strokeThickness: 1
|
||||
this._profilerText = new PIXI.Text(' ', {
|
||||
align: 'left',
|
||||
stroke: '#FFF',
|
||||
strokeThickness: 1,
|
||||
});
|
||||
// Add on top of all layers:
|
||||
this._pixiContainer.addChild(this._profilerText);
|
||||
}
|
||||
|
||||
var average = this._runtimeScene.getProfiler().getFramesAverageMeasures();
|
||||
var average = profiler.getFramesAverageMeasures();
|
||||
var outputs = [];
|
||||
gdjs.Profiler.getProfilerSectionTexts("All", average, outputs);
|
||||
gdjs.Profiler.getProfilerSectionTexts('All', average, outputs);
|
||||
|
||||
this._profilerText.text = outputs.join("\n");
|
||||
this._profilerText.text = outputs.join('\n');
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.renderDebugDraw = function(instances, layersCameraCoordinates) {
|
||||
/**
|
||||
* @param {gdjs.RuntimeObject[]} instances
|
||||
* @param {Object.<string, number[]>} layersCameraCoordinates
|
||||
*/
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.renderDebugDraw = function (
|
||||
instances,
|
||||
layersCameraCoordinates
|
||||
) {
|
||||
if (!this._debugDraw) {
|
||||
this._debugDraw = new PIXI.Graphics();
|
||||
// Add on top of all layers:
|
||||
this._pixiContainer.addChild(this._debugDraw);
|
||||
}
|
||||
/** @type PIXI.Graphics */
|
||||
@@ -61,7 +91,7 @@ gdjs.RuntimeScenePixiRenderer.prototype.renderDebugDraw = function(instances, la
|
||||
debugDraw.fill.alpha = 0.1;
|
||||
debugDraw.alpha = 0.8;
|
||||
|
||||
for(var i = 0;i < instances.length;i++) {
|
||||
for (var i = 0; i < instances.length; i++) {
|
||||
var object = instances[i];
|
||||
var cameraCoords = layersCameraCoordinates[object.getLayer()];
|
||||
var rendererObject = object.getRendererObject();
|
||||
@@ -69,19 +99,55 @@ gdjs.RuntimeScenePixiRenderer.prototype.renderDebugDraw = function(instances, la
|
||||
if (!cameraCoords || !rendererObject) continue;
|
||||
|
||||
var aabb = object.getAABB();
|
||||
debugDraw.drawRect(aabb.min[0], aabb.min[1], aabb.max[0] - aabb.min[0], aabb.max[1] - aabb.min[1]);
|
||||
debugDraw.drawRect(
|
||||
aabb.min[0],
|
||||
aabb.min[1],
|
||||
aabb.max[0] - aabb.min[0],
|
||||
aabb.max[1] - aabb.min[1]
|
||||
);
|
||||
}
|
||||
debugDraw.endFill();
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.hideCursor = function() {
|
||||
this._pixiRenderer.view.style.cursor = "none";
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.hideCursor = function () {
|
||||
if (!this._pixiRenderer) return;
|
||||
this._pixiRenderer.view.style.cursor = 'none';
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.showCursor = function() {
|
||||
this._pixiRenderer.view.style.cursor = "";
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.showCursor = function () {
|
||||
if (!this._pixiRenderer) return;
|
||||
this._pixiRenderer.view.style.cursor = '';
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.getPIXIContainer = function() {
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.getPIXIContainer = function () {
|
||||
return this._pixiContainer;
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.getPIXIRenderer = function () {
|
||||
return this._pixiRenderer;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {gdjs.Layer} layer
|
||||
* @param {number} index
|
||||
*/
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.setLayerIndex = function (
|
||||
layer,
|
||||
index
|
||||
) {
|
||||
/** @type {gdjs.LayerPixiRenderer} */
|
||||
// @ts-ignore - assume the renderer is the correct one
|
||||
var layerPixiRenderer = layer.getRenderer();
|
||||
|
||||
/** @type {PIXI.Container | ?PIXI.Sprite} */
|
||||
var layerPixiObject = layerPixiRenderer.getRendererObject();
|
||||
|
||||
if (layer.isLightingLayer())
|
||||
layerPixiObject = layerPixiRenderer.getLightingSprite();
|
||||
|
||||
if (!layerPixiObject) return;
|
||||
if (this._pixiContainer.children.indexOf(layerPixiObject) === index) return;
|
||||
|
||||
this._pixiContainer.removeChild(layerPixiObject);
|
||||
this._pixiContainer.addChildAt(layerPixiObject, index);
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user