Compare commits
9 Commits
feat/impro
...
feature/vi
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cba3a4e87a | ||
![]() |
15dc2fcdd0 | ||
![]() |
b21fdc1ac2 | ||
![]() |
ad4c53898a | ||
![]() |
7a0745a977 | ||
![]() |
69b3c23f1d | ||
![]() |
5f52b6d7e3 | ||
![]() |
dfdbd5c5a0 | ||
![]() |
958f89fb49 |
@@ -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> >*
|
||||
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
@@ -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
@@ -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
|
@@ -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;
|
||||
};
|
||||
|
@@ -65,14 +65,49 @@ 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 = 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) {
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
};
|
||||
|
@@ -8,7 +8,7 @@ describe('gdjs.DraggableRuntimeBehavior', function() {
|
||||
});
|
||||
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers:[{name:"", visibility: true}],
|
||||
layers:[{name:"", visibility: true, effects: []}],
|
||||
variables: [],
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
|
@@ -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() {
|
||||
};
|
||||
|
||||
|
@@ -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() {
|
||||
};
|
||||
|
||||
|
@@ -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: [],
|
||||
|
@@ -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();
|
||||
};
|
||||
|
@@ -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,12 +24,14 @@
|
||||
* @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
|
||||
@@ -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} */
|
||||
@@ -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;
|
||||
};
|
||||
|
@@ -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;
|
||||
|
@@ -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: [],
|
||||
|
@@ -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){
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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();
|
||||
};
|
||||
|
@@ -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
|
||||
) {
|
||||
|
@@ -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();
|
||||
@@ -327,9 +325,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" +
|
||||
" "
|
||||
@@ -396,33 +394,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) {
|
||||
@@ -839,15 +810,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"
|
||||
@@ -325,7 +327,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 = ";
|
||||
|
@@ -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(
|
||||
@@ -95,7 +79,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,8 +108,9 @@ 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.
|
||||
@@ -195,7 +184,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,8 +212,9 @@ 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.
|
||||
|
@@ -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,7 @@
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/CodeGeneration/EffectsCodeGenerator.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
|
||||
#include "GDCore/IDE/ProjectStripper.h"
|
||||
@@ -27,6 +28,7 @@
|
||||
#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 gdjs {
|
||||
@@ -41,37 +43,39 @@ 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);
|
||||
|
||||
if (!options.projectDataOnlyExport) {
|
||||
// Generate events code
|
||||
if (!ExportEventsCode(exportedProject, codeOutputDir, includesFiles, true))
|
||||
return false;
|
||||
@@ -79,49 +83,78 @@ bool ExporterHelper::ExportLayoutForPixiPreview(gd::Project &project,
|
||||
// Export source files
|
||||
if (!ExportExternalSourceFiles(
|
||||
exportedProject, codeOutputDir, includesFiles)) {
|
||||
gd::LogError(_("Error during exporting! Unable to export source files:\n") +
|
||||
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);
|
||||
|
||||
// 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);
|
||||
scriptFilesElement.AddChild("scriptFile")
|
||||
.SetStringAttribute("path", includeFile)
|
||||
.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, false);
|
||||
|
||||
// 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,
|
||||
gd::String ExporterHelper::ExportProjectData(
|
||||
gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
gd::String filename,
|
||||
gd::String wrapIntoVariable) {
|
||||
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;
|
||||
|
||||
@@ -500,6 +533,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");
|
||||
}
|
||||
@@ -674,6 +708,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,62 @@ 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 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 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 +101,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,
|
||||
static gd::String ExportProjectData(
|
||||
gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
gd::String filename,
|
||||
gd::String wrapIntoVariable);
|
||||
const gd::SerializerElement &runtimeGameOptions);
|
||||
|
||||
/**
|
||||
* \brief Copy all the resources of the project to to the export directory,
|
||||
@@ -119,6 +178,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 +274,13 @@ 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 Change the directory where code files are generated.
|
||||
|
@@ -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) {
|
||||
@@ -180,7 +182,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) {
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -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];
|
||||
|
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
@@ -21,7 +21,7 @@ gdjs.Layer = function(layerData, runtimeScene) {
|
||||
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
|
||||
@@ -35,7 +35,10 @@ gdjs.Layer = function(layerData, runtimeScene) {
|
||||
// @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() {
|
||||
@@ -275,8 +278,12 @@ 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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -285,6 +292,28 @@ gdjs.Layer.prototype.getEffectsData = function() {
|
||||
*/
|
||||
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]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,33 +393,6 @@ 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.
|
||||
|
@@ -21,7 +21,7 @@ gdjs.LayerPixiRenderer = function(layer, runtimeSceneRenderer) {
|
||||
this._layer = layer;
|
||||
runtimeSceneRenderer.getPIXIContainer().addChild(this._pixiContainer);
|
||||
|
||||
this._setupFilters();
|
||||
this._pixiContainer.filters = [];
|
||||
};
|
||||
|
||||
gdjs.LayerRenderer = gdjs.LayerPixiRenderer; //Register the class to let the engine use it.
|
||||
@@ -65,18 +65,6 @@ gdjs.LayerPixiRenderer.prototype.updateTime = function() {
|
||||
}
|
||||
};
|
||||
|
||||
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.
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -27,6 +27,22 @@ gdjs.RuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
this._activated = true;
|
||||
this.owner = owner;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the behavior must be updated using the specified behaviorData. This is the
|
||||
* case during hot-reload, and is only called if the behavior was modified.
|
||||
*
|
||||
* @see gdjs.RuntimeBehavior#onObjectHotReloaded
|
||||
*
|
||||
* @param {BehaviorData} oldBehaviorData The previous data for the behavior.
|
||||
* @param {BehaviorData} newBehaviorData The new data for the behavior.
|
||||
* @returns {boolean} true if the behavior was updated, false if it could not (i.e: hot-reload is not supported).
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.updateFromBehaviorData = function(oldBehaviorData, newBehaviorData) {
|
||||
// If not redefined, mark by default the hot-reload as failed.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the behavior.
|
||||
* @return {string} The behavior's name.
|
||||
@@ -146,4 +162,13 @@ gdjs.RuntimeBehavior.prototype.onDestroy = function() {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* This method is called when the owner of the behavior
|
||||
* was hot reloaded, so its position, angle, size can have been changed outside
|
||||
* of events.
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.onObjectHotReloaded = function() {
|
||||
|
||||
};
|
||||
|
||||
gdjs.registerBehavior("", gdjs.RuntimeBehavior);
|
||||
|
@@ -5,13 +5,28 @@
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} RuntimeGameOptionsScriptFile
|
||||
* @property {string} path The path for this script file.
|
||||
* @property {number} hash The hash of the script file content.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} RuntimeGameOptions
|
||||
* @property {boolean=} forceFullscreen if true, force fullscreen.
|
||||
* @property {boolean=} isPreview if true, game is run as a preview launched from an editor.
|
||||
* @property {string=} injectExternalLayout The name of the external layout to create in the scene at position 0;0.
|
||||
* @property {RuntimeGameOptionsScriptFile[]=} scriptFiles Script files, used for hot-reloading.
|
||||
* @property {boolean=} projectDataOnlyExport if true, export is a partial preview without events.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a game being played.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @class RuntimeGame
|
||||
* @param {ProjectData} data The object (usually stored in data.json) containing the full project data
|
||||
* @param {Object=} options Optional object for specifiying additional options: {forceFullscreen: ...}
|
||||
* @param {RuntimeGameOptions=} options Optional object for specifiying additional options: {forceFullscreen: ...}
|
||||
*/
|
||||
gdjs.RuntimeGame = function(data, options) {
|
||||
options = options || {};
|
||||
@@ -19,27 +34,27 @@ gdjs.RuntimeGame = function(data, options) {
|
||||
this._variables = new gdjs.VariablesContainer(data.variables);
|
||||
this._data = data;
|
||||
this._imageManager = new gdjs.ImageManager(
|
||||
data.resources.resources
|
||||
this._data.resources.resources
|
||||
);
|
||||
this._soundManager = new gdjs.SoundManager(
|
||||
data.resources.resources
|
||||
this._data.resources.resources
|
||||
);
|
||||
this._fontManager = new gdjs.FontManager(
|
||||
data.resources.resources
|
||||
this._data.resources.resources
|
||||
);
|
||||
this._jsonManager = new gdjs.JsonManager(
|
||||
data.resources.resources
|
||||
this._data.resources.resources
|
||||
);
|
||||
this._maxFPS = data ? data.properties.maxFPS : 60;
|
||||
this._minFPS = data ? data.properties.minFPS : 15;
|
||||
this._maxFPS = this._data ? this._data.properties.maxFPS : 60;
|
||||
this._minFPS = this._data ? this._data.properties.minFPS : 15;
|
||||
|
||||
this._gameResolutionWidth = data.properties.windowWidth;
|
||||
this._gameResolutionHeight = data.properties.windowHeight;
|
||||
this._gameResolutionWidth = this._data.properties.windowWidth;
|
||||
this._gameResolutionHeight = this._data.properties.windowHeight;
|
||||
this._originalWidth = this._gameResolutionWidth;
|
||||
this._originalHeight = this._gameResolutionHeight;
|
||||
this._resizeMode = data.properties.sizeOnStartupMode;
|
||||
this._resizeMode = this._data.properties.sizeOnStartupMode;
|
||||
this._adaptGameResolutionAtRuntime =
|
||||
data.properties.adaptGameResolutionAtRuntime;
|
||||
this._data.properties.adaptGameResolutionAtRuntime;
|
||||
/** @type {string} */
|
||||
this._scaleMode = data.properties.scaleMode || 'linear';
|
||||
this._renderer = new gdjs.RuntimeGameRenderer(
|
||||
@@ -71,9 +86,31 @@ gdjs.RuntimeGame = function(data, options) {
|
||||
this._isPreview = options.isPreview || false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the project data. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param {ProjectData} projectData The object (usually stored in data.json) containing the full project data
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.setProjectData = function(projectData) {
|
||||
this._data = projectData;
|
||||
|
||||
this._imageManager.setResources(
|
||||
this._data.resources.resources
|
||||
);
|
||||
this._soundManager.setResources(
|
||||
this._data.resources.resources
|
||||
);
|
||||
this._fontManager.setResources(
|
||||
this._data.resources.resources
|
||||
);
|
||||
this._jsonManager.setResources(
|
||||
this._data.resources.resources
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the additional options passed to the RuntimeGame when created.
|
||||
* @returns {?Object} The additional options, if any.
|
||||
* @returns {?RuntimeGameOptions} The additional options, if any.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getAdditionalOptions = function() {
|
||||
return this._options;
|
||||
|
@@ -99,6 +99,12 @@ gdjs.RuntimeObject = function(runtimeScene, objectData) {
|
||||
* @type {gdjs.RuntimeScene}
|
||||
*/
|
||||
this._runtimeScene = runtimeScene;
|
||||
/**
|
||||
* An optional UUID associated to the object to be used
|
||||
* for hot reload. Don't modify or use otherwise.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.persistentUuid = null;
|
||||
|
||||
//Hit boxes:
|
||||
if ( this._defaultHitBoxes === undefined ) {
|
||||
@@ -270,6 +276,19 @@ gdjs.RuntimeObject.prototype.extraInitializationFromInitialInstance = function(i
|
||||
//Nothing to do.
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the object must be updated using the specified objectData. This is the
|
||||
* case during hot-reload, and is only called if the object was modified.
|
||||
*
|
||||
* @param {ObjectData} oldObjectData The previous data for the object.
|
||||
* @param {ObjectData} newObjectData The new data for the object.
|
||||
* @returns {boolean} true if the object was updated, false if it could not (i.e: hot-reload is not supported).
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
// If not redefined, mark by default the hot-reload as failed.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an object from a scene.
|
||||
*
|
||||
@@ -665,7 +684,7 @@ gdjs.RuntimeObject.prototype.hide = function(enable) {
|
||||
/**
|
||||
* Return true if the object is not hidden.
|
||||
*
|
||||
* Note: This is unrelated to the actual visibility of the objec on the screen.
|
||||
* Note: This is unrelated to the actual visibility of the object on the screen.
|
||||
* For this, see `getVisibilityAABB` to get the bounding boxes of the object as displayed
|
||||
* on the scene.
|
||||
*
|
||||
@@ -1042,13 +1061,25 @@ gdjs.RuntimeObject.prototype.stepBehaviorsPostEvents = function(runtimeScene) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the object was hot reloaded, to notify behaviors
|
||||
* that the object was modified. Useful for behaviors that
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.notifyBehaviorsObjectHotReloaded = function() {
|
||||
for(var i = 0, len = this._behaviors.length;i<len;++i) {
|
||||
this._behaviors[i].onObjectHotReloaded();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a behavior from its name.
|
||||
* If the behavior does not exists, `undefined` is returned.
|
||||
*
|
||||
* Be careful, the behavior must exists, no check is made on the name.
|
||||
* **Never keep a reference** to a behavior, as they can be hot-reloaded. Instead,
|
||||
* always call getBehavior on the object.
|
||||
*
|
||||
* @param name {String} The behavior name.
|
||||
* @return {gdjs.RuntimeBehavior} The behavior with the given name, or undefined.
|
||||
* @return {gdjs.RuntimeBehavior?} The behavior with the given name, or undefined.
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.getBehavior = function(name) {
|
||||
return this._behaviorsTable.get(name);
|
||||
@@ -1078,7 +1109,7 @@ gdjs.RuntimeObject.prototype.activateBehavior = function(name, enable) {
|
||||
/**
|
||||
* Check if a behavior is activated
|
||||
*
|
||||
* @param name {String} The behavior name.
|
||||
* @param {string} name The behavior name.
|
||||
* @return true if the behavior is activated.
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.behaviorActivated = function(name) {
|
||||
@@ -1089,6 +1120,39 @@ gdjs.RuntimeObject.prototype.behaviorActivated = function(name) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the behavior with the given name.
|
||||
*
|
||||
* @param {string} name The name of the behavior to remove.
|
||||
* @returns {boolean} true if the behavior was properly removed, false otherwise.
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.removeBehavior = function(name) {
|
||||
var behavior = this._behaviorsTable.get(name);
|
||||
if (!behavior) return false;
|
||||
|
||||
var behaviorIndex = this._behaviors.indexOf(behavior);
|
||||
if (behaviorIndex !== -1) this._behaviors.splice(behaviorIndex, 1);
|
||||
this._behaviorsTable.remove(name);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the behavior decribed by the given BehaviorData
|
||||
*
|
||||
* @param {BehaviorData} behaviorData The data to be used to construct the behavior.
|
||||
* @returns {boolean} true if the behavior was properly created, false otherwise.
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.addNewBehavior = function(behaviorData) {
|
||||
var Ctor = gdjs.getBehaviorConstructor(behaviorData.type);
|
||||
if (!Ctor) return false;
|
||||
|
||||
var newRuntimeBehavior = new Ctor(this._runtimeScene, behaviorData, this);
|
||||
this._behaviors.push(newRuntimeBehavior);
|
||||
this._behaviorsTable.put(behaviorData.name, newRuntimeBehavior);
|
||||
return true;
|
||||
};
|
||||
|
||||
//Timers:
|
||||
|
||||
/**
|
||||
|
@@ -85,10 +85,7 @@ gdjs.RuntimeScene.prototype.loadFromScene = function(sceneData) {
|
||||
|
||||
//Load layers
|
||||
for(var i = 0, len = sceneData.layers.length;i<len;++i) {
|
||||
var layerData = sceneData.layers[i];
|
||||
|
||||
this._layers.put(layerData.name, new gdjs.Layer(layerData, this));
|
||||
//console.log("Created layer : \""+name+"\".");
|
||||
this.addLayer(sceneData.layers[i]);
|
||||
}
|
||||
|
||||
//Load variables
|
||||
@@ -96,10 +93,9 @@ gdjs.RuntimeScene.prototype.loadFromScene = function(sceneData) {
|
||||
|
||||
//Cache the initial shared data of the behaviors
|
||||
for(var i = 0, len = sceneData.behaviorsSharedData.length;i<len;++i) {
|
||||
var data = sceneData.behaviorsSharedData[i];
|
||||
var behaviorSharedData = sceneData.behaviorsSharedData[i];
|
||||
|
||||
//console.log("Initializing shared data for "+data.name);
|
||||
this._initialBehaviorSharedData.put(data.name, data);
|
||||
this.setInitialSharedDataForBehavior(behaviorSharedData.name, behaviorSharedData);
|
||||
}
|
||||
|
||||
//Registering objects: Global objects first...
|
||||
@@ -114,16 +110,10 @@ gdjs.RuntimeScene.prototype.loadFromScene = function(sceneData) {
|
||||
}
|
||||
|
||||
//Create initial instances of objects
|
||||
this.createObjectsFrom(sceneData.instances, 0, 0);
|
||||
this.createObjectsFrom(sceneData.instances, 0, 0, /*trackByPersistentUuid=*/ true);
|
||||
|
||||
//Set up the function to be executed at each tick
|
||||
var module = gdjs[sceneData.mangledName+"Code"];
|
||||
if ( module && module.func )
|
||||
this._eventsFunction = module.func;
|
||||
else {
|
||||
console.log("Warning: no function found for running logic of scene " + this._name);
|
||||
this._eventsFunction = (function() {});
|
||||
}
|
||||
this.setEventsGeneratedCodeFunction(sceneData);
|
||||
|
||||
this._onceTriggers = new gdjs.OnceTriggers();
|
||||
|
||||
@@ -185,6 +175,29 @@ gdjs.RuntimeScene.prototype.updateObject = function(objectData) {
|
||||
// Don't erase instances, nor instances cache, or objectsCtor cache.
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a {@link gdjs.RuntimeObject}. Instances will be destroyed.
|
||||
* @param {string} objectName The name of the object to unregister.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.unregisterObject = function(objectName) {
|
||||
var instances = this._instances.get(objectName);
|
||||
if (instances) {
|
||||
// This is sub-optimal: markObjectForDeletion will search the instance to
|
||||
// remove in instances, so cost is O(n^2), n being the number of instances.
|
||||
// As we're unregistering an object which only happen during a hot-reloading,
|
||||
// this is fine.
|
||||
var instancesToRemove = instances.slice();
|
||||
for(var i = 0;i<instancesToRemove.length;i++) {
|
||||
this.markObjectForDeletion(instancesToRemove[i]);
|
||||
}
|
||||
this._cacheOrClearRemovedInstances();
|
||||
}
|
||||
this._objects.remove(objectName);
|
||||
this._instances.remove(objectName);
|
||||
this._instancesCache.put(objectName);
|
||||
this._objectsCtor.remove(objectName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a scene is "paused", i.e it will be not be rendered again
|
||||
* for some time, until it's resumed or unloaded.
|
||||
@@ -269,14 +282,22 @@ gdjs.RuntimeScene.prototype.unloadScene = function() {
|
||||
* @param {InstanceData[]} data The instances data
|
||||
* @param {number} xPos The offset on X axis
|
||||
* @param {number} yPos The offset on Y axis
|
||||
* @param {boolean} trackByPersistentUuid If true, objects are tracked by setting their persistentUuid
|
||||
* to the same as the associated instance. Useful for hot-reloading when instances are changed.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.createObjectsFrom = function(data, xPos, yPos) {
|
||||
gdjs.RuntimeScene.prototype.createObjectsFrom = function(data, xPos, yPos, trackByPersistentUuid) {
|
||||
for(var i = 0, len = data.length;i<len;++i) {
|
||||
var instanceData = data[i];
|
||||
var objectName = instanceData.name;
|
||||
var newObject = this.createObject(objectName);
|
||||
|
||||
if ( newObject !== null ) {
|
||||
if (trackByPersistentUuid) {
|
||||
// Give the object the same persistentUuid as the instance, so that
|
||||
// it can be hot-reloaded.
|
||||
newObject.persistentUuid = instanceData.persistentUuid || null;
|
||||
}
|
||||
|
||||
newObject.setPosition(parseFloat(instanceData.x) + xPos, parseFloat(instanceData.y) + yPos);
|
||||
newObject.setZOrder(parseFloat(instanceData.zOrder));
|
||||
newObject.setAngle(parseFloat(instanceData.angle));
|
||||
@@ -287,6 +308,24 @@ gdjs.RuntimeScene.prototype.createObjectsFrom = function(data, xPos, yPos) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set the function called each time the scene is stepped to be the events generated code,
|
||||
* which is by convention assumed to be a function in `gdjs` with a name based on the scene
|
||||
* mangled name.
|
||||
*
|
||||
* @param {LayoutData} sceneData The scene data, used to find where the code was generated.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.setEventsGeneratedCodeFunction = function(sceneData) {
|
||||
var module = gdjs[sceneData.mangledName+"Code"];
|
||||
if (module && module.func)
|
||||
this._eventsFunction = module.func;
|
||||
else {
|
||||
console.log("Warning: no function found for running logic of scene " + this._name);
|
||||
this._eventsFunction = (function() {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the function called each time the scene is stepped.
|
||||
* The function will be passed the `runtimeScene` as argument.
|
||||
@@ -710,16 +749,27 @@ gdjs.RuntimeScene.prototype.getVariables = function() {
|
||||
/**
|
||||
* Get the data representing the initial shared data of the scene for the specified behavior.
|
||||
* @param {string} name The name of the behavior
|
||||
* @returns {?BehaviorSharedData} The shared data for the behavior, if any.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getInitialSharedDataForBehavior = function(name) {
|
||||
if ( this._initialBehaviorSharedData.containsKey(name) ) {
|
||||
return this._initialBehaviorSharedData.get(name);
|
||||
const behaviorSharedData = this._initialBehaviorSharedData.get(name);
|
||||
if (behaviorSharedData) {
|
||||
return behaviorSharedData;
|
||||
}
|
||||
|
||||
console.error("Can't find shared data for behavior with name:", name);
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the data representing the initial shared data of the scene for the specified behavior.
|
||||
* @param {string} name The name of the behavior
|
||||
* @param {?BehaviorSharedData} behaviorSharedData The shared data for the behavior, or null to remove it.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.setInitialSharedDataForBehavior = function(name, sharedData) {
|
||||
this._initialBehaviorSharedData.put(name, sharedData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the layer with the given name
|
||||
* @param {string} name The name of the layer
|
||||
@@ -740,6 +790,36 @@ gdjs.RuntimeScene.prototype.hasLayer = function(name) {
|
||||
return this._layers.containsKey(name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a layer.
|
||||
* @param {string} name The name of the layer
|
||||
* @param {LayerData} layerData The data to construct the layer
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.addLayer = function(layerData) {
|
||||
this._layers.put(layerData.name, new gdjs.Layer(layerData, this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a layer. All {@link gdjs.RuntimeObject} on this layer will
|
||||
* be moved back to the base layer.
|
||||
* @param {string} layerName The name of the layer to remove
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.removeLayer = function(layerName) {
|
||||
var allInstances = this.getAdhocListOfAllInstances();
|
||||
for(var i = 0;i < allInstances.length;++i) {
|
||||
var runtimeObject = allInstances[i];
|
||||
if (runtimeObject.getLayer() === layerName) {
|
||||
runtimeObject.setLayer("");
|
||||
}
|
||||
}
|
||||
|
||||
this._layers.remove(layerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the array passed as argument with the names of all layers
|
||||
* @param {string[]} result The array where to put the layer names
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getAllLayerNames = function(result) {
|
||||
this._layers.keys(result);
|
||||
};
|
||||
|
@@ -98,11 +98,11 @@ gdjs.SceneStack.prototype.push = function(newSceneName, externalLayoutName) {
|
||||
newScene.loadFromScene(this._runtimeGame.getSceneData(newSceneName));
|
||||
this._wasFirstSceneLoaded = true;
|
||||
|
||||
//Optionally create the objects from an external layout.
|
||||
// Optionally create the objects from an external layout.
|
||||
if (externalLayoutName) {
|
||||
var externalLayoutData = this._runtimeGame.getExternalLayoutData(externalLayoutName);
|
||||
if (externalLayoutData)
|
||||
newScene.createObjectsFrom(externalLayoutData.instances, 0, 0);
|
||||
newScene.createObjectsFrom(externalLayoutData.instances, 0, 0, /*trackByPersistentUuid=*/ true);
|
||||
}
|
||||
|
||||
this._stack.push(newScene);
|
||||
|
@@ -308,6 +308,30 @@ gdjs.registerObject("Sprite", gdjs.SpriteRuntimeObject); //Notify gdjs of the ob
|
||||
|
||||
//Others initialization and internal state management :
|
||||
|
||||
/**
|
||||
* @param {SpriteObjectData} oldObjectData
|
||||
* @param {SpriteObjectData} newObjectData
|
||||
*/
|
||||
gdjs.SpriteRuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) {
|
||||
var runtimeScene = this._runtimeScene;
|
||||
for(var i = 0, len = newObjectData.animations.length;i<len;++i) {
|
||||
var animData = newObjectData.animations[i];
|
||||
|
||||
if ( i < this._animations.length )
|
||||
gdjs.SpriteAnimation.call(this._animations[i], runtimeScene.getGame().getImageManager(), animData);
|
||||
else
|
||||
this._animations.push(new gdjs.SpriteAnimation(runtimeScene.getGame().getImageManager(), animData));
|
||||
}
|
||||
this._animations.length = i; //Make sure to delete already existing animations which are not used anymore.
|
||||
|
||||
this._updateAnimationFrame();
|
||||
if (!this._animationFrame) {
|
||||
this.setAnimation(0);
|
||||
}
|
||||
this.hitBoxesDirty = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @param {{numberProperties: Array<{name: string, value: number}>, customSize: {width: number, height: number}}} initialInstanceData The extra parameters
|
||||
|
@@ -19,4 +19,5 @@
|
||||
* @typedef { import("./project-data").EffectData } EffectData
|
||||
* @typedef { import("./project-data").ResourceData } ResourceData
|
||||
* @typedef { import("./project-data").ResourcesData } ResourcesData
|
||||
* @typedef { import("./project-data").BehaviorSharedData } BehaviorSharedData
|
||||
*/
|
||||
|
6
GDJS/Runtime/types/project-data.d.ts
vendored
@@ -44,10 +44,10 @@ export interface LayoutData {
|
||||
instances: InstanceData[];
|
||||
objects: ObjectData[];
|
||||
layers: LayerData[];
|
||||
behaviorsSharedData: BehaviorsSharedDatum[];
|
||||
behaviorsSharedData: BehaviorSharedData[];
|
||||
}
|
||||
|
||||
export interface BehaviorsSharedDatum {
|
||||
export interface BehaviorSharedData {
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
@@ -158,6 +158,7 @@ export interface ResourceData {
|
||||
name: string;
|
||||
smoothed?: boolean;
|
||||
userAdded: boolean;
|
||||
disablePreload?: boolean;
|
||||
}
|
||||
|
||||
export enum ResourceKind {
|
||||
@@ -165,4 +166,5 @@ export enum ResourceKind {
|
||||
Image = 'image',
|
||||
Font = 'font',
|
||||
Video = 'video',
|
||||
Json = 'json',
|
||||
}
|
||||
|
@@ -110,6 +110,21 @@ gdjs.Variable.prototype.getChild = function(childName) {
|
||||
return this._children[childName];
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a child variable with the specified name.
|
||||
*
|
||||
* If there is an existing child variable with this name, it is erased.
|
||||
* @method
|
||||
* @param {string} childName The name of the variable to add
|
||||
* @param {gdjs.Variable} childVariable The variable to add as a child
|
||||
* @returns {gdjs.Variable} The variable (for chaining calls)
|
||||
*/
|
||||
gdjs.Variable.prototype.addChild = function(childName, childVariable) {
|
||||
this._isStructure = true;
|
||||
this._children[childName] = childVariable;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the child in a variable.
|
||||
*
|
||||
|
@@ -17,7 +17,10 @@
|
||||
gdjs.VariablesContainer = function(initialVariablesData)
|
||||
{
|
||||
if ( this._variables === undefined ) this._variables = new Hashtable();
|
||||
if ( this._variablesArray === undefined ) this._variablesArray = [];
|
||||
if ( this._variablesArray === undefined ) {
|
||||
/** @type {gdjs.Variable[]} */
|
||||
this._variablesArray = [];
|
||||
}
|
||||
|
||||
if ( initialVariablesData !== undefined ) this.initFrom(initialVariablesData);
|
||||
};
|
||||
@@ -81,11 +84,28 @@ gdjs.VariablesContainer.prototype.initFrom = function(data, keepOldVariables) {
|
||||
|
||||
/**
|
||||
* Add a new variable.
|
||||
* This can be costly, don't use in performance sensitive paths.
|
||||
*
|
||||
* @param {string} name Variable name
|
||||
* @param {gdjs.Variable} variable The variable to be added
|
||||
* @param {gdjs.Variable} newVariable The variable to be added
|
||||
*/
|
||||
gdjs.VariablesContainer.prototype.add = function(name, variable) {
|
||||
this._variables.put(name, variable);
|
||||
gdjs.VariablesContainer.prototype.add = function(name, newVariable) {
|
||||
var oldVariable = this._variables.get(name);
|
||||
|
||||
// Variable is either already defined, considered as undefined
|
||||
// in the container or missing in the container.
|
||||
// Whatever the case, replace it by the new.
|
||||
this._variables.put(name, newVariable);
|
||||
|
||||
if (oldVariable) {
|
||||
// If variable is indexed, ensure that the variable as the index
|
||||
// is replaced too. This can be costly (indexOf) but we assume `add` is not
|
||||
// used in performance sensitive code.
|
||||
var variableIndex = this._variablesArray.indexOf(oldVariable);
|
||||
if (variableIndex !== -1) {
|
||||
this._variablesArray[variableIndex] = newVariable;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
1326
GDJS/Runtime/websocket-debugger-client/hot-reloader.js
Normal file
@@ -1,4 +1,4 @@
|
||||
/// @ts-check
|
||||
// @ts-check
|
||||
/**
|
||||
* An client side implementation of the Debugger
|
||||
* @interface
|
||||
@@ -54,6 +54,7 @@ gdjs.IDebuggerClient.prototype.sendProfilerOutput = function(framesAverageMeasur
|
||||
*/
|
||||
gdjs.WebsocketDebuggerClient = function(runtimeGame) {
|
||||
this._runtimegame = runtimeGame;
|
||||
this._hotReloader = new gdjs.HotReloader(runtimeGame);
|
||||
|
||||
if (typeof WebSocket === 'undefined') {
|
||||
console.log("WebSocket is not defined, debugger won't work");
|
||||
@@ -114,6 +115,10 @@ gdjs.WebsocketDebuggerClient = function(runtimeGame) {
|
||||
that.sendProfilerStarted();
|
||||
} else if (data.command === 'profiler.stop') {
|
||||
runtimeGame.stopCurrentSceneProfiler();
|
||||
} else if (data.command === 'hotReload') {
|
||||
that._hotReloader.hotReload().then((logs) => {
|
||||
that.sendHotReloaderLogs(logs);
|
||||
});
|
||||
} else {
|
||||
console.info(
|
||||
'Unknown command "' + data.command + '" received by the debugger.'
|
||||
@@ -269,6 +274,20 @@ gdjs.WebsocketDebuggerClient.prototype.sendRuntimeGameDump = function() {
|
||||
this._ws.send(stringifiedMessage);
|
||||
};
|
||||
|
||||
gdjs.WebsocketDebuggerClient.prototype.sendHotReloaderLogs = function(logs) {
|
||||
if (!this._ws) {
|
||||
console.warn('No connection to debugger opened');
|
||||
return;
|
||||
}
|
||||
|
||||
this._ws.send(
|
||||
this._circularSafeStringify({
|
||||
command: 'hotReloader.logs',
|
||||
payload: logs,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
gdjs.WebsocketDebuggerClient.prototype.sendProfilerStarted = function() {
|
||||
if (!this._ws) {
|
||||
console.warn('No connection to debugger opened');
|
||||
|
@@ -44,6 +44,7 @@ module.exports = function(config) {
|
||||
'../Runtime/events-tools/storagetools.js',
|
||||
'../Runtime/events-tools/stringtools.js',
|
||||
'../Runtime/events-tools/windowtools.js',
|
||||
'../Runtime/websocket-debugger-client/hot-reloader.js',
|
||||
|
||||
//Extensions:
|
||||
'../../Extensions/DraggableBehavior/draggableruntimebehavior.js',
|
||||
|
365
GDJS/tests/tests/hot-reloader.js
Normal file
@@ -0,0 +1,365 @@
|
||||
// @ts-check
|
||||
/**
|
||||
* Tests for gdjs.InputManager and related.
|
||||
*/
|
||||
|
||||
describe('gdjs.HotReloader.deepEqual', () => {
|
||||
it('compares object, arrays, and primitives', () => {
|
||||
expect(gdjs.HotReloader.deepEqual('a', 'a')).to.be(true);
|
||||
expect(gdjs.HotReloader.deepEqual('a', 'b')).to.be(false);
|
||||
|
||||
expect(gdjs.HotReloader.deepEqual(1, 1)).to.be(true);
|
||||
expect(gdjs.HotReloader.deepEqual(1, 2)).to.be(false);
|
||||
|
||||
expect(gdjs.HotReloader.deepEqual(true, true)).to.be(true);
|
||||
expect(gdjs.HotReloader.deepEqual(true, false)).to.be(false);
|
||||
|
||||
expect(gdjs.HotReloader.deepEqual({ a: 1 }, { a: 1 })).to.be(true);
|
||||
expect(gdjs.HotReloader.deepEqual({ a: 1, b: 2 }, { a: 1, b: 2 })).to.be(
|
||||
true
|
||||
);
|
||||
expect(gdjs.HotReloader.deepEqual({ a: 1 }, { a: 2 })).to.be(false);
|
||||
expect(gdjs.HotReloader.deepEqual({ a: 1 }, { b: 2 })).to.be(false);
|
||||
expect(gdjs.HotReloader.deepEqual({ a: 1 }, { a: 1, b: 2 })).to.be(false);
|
||||
expect(gdjs.HotReloader.deepEqual({ a: 1, b: 2 }, { a: 1 })).to.be(false);
|
||||
|
||||
expect(gdjs.HotReloader.deepEqual([1], [1])).to.be(true);
|
||||
expect(gdjs.HotReloader.deepEqual([1, 2], [1, 2])).to.be(true);
|
||||
expect(gdjs.HotReloader.deepEqual([1, { a: 1 }], [1, { a: 1 }])).to.be(
|
||||
true
|
||||
);
|
||||
expect(gdjs.HotReloader.deepEqual([1], [2])).to.be(false);
|
||||
expect(gdjs.HotReloader.deepEqual([1, 2], [2])).to.be(false);
|
||||
expect(gdjs.HotReloader.deepEqual([1, { a: 1 }], [1, { a: 2 }])).to.be(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('hot-reloads variables', () => {
|
||||
const runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
resources: { resources: [] },
|
||||
// @ts-ignore
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
});
|
||||
const hotReloader = new gdjs.HotReloader(runtimeGame);
|
||||
const variablesContainer = new gdjs.VariablesContainer([]);
|
||||
|
||||
// Add a new variable
|
||||
/** @type {VariableData[]} */
|
||||
const dataWithMyVariable = [
|
||||
{
|
||||
name: 'MyVariable',
|
||||
value: '123',
|
||||
},
|
||||
];
|
||||
hotReloader._hotReloadVariablesContainer(
|
||||
[],
|
||||
dataWithMyVariable,
|
||||
variablesContainer
|
||||
);
|
||||
expect(variablesContainer.has('MyVariable')).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').getAsNumber()).to.be(123);
|
||||
|
||||
// Remove a new variable
|
||||
hotReloader._hotReloadVariablesContainer(
|
||||
dataWithMyVariable,
|
||||
[],
|
||||
variablesContainer
|
||||
);
|
||||
expect(variablesContainer.has('MyVariable')).to.be(false);
|
||||
|
||||
// Change a variable
|
||||
/** @type {VariableData[]} */
|
||||
const dataWithMyVariableAsString = [
|
||||
{
|
||||
name: 'MyVariable',
|
||||
value: 'Hello World',
|
||||
},
|
||||
];
|
||||
hotReloader._hotReloadVariablesContainer(
|
||||
dataWithMyVariable,
|
||||
dataWithMyVariableAsString,
|
||||
variablesContainer
|
||||
);
|
||||
expect(variablesContainer.has('MyVariable')).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').getAsString()).to.be(
|
||||
'Hello World'
|
||||
);
|
||||
|
||||
// Add a new structure
|
||||
/** @type {VariableData[]} */
|
||||
const dataWithMyVariableAsStructure = [
|
||||
{
|
||||
name: 'MyVariable',
|
||||
children: [
|
||||
{
|
||||
name: 'MyChild1',
|
||||
value: '123',
|
||||
},
|
||||
{
|
||||
name: 'MyChild2',
|
||||
value: 'Hello World',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
hotReloader._hotReloadVariablesContainer(
|
||||
[],
|
||||
dataWithMyVariableAsStructure,
|
||||
variablesContainer
|
||||
);
|
||||
expect(variablesContainer.has('MyVariable')).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').isStructure()).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild1')).to.be(
|
||||
true
|
||||
);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild2')).to.be(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild1').getAsNumber()
|
||||
).to.be(123);
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild2').getAsString()
|
||||
).to.be('Hello World');
|
||||
|
||||
// Change a variable in a structure
|
||||
/** @type {VariableData[]} */
|
||||
const dataWithMyVariableAsStructure2 = [
|
||||
{
|
||||
name: 'MyVariable',
|
||||
children: [
|
||||
{
|
||||
name: 'MyChild1',
|
||||
value: '124',
|
||||
},
|
||||
{
|
||||
name: 'MyChild2',
|
||||
value: 'Hello World 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
hotReloader._hotReloadVariablesContainer(
|
||||
dataWithMyVariableAsStructure,
|
||||
dataWithMyVariableAsStructure2,
|
||||
variablesContainer
|
||||
);
|
||||
expect(variablesContainer.has('MyVariable')).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').isStructure()).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild1')).to.be(
|
||||
true
|
||||
);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild2')).to.be(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild1').getAsNumber()
|
||||
).to.be(124);
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild2').getAsString()
|
||||
).to.be('Hello World 2');
|
||||
|
||||
// Remove a child variable
|
||||
/** @type {VariableData[]} */
|
||||
const dataWithMyVariableAsStructureWithoutChild1 = [
|
||||
{
|
||||
name: 'MyVariable',
|
||||
children: [
|
||||
{
|
||||
name: 'MyChild2',
|
||||
value: 'Hello World 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
hotReloader._hotReloadVariablesContainer(
|
||||
dataWithMyVariableAsStructure2,
|
||||
dataWithMyVariableAsStructureWithoutChild1,
|
||||
variablesContainer
|
||||
);
|
||||
expect(variablesContainer.has('MyVariable')).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').isStructure()).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild1')).to.be(
|
||||
false
|
||||
);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild2')).to.be(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild2').getAsString()
|
||||
).to.be('Hello World 2');
|
||||
|
||||
// Add a child variable as a structure
|
||||
/** @type {VariableData[]} */
|
||||
const dataWithMyVariableAsStructureWithChild1AsStructure = [
|
||||
{
|
||||
name: 'MyVariable',
|
||||
children: [
|
||||
{
|
||||
name: 'MyChild1',
|
||||
children: [
|
||||
{
|
||||
name: 'MyGrandChild1',
|
||||
value: '456',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'MyChild2',
|
||||
value: 'Hello World 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
hotReloader._hotReloadVariablesContainer(
|
||||
dataWithMyVariableAsStructureWithoutChild1,
|
||||
dataWithMyVariableAsStructureWithChild1AsStructure,
|
||||
variablesContainer
|
||||
);
|
||||
expect(variablesContainer.has('MyVariable')).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').isStructure()).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild1')).to.be(
|
||||
true
|
||||
);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild2')).to.be(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild2').getAsString()
|
||||
).to.be('Hello World 2');
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild1').isStructure()
|
||||
).to.be(true);
|
||||
expect(
|
||||
variablesContainer
|
||||
.get('MyVariable')
|
||||
.getChild('MyChild1')
|
||||
.hasChild('MyGrandChild1')
|
||||
).to.be(true);
|
||||
expect(
|
||||
variablesContainer
|
||||
.get('MyVariable')
|
||||
.getChild('MyChild1')
|
||||
.getChild('MyGrandChild1')
|
||||
.getAsNumber()
|
||||
).to.be(456);
|
||||
|
||||
// Modify a grand child variable
|
||||
/** @type {VariableData[]} */
|
||||
const dataWithMyVariableAsStructureWithChild1AsStructure2 = [
|
||||
{
|
||||
name: 'MyVariable',
|
||||
children: [
|
||||
{
|
||||
name: 'MyChild1',
|
||||
children: [
|
||||
{
|
||||
name: 'MyGrandChild1',
|
||||
value: '789',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'MyChild2',
|
||||
value: 'Hello World 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
hotReloader._hotReloadVariablesContainer(
|
||||
dataWithMyVariableAsStructureWithChild1AsStructure,
|
||||
dataWithMyVariableAsStructureWithChild1AsStructure2,
|
||||
variablesContainer
|
||||
);
|
||||
expect(variablesContainer.has('MyVariable')).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').isStructure()).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild1')).to.be(
|
||||
true
|
||||
);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild2')).to.be(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild2').getAsString()
|
||||
).to.be('Hello World 2');
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild1').isStructure()
|
||||
).to.be(true);
|
||||
expect(
|
||||
variablesContainer
|
||||
.get('MyVariable')
|
||||
.getChild('MyChild1')
|
||||
.hasChild('MyGrandChild1')
|
||||
).to.be(true);
|
||||
expect(
|
||||
variablesContainer
|
||||
.get('MyVariable')
|
||||
.getChild('MyChild1')
|
||||
.getChild('MyGrandChild1')
|
||||
.getAsNumber()
|
||||
).to.be(789);
|
||||
|
||||
// Remove a grand child variable and add another
|
||||
/** @type {VariableData[]} */
|
||||
const dataWithMyVariableAsStructureWithChild1AsStructure3 = [
|
||||
{
|
||||
name: 'MyVariable',
|
||||
children: [
|
||||
{
|
||||
name: 'MyChild1',
|
||||
children: [
|
||||
{
|
||||
name: 'MyGrandChild2',
|
||||
value: 'Hello World 3',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'MyChild2',
|
||||
value: 'Hello World 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
hotReloader._hotReloadVariablesContainer(
|
||||
dataWithMyVariableAsStructureWithChild1AsStructure2,
|
||||
dataWithMyVariableAsStructureWithChild1AsStructure3,
|
||||
variablesContainer
|
||||
);
|
||||
expect(variablesContainer.has('MyVariable')).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').isStructure()).to.be(true);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild1')).to.be(
|
||||
true
|
||||
);
|
||||
expect(variablesContainer.get('MyVariable').hasChild('MyChild2')).to.be(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild2').getAsString()
|
||||
).to.be('Hello World 2');
|
||||
expect(
|
||||
variablesContainer.get('MyVariable').getChild('MyChild1').isStructure()
|
||||
).to.be(true);
|
||||
expect(
|
||||
variablesContainer
|
||||
.get('MyVariable')
|
||||
.getChild('MyChild1')
|
||||
.hasChild('MyGrandChild1')
|
||||
).to.be(false);
|
||||
expect(
|
||||
variablesContainer
|
||||
.get('MyVariable')
|
||||
.getChild('MyChild1')
|
||||
.hasChild('MyGrandChild2')
|
||||
).to.be(true);
|
||||
expect(
|
||||
variablesContainer
|
||||
.get('MyVariable')
|
||||
.getChild('MyChild1')
|
||||
.getChild('MyGrandChild2')
|
||||
.getAsString()
|
||||
).to.be('Hello World 3');
|
||||
});
|
||||
});
|
@@ -140,7 +140,7 @@ describe('gdjs.RuntimeObject.cursorOnObject', function() {
|
||||
});
|
||||
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers: [{ name: '', visibility: true }],
|
||||
layers: [{ name: '', visibility: true, effects: [] }],
|
||||
variables: [],
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
|
@@ -1,17 +1,24 @@
|
||||
// @ts-check
|
||||
/**
|
||||
* Test for gdjs.RuntimeScene
|
||||
*/
|
||||
|
||||
describe('gdjs.RuntimeScene integration tests', function() {
|
||||
describe('gdjs.RuntimeScene integration tests', function () {
|
||||
describe('Object and behavior lifecycles (using TestObject and TestBehavior)', function () {
|
||||
it('should properly create and destroy object, including the behaviors', function() {
|
||||
const runtimeGame = new gdjs.RuntimeGame({variables: [], properties: {windowWidth: 800, windowHeight: 600}, resources: {resources: []}});
|
||||
it('should properly create and destroy object, including the behaviors', function () {
|
||||
const runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
// @ts-ignore
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
resources: { resources: [] },
|
||||
});
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers:[{name:"", visibility: true}],
|
||||
layers: [{ name: '', visibility: true, effects: [] }],
|
||||
variables: [],
|
||||
behaviorsSharedData: [],
|
||||
objects: [{
|
||||
objects: [
|
||||
{
|
||||
type: 'TestObject::TestObject',
|
||||
name: 'Object1',
|
||||
behaviors: [
|
||||
@@ -19,40 +26,92 @@ describe('gdjs.RuntimeScene integration tests', function() {
|
||||
type: 'TestBehavior::TestBehavior',
|
||||
},
|
||||
],
|
||||
}],
|
||||
instances: []
|
||||
},
|
||||
],
|
||||
instances: [],
|
||||
});
|
||||
|
||||
const object = runtimeScene.createObject('Object1');
|
||||
|
||||
// Check that the behavior was properly created
|
||||
expect(
|
||||
object
|
||||
.getVariables()
|
||||
.get('lastState')
|
||||
.getAsString()
|
||||
).to.eql('created');
|
||||
expect(object.getVariables().get('lastState').getAsString()).to.eql(
|
||||
'created'
|
||||
);
|
||||
|
||||
// Check that the behaviors are properly destroyed
|
||||
runtimeScene.markObjectForDeletion(object);
|
||||
expect(
|
||||
object
|
||||
.getVariables()
|
||||
.get('lastState')
|
||||
.getAsString()
|
||||
).to.eql('onDestroy');
|
||||
expect(object.getVariables().get('lastState').getAsString()).to.eql(
|
||||
'onDestroy'
|
||||
);
|
||||
|
||||
const object2 = runtimeScene.createObject('Object1');
|
||||
|
||||
// Check that the behaviors are properly destroyed
|
||||
runtimeScene.unloadScene();
|
||||
expect(
|
||||
object2
|
||||
.getVariables()
|
||||
.get('lastState')
|
||||
.getAsString()
|
||||
).to.eql('onDestroy');
|
||||
expect(object2.getVariables().get('lastState').getAsString()).to.eql(
|
||||
'onDestroy'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Layers (using a Sprite object)', function () {
|
||||
const runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
// @ts-ignore
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
resources: { resources: [] },
|
||||
});
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers: [
|
||||
{ name: '', visibility: true, effects: [] },
|
||||
{ name: 'MyLayer', visibility: true, effects: [] },
|
||||
],
|
||||
variables: [],
|
||||
behaviorsSharedData: [],
|
||||
objects: [
|
||||
{
|
||||
type: 'Sprite',
|
||||
name: 'MyObject',
|
||||
behaviors: [],
|
||||
animations: [],
|
||||
updateIfNotVisible: false,
|
||||
},
|
||||
],
|
||||
instances: [],
|
||||
});
|
||||
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyOtherLayer')).to.be(false);
|
||||
|
||||
const object1 = runtimeScene.createObject('MyObject');
|
||||
const object2 = runtimeScene.createObject('MyObject');
|
||||
const object3 = runtimeScene.createObject('MyObject');
|
||||
object2.setLayer('MyLayer');
|
||||
|
||||
runtimeScene.addLayer({
|
||||
name: 'MyOtherLayer',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
});
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyOtherLayer')).to.be(true);
|
||||
|
||||
object3.setLayer('MyOtherLayer');
|
||||
expect(object1.getLayer()).to.be('');
|
||||
expect(object2.getLayer()).to.be('MyLayer');
|
||||
expect(object3.getLayer()).to.be('MyOtherLayer');
|
||||
|
||||
runtimeScene.removeLayer("MyLayer");
|
||||
|
||||
expect(object1.getLayer()).to.be('');
|
||||
expect(object2.getLayer()).to.be('');
|
||||
expect(object3.getLayer()).to.be('MyOtherLayer');
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(false);
|
||||
expect(runtimeScene.hasLayer('MyOtherLayer')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
@@ -36,7 +36,7 @@ describe('gdjs.VariablesContainer', function() {
|
||||
expect(container.has('MyVariable3')).to.be(true);
|
||||
});
|
||||
|
||||
it('can be constructed from data', function() {
|
||||
it('can be constructed from data, so that variables are indexed', function() {
|
||||
const container = new gdjs.VariablesContainer([{
|
||||
name: 'Var1',
|
||||
value: 123
|
||||
@@ -65,7 +65,7 @@ describe('gdjs.VariablesContainer', function() {
|
||||
expect(container.getFromIndex(1).getAsString()).to.be('Hello World');
|
||||
expect(container.getFromIndex(2).isStructure()).to.be(true);
|
||||
|
||||
// Call initFrom to add more variables (overriding the existing ones)
|
||||
// Call initFrom to add more variables (not overriding the existing ones)
|
||||
container.initFrom([{
|
||||
name: 'Var4',
|
||||
value: 456
|
||||
@@ -107,6 +107,43 @@ describe('gdjs.VariablesContainer', function() {
|
||||
expect(container.get('Var5').getAsNumber()).to.be(789);
|
||||
expect(container.has('Var6')).to.be(true);
|
||||
expect(container.get('Var6').getAsString()).to.be('The Only Hello World');
|
||||
});
|
||||
|
||||
it('persists index of variables constructed from data', function() {
|
||||
const container = new gdjs.VariablesContainer([{
|
||||
name: 'Var1',
|
||||
value: 123
|
||||
},{
|
||||
name: 'Var2',
|
||||
value: 'Hello World'
|
||||
}, {
|
||||
name: 'Var3',
|
||||
children: [{
|
||||
name: 'Var3.1',
|
||||
value: 1,
|
||||
}]
|
||||
}]);
|
||||
|
||||
// Check that getFromIndex works (for faster lookup in case we know
|
||||
// the order of variables).
|
||||
expect(container.getFromIndex(0).getAsNumber()).to.be(123);
|
||||
expect(container.getFromIndex(1).getAsString()).to.be('Hello World');
|
||||
expect(container.getFromIndex(2).isStructure()).to.be(true);
|
||||
|
||||
// Remove a variable (that is indexed) and add it back
|
||||
container.remove('Var2');
|
||||
const newVar2 = new gdjs.Variable();
|
||||
newVar2.setNumber(456);
|
||||
container.add('Var2', newVar2);
|
||||
|
||||
// Also replace a variable (that is indexed)
|
||||
const newVar3 = new gdjs.Variable();
|
||||
newVar3.setNumber(789);
|
||||
container.add('Var3', newVar3);
|
||||
|
||||
// Verify that we can still access indexed variables using getFromIndex
|
||||
expect(container.getFromIndex(0).getAsNumber()).to.be(123);
|
||||
expect(container.getFromIndex(1).getAsNumber()).to.be(456);
|
||||
expect(container.getFromIndex(2).getAsNumber()).to.be(789);
|
||||
});
|
||||
});
|
||||
|
@@ -756,6 +756,8 @@ interface InitialInstance {
|
||||
void SetCustomHeight(float height);
|
||||
float GetCustomHeight();
|
||||
|
||||
[Ref] InitialInstance ResetPersistentUuid();
|
||||
|
||||
void UpdateCustomProperty([Const] DOMString name, [Const] DOMString value, [Ref] Project project, [Ref] Layout layout);
|
||||
[Value] MapStringPropertyDescriptor GetCustomProperties([Ref] Project project, [Ref] Layout layout);
|
||||
float GetRawFloatProperty([Const] DOMString name);
|
||||
@@ -2364,13 +2366,21 @@ interface EventsFunctionsExtensionCodeGenerator {
|
||||
[Const, Value] DOMString GenerateFreeEventsFunctionCompleteCode([Const, Ref] EventsFunction eventsFunction, [Const] DOMString codeNamespac, [Ref] SetString includes, boolean compilationForRuntime);
|
||||
};
|
||||
|
||||
[Prefix="gdjs::"]
|
||||
interface PreviewExportOptions {
|
||||
void PreviewExportOptions([Ref] Project project, [Const] DOMString outputPath);
|
||||
[Ref] PreviewExportOptions SetLayoutName([Const] DOMString layoutName);
|
||||
[Ref] PreviewExportOptions SetExternalLayoutName([Const] DOMString externalLayoutName);
|
||||
[Ref] PreviewExportOptions SetIncludeFileHash([Const] DOMString includeFile, long hash);
|
||||
[Ref] PreviewExportOptions SetProjectDataOnlyExport(boolean enable);
|
||||
};
|
||||
|
||||
[Prefix="gdjs::"]
|
||||
interface Exporter {
|
||||
void Exporter([Ref] AbstractFileSystem fs, [Const] DOMString gdjsRoot);
|
||||
void SetCodeOutputDirectory([Const] DOMString path);
|
||||
|
||||
boolean ExportLayoutForPixiPreview([Ref] Project project, [Ref] Layout layout, [Const] DOMString exportDir);
|
||||
boolean ExportExternalLayoutForPixiPreview([Ref] Project project, [Ref] Layout layout, [Ref] ExternalLayout externalLayout, [Const] DOMString exportDir);
|
||||
boolean ExportProjectForPixiPreview([Const, Ref] PreviewExportOptions options);
|
||||
boolean ExportWholePixiProject([Ref] Project project, [Const] DOMString exportDir, [Ref] MapStringBoolean exportOptions);
|
||||
|
||||
boolean ExportWholeCocos2dProject([Ref] Project project, boolean debugMode, [Const] DOMString exportDir);
|
||||
|
@@ -78,6 +78,7 @@
|
||||
#include <GDJS/Events/CodeGeneration/BehaviorCodeGenerator.h>
|
||||
#include <GDJS/Events/CodeGeneration/EventsFunctionsExtensionCodeGenerator.h>
|
||||
#include <GDJS/IDE/Exporter.h>
|
||||
#include <GDJS/IDE/ExporterHelper.h>
|
||||
|
||||
#include <emscripten.h>
|
||||
#include "ProjectHelper.h"
|
||||
|
@@ -593,6 +593,38 @@ describe('libGD.js', function() {
|
||||
expect(initialInstance2.getCustomWidth()).toBe(34);
|
||||
expect(initialInstance2.getCustomHeight()).toBe(30);
|
||||
});
|
||||
it('can be serialized with a persistent UUID called persistentUuid', function() {
|
||||
const initialInstance = new gd.InitialInstance();
|
||||
initialInstance.setObjectName("MyObject");
|
||||
|
||||
// Serialized object must have a non empty "persistentUuid".
|
||||
var element = new gd.SerializerElement();
|
||||
initialInstance.serializeTo(element);
|
||||
const persistentUuid = element.getStringAttribute("persistentUuid");
|
||||
expect(persistentUuid).toBeTruthy();
|
||||
|
||||
// The UUID must persist across serializations.
|
||||
const initialInstance2 = new gd.InitialInstance();
|
||||
initialInstance2.unserializeFrom(element);
|
||||
|
||||
var element2 = new gd.SerializerElement();
|
||||
initialInstance.serializeTo(element2);
|
||||
const persistentUuid2 = element2.getStringAttribute("persistentUuid");
|
||||
expect(persistentUuid2).toBe(persistentUuid);
|
||||
|
||||
// The UUID can be reset
|
||||
initialInstance2.resetPersistentUuid();
|
||||
var element3 = new gd.SerializerElement();
|
||||
initialInstance2.serializeTo(element3);
|
||||
const persistentUuid3 = element3.getStringAttribute("persistentUuid");
|
||||
expect(persistentUuid3).not.toBe(persistentUuid2);
|
||||
|
||||
element.delete();
|
||||
element2.delete();
|
||||
element3.delete();
|
||||
initialInstance.delete();
|
||||
initialInstance2.delete();
|
||||
});
|
||||
|
||||
afterAll(function() {
|
||||
project.delete();
|
||||
@@ -2609,8 +2641,12 @@ describe('libGD.js', function() {
|
||||
done();
|
||||
};
|
||||
|
||||
var exporter = new gd.Exporter(fs);
|
||||
exporter.exportLayoutForPixiPreview(project, layout, '/path/for/export/');
|
||||
const exporter = new gd.Exporter(fs);
|
||||
const previewExportOptions =
|
||||
new gd.PreviewExportOptions(project, '/path/for/export/');
|
||||
previewExportOptions.setLayoutName('Scene');
|
||||
exporter.exportProjectForPixiPreview(previewExportOptions);
|
||||
previewExportOptions.delete();
|
||||
exporter.delete();
|
||||
});
|
||||
});
|
||||
|
@@ -301,6 +301,107 @@ describe('libGD.js - GDJS Code Generation integration tests', function () {
|
||||
eventsFunction.delete();
|
||||
project.delete();
|
||||
});
|
||||
|
||||
it('generates a working function with BuiltinCommonInstructions::Once', function () {
|
||||
// Create events using the Trigger Once condition.
|
||||
const eventsSerializerElement = gd.Serializer.fromJSON(
|
||||
JSON.stringify([
|
||||
{
|
||||
disabled: false,
|
||||
folded: false,
|
||||
type: 'BuiltinCommonInstructions::Standard',
|
||||
conditions: [
|
||||
{
|
||||
type: {
|
||||
inverted: false,
|
||||
value: 'BuiltinCommonInstructions::Once',
|
||||
},
|
||||
parameters: [],
|
||||
subInstructions: [],
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: { inverted: false, value: 'ModVarScene' },
|
||||
parameters: ['SuccessVariable', '+', '1'],
|
||||
subInstructions: [],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
{
|
||||
disabled: false,
|
||||
folded: false,
|
||||
type: 'BuiltinCommonInstructions::Standard',
|
||||
conditions: [
|
||||
{
|
||||
type: {
|
||||
inverted: false,
|
||||
value: 'BuiltinCommonInstructions::Once',
|
||||
},
|
||||
parameters: [],
|
||||
subInstructions: [],
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: { inverted: false, value: 'ModVarScene' },
|
||||
parameters: ['SuccessVariable', '+', '1'],
|
||||
subInstructions: [],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
const project = new gd.ProjectHelper.createNewGDJSProject();
|
||||
const eventsFunction = new gd.EventsFunction();
|
||||
eventsFunction
|
||||
.getEvents()
|
||||
.unserializeFrom(project, eventsSerializerElement);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsForEventsFunction(
|
||||
gd,
|
||||
project,
|
||||
eventsFunction
|
||||
);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
||||
runtimeScene.getOnceTriggers().startNewFrame();
|
||||
runCompiledEvents(gdjs, runtimeScene);
|
||||
runtimeScene.getOnceTriggers().startNewFrame();
|
||||
runCompiledEvents(gdjs, runtimeScene);
|
||||
|
||||
// Check that after running the compiled events twice, actions were
|
||||
// executed only twice.
|
||||
expect(runtimeScene.getVariables().has('SuccessVariable')).toBe(true);
|
||||
expect(
|
||||
runtimeScene.getVariables().get('SuccessVariable').getAsNumber()
|
||||
).toBe(2);
|
||||
|
||||
// Check that compiling again the events will result in stable ids for Trigger Once conditions
|
||||
// (trigger once should not be launched again), even after a copy of the events function.
|
||||
const eventsFunctionCopy = eventsFunction.clone();
|
||||
const runCompiledEvents2 = generateCompiledEventsForEventsFunction(
|
||||
gd,
|
||||
project,
|
||||
eventsFunctionCopy
|
||||
);
|
||||
runtimeScene.getOnceTriggers().startNewFrame();
|
||||
runCompiledEvents2(gdjs, runtimeScene);
|
||||
runtimeScene.getOnceTriggers().startNewFrame();
|
||||
runCompiledEvents2(gdjs, runtimeScene);
|
||||
|
||||
expect(runtimeScene.getVariables().has('SuccessVariable')).toBe(true);
|
||||
expect(
|
||||
runtimeScene.getVariables().get('SuccessVariable').getAsNumber()
|
||||
).toBe(2);
|
||||
|
||||
eventsFunction.delete();
|
||||
eventsFunctionCopy.delete();
|
||||
project.delete();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -359,3 +460,24 @@ function generateCompiledEventsFromSerializedEvents(
|
||||
|
||||
return runCompiledEvents;
|
||||
}
|
||||
|
||||
/** Helper to create compiled events from serialized events, creating a project and the events function. */
|
||||
function generateCompiledEventsFromSerializedEvents(
|
||||
gd,
|
||||
eventsSerializerElement
|
||||
) {
|
||||
const project = new gd.ProjectHelper.createNewGDJSProject();
|
||||
const eventsFunction = new gd.EventsFunction();
|
||||
eventsFunction.getEvents().unserializeFrom(project, eventsSerializerElement);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsForEventsFunction(
|
||||
gd,
|
||||
project,
|
||||
eventsFunction
|
||||
);
|
||||
|
||||
eventsFunction.delete();
|
||||
project.delete();
|
||||
|
||||
return runCompiledEvents;
|
||||
}
|
||||
|
@@ -2,8 +2,7 @@
|
||||
declare class gdjsExporter {
|
||||
constructor(fs: gdAbstractFileSystem, gdjsRoot: string): void;
|
||||
setCodeOutputDirectory(path: string): void;
|
||||
exportLayoutForPixiPreview(project: gdProject, layout: gdLayout, exportDir: string): boolean;
|
||||
exportExternalLayoutForPixiPreview(project: gdProject, layout: gdLayout, externalLayout: gdExternalLayout, exportDir: string): boolean;
|
||||
exportProjectForPixiPreview(options: gdPreviewExportOptions): boolean;
|
||||
exportWholePixiProject(project: gdProject, exportDir: string, exportOptions: gdMapStringBoolean): boolean;
|
||||
exportWholeCocos2dProject(project: gdProject, debugMode: boolean, exportDir: string): boolean;
|
||||
getLastError(): string;
|
||||
|
@@ -21,6 +21,7 @@ declare class gdInitialInstance {
|
||||
getCustomWidth(): number;
|
||||
setCustomHeight(height: number): void;
|
||||
getCustomHeight(): number;
|
||||
resetPersistentUuid(): gdInitialInstance;
|
||||
updateCustomProperty(name: string, value: string, project: gdProject, layout: gdLayout): void;
|
||||
getCustomProperties(project: gdProject, layout: gdLayout): gdMapStringPropertyDescriptor;
|
||||
getRawFloatProperty(name: string): number;
|
||||
|
10
GDevelop.js/types/gdpreviewexportoptions.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// Automatically generated by GDevelop.js/scripts/generate-types.js
|
||||
declare class gdPreviewExportOptions {
|
||||
constructor(project: gdProject, outputPath: string): void;
|
||||
setLayoutName(layoutName: string): gdPreviewExportOptions;
|
||||
setExternalLayoutName(externalLayoutName: string): gdPreviewExportOptions;
|
||||
setIncludeFileHash(includeFile: string, hash: number): gdPreviewExportOptions;
|
||||
setProjectDataOnlyExport(enable: boolean): gdPreviewExportOptions;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
@@ -180,6 +180,7 @@ declare class libGDevelop {
|
||||
LayoutCodeGenerator: Class<gdLayoutCodeGenerator>;
|
||||
BehaviorCodeGenerator: Class<gdBehaviorCodeGenerator>;
|
||||
EventsFunctionsExtensionCodeGenerator: Class<gdEventsFunctionsExtensionCodeGenerator>;
|
||||
PreviewExportOptions: Class<gdPreviewExportOptions>;
|
||||
Exporter: Class<gdExporter>;
|
||||
JsCodeEvent: Class<gdJsCodeEvent>;
|
||||
};
|
21
newIDE/app/package-lock.json
generated
@@ -2363,11 +2363,11 @@
|
||||
}
|
||||
},
|
||||
"@material-ui/icons": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.2.1.tgz",
|
||||
"integrity": "sha512-FvSD5lUBJ66frI4l4AYAPy2CH14Zs2Dgm0o3oOMr33BdQtOAjCgbdOcvPBeaD1w6OQl31uNW3CKOE8xfPNxvUQ==",
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.9.1.tgz",
|
||||
"integrity": "sha512-GBitL3oBWO0hzBhvA9KxqcowRUsA0qzwKkURyC8nppnC3fw54KPKZ+d4V1Eeg/UnDRSzDaI9nGCdel/eh9AQMg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.2.0"
|
||||
"@babel/runtime": "^7.4.4"
|
||||
}
|
||||
},
|
||||
"@material-ui/lab": {
|
||||
@@ -7793,6 +7793,11 @@
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz",
|
||||
"integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q=="
|
||||
},
|
||||
"cuint": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
|
||||
"integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs="
|
||||
},
|
||||
"cyclist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
|
||||
@@ -25088,6 +25093,14 @@
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||
},
|
||||
"xxhashjs": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz",
|
||||
"integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==",
|
||||
"requires": {
|
||||
"cuint": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
|
||||
|
@@ -32,7 +32,7 @@
|
||||
"@blueprintjs/icons": "file:src/Utils/BlueprintJsPlaceholder",
|
||||
"@lingui/react": "git://github.com/4ian/lingui-react.git#master",
|
||||
"@material-ui/core": "4.9.10",
|
||||
"@material-ui/icons": "4.2.1",
|
||||
"@material-ui/icons": "4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.49",
|
||||
"algoliasearch": "3.33.0",
|
||||
"axios": "^0.18.1",
|
||||
@@ -72,7 +72,8 @@
|
||||
"slugs": "0.1.3",
|
||||
"source-map-explorer": "^2.0.1",
|
||||
"url-search-params": "^1.0.2",
|
||||
"wait-promise": "0.4.1"
|
||||
"wait-promise": "0.4.1",
|
||||
"xxhashjs": "^0.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "npm run import-resources && npm run make-version-metadata",
|
||||
|
BIN
newIDE/app/public/res/ribbon_default/hotReload64.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1.4 KiB |
BIN
newIDE/app/public/res/ribbon_default/pause64.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 560 B |
BIN
newIDE/app/public/res/ribbon_default/preview64.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 17 KiB |
BIN
newIDE/app/public/res/tutorial_icons/pause-menu.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
newIDE/app/public/res/tutorial_icons/push-objects.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
newIDE/app/public/res/tutorial_icons/responsive-ui.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
newIDE/app/public/res/tutorial_icons/save-and-load.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
newIDE/app/public/res/tutorial_icons/smooth-camera-movement.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
newIDE/app/public/res/tutorial_icons/tween-behavior.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { t } from '@lingui/macro';
|
||||
import React, { Component } from 'react';
|
||||
@@ -16,6 +17,14 @@ import { isNullPtr } from '../Utils/IsNullPtr';
|
||||
import Window from '../Utils/Window';
|
||||
import { Column, Line } from '../UI/Grid';
|
||||
import RaisedButton from '../UI/RaisedButton';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource.flow';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { getBehaviorTutorialHints } from '../Hints';
|
||||
import DismissableTutorialMessage from '../Hints/DismissableTutorialMessage';
|
||||
import { ColumnStackLayout } from '../UI/Layout';
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
const AddBehaviorLine = ({ onAdd }) => (
|
||||
@@ -31,25 +40,22 @@ const AddBehaviorLine = ({ onAdd }) => (
|
||||
</Column>
|
||||
);
|
||||
|
||||
export default class BehaviorsEditor extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { newBehaviorDialogOpen: false };
|
||||
}
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
object: gdObject,
|
||||
onUpdateBehaviorsSharedData: () => void,
|
||||
onSizeUpdated?: ?() => void,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
|};
|
||||
|
||||
componentWillMount() {
|
||||
this._loadFrom(this.props.object);
|
||||
}
|
||||
type State = {|
|
||||
newBehaviorDialogOpen: boolean,
|
||||
|};
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
if (this.props.object !== newProps.object) {
|
||||
this._loadFrom(newProps.object);
|
||||
}
|
||||
}
|
||||
|
||||
_loadFrom(object) {
|
||||
if (!object) return;
|
||||
}
|
||||
export default class BehaviorsEditor extends Component<Props, State> {
|
||||
state = { newBehaviorDialogOpen: false };
|
||||
|
||||
chooseNewBehavior = () => {
|
||||
this.setState({
|
||||
@@ -57,7 +63,7 @@ export default class BehaviorsEditor extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
_hasBehaviorWithType = type => {
|
||||
_hasBehaviorWithType = (type: string) => {
|
||||
const { object } = this.props;
|
||||
const allBehaviorNames = object.getAllBehaviorNames().toJSArray();
|
||||
|
||||
@@ -67,7 +73,7 @@ export default class BehaviorsEditor extends Component {
|
||||
.filter(behaviorType => behaviorType === type).length;
|
||||
};
|
||||
|
||||
addBehavior = (type, defaultName) => {
|
||||
addBehavior = (type: string, defaultName: string) => {
|
||||
const { object, project } = this.props;
|
||||
|
||||
this.setState(
|
||||
@@ -90,11 +96,15 @@ export default class BehaviorsEditor extends Component {
|
||||
|
||||
this.forceUpdate();
|
||||
if (this.props.onSizeUpdated) this.props.onSizeUpdated();
|
||||
this.props.onUpdateBehaviorsSharedData();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
_onChangeBehaviorName = (behaviorContent, newName) => {
|
||||
_onChangeBehaviorName = (
|
||||
behaviorContent: gdBehaviorContent,
|
||||
newName: string
|
||||
) => {
|
||||
// TODO: This is disabled for now as there is no proper refactoring
|
||||
// of events after a behavior renaming. Once refactoring is available,
|
||||
// the text field can be enabled again and refactoring calls added here
|
||||
@@ -108,7 +118,7 @@ export default class BehaviorsEditor extends Component {
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
_onRemoveBehavior = behaviorName => {
|
||||
_onRemoveBehavior = (behaviorName: string) => {
|
||||
const { object } = this.props;
|
||||
const answer = Window.showConfirmDialog(
|
||||
"Are you sure you want to remove this behavior? This can't be undone."
|
||||
@@ -130,9 +140,8 @@ export default class BehaviorsEditor extends Component {
|
||||
{allBehaviorNames
|
||||
.map((behaviorName, index) => {
|
||||
const behaviorContent = object.getBehavior(behaviorName);
|
||||
const behavior = gd.JsPlatform.get().getBehavior(
|
||||
behaviorContent.getTypeName()
|
||||
);
|
||||
const behaviorTypeName = behaviorContent.getTypeName();
|
||||
const behavior = gd.JsPlatform.get().getBehavior(behaviorTypeName);
|
||||
if (isNullPtr(gd, behavior)) {
|
||||
return (
|
||||
<div key={index}>
|
||||
@@ -161,8 +170,9 @@ export default class BehaviorsEditor extends Component {
|
||||
}
|
||||
|
||||
const BehaviorComponent = BehaviorsEditorService.getEditor(
|
||||
behaviorContent.getTypeName()
|
||||
behaviorTypeName
|
||||
);
|
||||
const tutorialHints = getBehaviorTutorialHints(behaviorTypeName);
|
||||
|
||||
return (
|
||||
<div key={index}>
|
||||
@@ -189,6 +199,18 @@ export default class BehaviorsEditor extends Component {
|
||||
</IconButton>
|
||||
<HelpIcon helpPagePath={getBehaviorHelpPagePath(behavior)} />
|
||||
</MiniToolbar>
|
||||
{tutorialHints.length ? (
|
||||
<Line>
|
||||
<ColumnStackLayout expand>
|
||||
{tutorialHints.map(tutorialHint => (
|
||||
<DismissableTutorialMessage
|
||||
key={tutorialHint.identifier}
|
||||
tutorialHint={tutorialHint}
|
||||
/>
|
||||
))}
|
||||
</ColumnStackLayout>
|
||||
</Line>
|
||||
) : null}
|
||||
<Line>
|
||||
<BehaviorComponent
|
||||
behavior={behavior}
|
||||
|
@@ -38,7 +38,7 @@ export const create = (authentification: Authentification) => {
|
||||
<Providers
|
||||
authentification={authentification}
|
||||
disableCheckForUpdates={!!appArguments['disable-update-check']}
|
||||
eventsFunctionCodeWriter={makeBrowserS3EventsFunctionCodeWriter()}
|
||||
makeEventsFunctionCodeWriter={makeBrowserS3EventsFunctionCodeWriter}
|
||||
eventsFunctionsExtensionWriter={null}
|
||||
eventsFunctionsExtensionOpener={null}
|
||||
>
|
||||
|