mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
69 Commits
v5.0.0-bet
...
v5.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e8ce83b162 | ||
![]() |
9b91f06011 | ||
![]() |
17247cbbf1 | ||
![]() |
10b81dd232 | ||
![]() |
3f3a5dbd3b | ||
![]() |
6ff8ee749d | ||
![]() |
db5f146818 | ||
![]() |
b7467afd1b | ||
![]() |
ee993f0cdb | ||
![]() |
bc6d3ce16f | ||
![]() |
1fae899497 | ||
![]() |
8319f60c95 | ||
![]() |
f6fe1a3205 | ||
![]() |
01e2b53a3f | ||
![]() |
da9eb3cea7 | ||
![]() |
c044b32c04 | ||
![]() |
4936b014ae | ||
![]() |
c1cd0d8780 | ||
![]() |
e79a328748 | ||
![]() |
2083ee1029 | ||
![]() |
3e982cdd87 | ||
![]() |
883527b289 | ||
![]() |
a4d84efdd5 | ||
![]() |
e46c8493bc | ||
![]() |
4c443b09cf | ||
![]() |
98bc2236f4 | ||
![]() |
5199299639 | ||
![]() |
3268d1db25 | ||
![]() |
741770924b | ||
![]() |
49aa9469bb | ||
![]() |
7cbe34436c | ||
![]() |
122f7ecf3e | ||
![]() |
d79bdd9554 | ||
![]() |
3e331cb2e8 | ||
![]() |
361fb6aeab | ||
![]() |
b4a76895ee | ||
![]() |
b2251e1a12 | ||
![]() |
1c1860370a | ||
![]() |
1a190b2a44 | ||
![]() |
b1e0f72416 | ||
![]() |
2c4ae7573e | ||
![]() |
04ff1f2726 | ||
![]() |
eaa5200f95 | ||
![]() |
53c749b79e | ||
![]() |
eb96ee8497 | ||
![]() |
458444ee7b | ||
![]() |
c8eb13f18f | ||
![]() |
a224b93edc | ||
![]() |
f077ca6723 | ||
![]() |
6bbfa1d4a1 | ||
![]() |
3c3dc6ef6e | ||
![]() |
5c101dbcda | ||
![]() |
701b78361a | ||
![]() |
07f26027f6 | ||
![]() |
09cf13d6e2 | ||
![]() |
42f91565fa | ||
![]() |
ac6c146808 | ||
![]() |
59ad23f8ac | ||
![]() |
769c6fe3d5 | ||
![]() |
07b92911ab | ||
![]() |
eb57bcfc87 | ||
![]() |
7addeba73a | ||
![]() |
263902b45a | ||
![]() |
d283f759fe | ||
![]() |
fabd028a63 | ||
![]() |
2b18272c41 | ||
![]() |
a1fb39da3d | ||
![]() |
09602fdf9e | ||
![]() |
d574ef17ba |
66
.vscode/c_cpp_properties.json
vendored
66
.vscode/c_cpp_properties.json
vendored
@@ -10,29 +10,31 @@
|
||||
"${workspaceRoot}/Extensions",
|
||||
"${workspaceRoot}/Core",
|
||||
"${workspaceRoot}/ExtLibs/SFML/include",
|
||||
"/usr/local/lib/wx/include/osx_cocoa-unicode-3.0",
|
||||
"/usr/local/include/wx-3.0",
|
||||
"/usr/include/machine",
|
||||
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
|
||||
"/usr/local/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.0.0/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
|
||||
"/usr/include"
|
||||
"/usr/include",
|
||||
"/usr/local/lib/wx/include/osx_cocoa-unicode-3.0",
|
||||
"/usr/local/include/wx-3.0",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"defines": [
|
||||
"GD_IDE_ONLY",
|
||||
"__WXMAC__",
|
||||
"__WXOSX__",
|
||||
"__WXOSX_COCOA__",
|
||||
"GD_CORE_API=\" \"",
|
||||
"GD_CORE_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_EXTENSION_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"WXUSINGDLL"
|
||||
],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
|
||||
"/usr/local/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.0.0/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
|
||||
"/usr/include",
|
||||
"${workspaceRoot}"
|
||||
@@ -40,7 +42,6 @@
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"macFrameworkPath": [
|
||||
"/System/Library/Frameworks",
|
||||
"/Library/Frameworks"
|
||||
@@ -50,10 +51,27 @@
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceRoot}",
|
||||
"${workspaceRoot}/IDE",
|
||||
"${workspaceRoot}/GDCpp",
|
||||
"${workspaceRoot}/GDJS",
|
||||
"${workspaceRoot}/Extensions",
|
||||
"${workspaceRoot}/Core",
|
||||
"${workspaceRoot}/ExtLibs/SFML/include",
|
||||
"/usr/include",
|
||||
"/usr/local/include"
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"defines": [],
|
||||
"defines": [
|
||||
"GD_IDE_ONLY",
|
||||
"__WXMAC__",
|
||||
"__WXOSX__",
|
||||
"__WXOSX_COCOA__",
|
||||
"GD_CORE_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_EXTENSION_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"WXUSINGDLL"
|
||||
],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/usr/include",
|
||||
@@ -62,19 +80,34 @@
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"intelliSenseMode": "clang-x64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"${workspaceRoot}",
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include"
|
||||
"${workspaceRoot}/IDE",
|
||||
"${workspaceRoot}/GDCpp",
|
||||
"${workspaceRoot}/GDJS",
|
||||
"${workspaceRoot}/Extensions",
|
||||
"${workspaceRoot}/Core",
|
||||
"${workspaceRoot}/ExtLibs/SFML/include",
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE"
|
||||
"UNICODE",
|
||||
"GD_IDE_ONLY",
|
||||
"__WXMAC__",
|
||||
"__WXOSX__",
|
||||
"__WXOSX_COCOA__",
|
||||
"GD_CORE_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_EXTENSION_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"WXUSINGDLL"
|
||||
],
|
||||
"intelliSenseMode": "msvc-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*",
|
||||
@@ -82,8 +115,7 @@
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"intelliSenseMode": "msvc-x64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 3
|
||||
|
File diff suppressed because it is too large
Load Diff
BIN
Binaries/Output/Release_Windows/res/conditions/raycast.png
Normal file
BIN
Binaries/Output/Release_Windows/res/conditions/raycast.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 328 B |
BIN
Binaries/Output/Release_Windows/res/conditions/raycast24.png
Normal file
BIN
Binaries/Output/Release_Windows/res/conditions/raycast24.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 378 B |
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
Binaries/Output/Release_Windows/res/ribbon_default/networkpreview64.png
Executable file
BIN
Binaries/Output/Release_Windows/res/ribbon_default/networkpreview64.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
@@ -2,7 +2,9 @@
|
||||
Version=1.0
|
||||
Name=GDevelop
|
||||
GenericName=Game creator IDE
|
||||
GenericName[de]=Entwicklungsumgebung für Spiele
|
||||
Comment=HTML5 and native game development software
|
||||
Comment[de]=Entwicklungsumgebung für native und HTML5-Spiele
|
||||
Exec=sh -c "gdevelop %F"
|
||||
MimeType=application/x-gdevelop-project;
|
||||
Icon=GDevelop
|
||||
|
@@ -337,7 +337,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAudioExtension(gd::Plat
|
||||
extension.AddCondition("GlobalVolume",
|
||||
_("Global volume"),
|
||||
_("Test the global sound level. The volume is between 0 and 100."),
|
||||
_("The global game volume is _PARAM2_ to _PARAM1_"),
|
||||
_("The global game volume is _PARAM1__PARAM2_"),
|
||||
_("Audio"),
|
||||
"res/conditions/volume24.png",
|
||||
"res/conditions/volume.png")
|
||||
|
@@ -860,6 +860,23 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(gd:
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension.AddCondition("Raycast",
|
||||
_("Raycast"),
|
||||
_("Sends a ray from the given source position and angle, intersecting the closest object.\nThe instersected object will become the only one taken into account.\nIf the condition is inverted, the object to be intersected will be the farthest one within the ray radius."),
|
||||
_("Raycast _PARAM0_ from _PARAM1_;_PARAM2_, and save the result in _PARAM5_, _PARAM6_"),
|
||||
_("Collision"),
|
||||
"res/conditions/raycast24.png",
|
||||
"res/conditions/raycast.png")
|
||||
.AddParameter("objectList", _("Objects to test against the ray"))
|
||||
.AddParameter("expression", _("Ray source X position"))
|
||||
.AddParameter("expression", _("Ray source Y position"))
|
||||
.AddParameter("expression", _("Ray angle (in degrees)"))
|
||||
.AddParameter("expression", _("Ray maximum distance (in pixels)"))
|
||||
.AddParameter("scenevar", _("Variable where to store the X position of the intersection"))
|
||||
.AddParameter("scenevar", _("Variable where to store the Y position of the intersection"))
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension.AddExpression("Count", _("Number of objects"), _("Count the number of the specified objects currently picked"), _("Objects"), "res/conditions/nbObjet.png")
|
||||
.AddParameter("objectList", _("Object"));
|
||||
#endif
|
||||
|
@@ -52,6 +52,7 @@
|
||||
#include "GDCore/Tools/Log.h"
|
||||
#include "GDCore/IDE/Dialogs/DndResourcesEditor.h"
|
||||
#include "GDCore/IDE/wxTools/TreeItemStringData.h"
|
||||
#include "GDCore/IDE/Dialogs/PropertyDescriptor.h"
|
||||
|
||||
#ifdef __WXGTK__
|
||||
#include <gtk/gtk.h>
|
||||
@@ -635,7 +636,7 @@ void ResourcesEditor::OnresourcesTreeSelectionChanged( wxTreeEvent& event )
|
||||
|
||||
void ResourcesEditor::UpdatePropertyGrid()
|
||||
{
|
||||
std::vector<gd::String> commonProperties; ///< The name of the properties to be displayed
|
||||
std::map<gd::String, gd::PropertyDescriptor> commonProperties; ///< The name of the properties to be displayed
|
||||
bool aFolderIsSelected = false;
|
||||
|
||||
//First construct the list of common properties
|
||||
@@ -646,18 +647,29 @@ void ResourcesEditor::UpdatePropertyGrid()
|
||||
gd::TreeItemStringData * data = dynamic_cast<gd::TreeItemStringData*>(resourcesTree->GetItemData(selection[i]));
|
||||
if ( data && data->GetString() == "Image")
|
||||
{
|
||||
std::vector<gd::String> properties = project.GetResourcesManager().GetResource(data->GetSecondString()).GetAllProperties(project);
|
||||
const std::map<gd::String, gd::PropertyDescriptor> & properties =
|
||||
project.GetResourcesManager().GetResource(data->GetSecondString()).GetProperties(project);
|
||||
if ( i == 0 )
|
||||
commonProperties = properties;
|
||||
else
|
||||
{
|
||||
//Keep only properties that are common to all the selected resources
|
||||
for (std::size_t j = 0;j<commonProperties.size();)
|
||||
// TODO: This could be factored with helpers like ObjectsPropgridHelper
|
||||
|
||||
//Merge custom properties
|
||||
for(std::map<gd::String, gd::PropertyDescriptor>::const_iterator it = properties.begin();
|
||||
it != properties.end();++it)
|
||||
{
|
||||
if ( find(properties.begin(), properties.end(), commonProperties[j]) == properties.end() )
|
||||
commonProperties.erase(commonProperties.begin()+j);
|
||||
else
|
||||
++j;
|
||||
if ( commonProperties.find(it->first) == commonProperties.end() ) continue;
|
||||
if ( commonProperties[it->first].GetValue() != it->second.GetValue() )
|
||||
commonProperties[it->first].SetValue(_("(Multiples values)"));
|
||||
}
|
||||
//Also erase properties which are not in common.
|
||||
for(std::map<gd::String, gd::PropertyDescriptor>::iterator it = commonProperties.begin();
|
||||
it != commonProperties.end();)
|
||||
{
|
||||
if ( properties.find(it->first) == properties.end() )
|
||||
commonProperties.erase(it++);
|
||||
else ++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -721,32 +733,11 @@ void ResourcesEditor::UpdatePropertyGrid()
|
||||
|
||||
//Other properties
|
||||
if ( !commonProperties.empty() ) propertyGrid->Append( new wxPropertyCategory(_("Other properties")) );
|
||||
for (std::size_t j = 0;j<commonProperties.size();++j)
|
||||
for(auto & propertyIterator: commonProperties)
|
||||
{
|
||||
wxPGProperty * property = propertyGrid->Append( new wxStringProperty("", commonProperties[j], "") );
|
||||
wxString commonValue;
|
||||
|
||||
for (std::size_t i = 0;i<selection.size();++i)
|
||||
{
|
||||
gd::TreeItemStringData * data = dynamic_cast<gd::TreeItemStringData*>(resourcesTree->GetItemData(selection[i]));
|
||||
if ( data && data->GetString() == "Image")
|
||||
{
|
||||
//It is assumed that the description and the user friendly name
|
||||
//are the same for all the properties with the same name.
|
||||
gd::String propertyUserFriendlyName;
|
||||
gd::String propertyDescription;
|
||||
project.GetResourcesManager().GetResource(data->GetSecondString()).GetPropertyInformation(project, commonProperties[j], propertyUserFriendlyName, propertyDescription);
|
||||
propertyGrid->SetPropertyLabel(property, propertyUserFriendlyName);
|
||||
propertyGrid->SetPropertyHelpString(property, propertyDescription);
|
||||
|
||||
//Values can be different though
|
||||
gd::String propertyValue = project.GetResourcesManager().GetResource(data->GetSecondString()).GetProperty(project, commonProperties[j]);
|
||||
if ( i == 0 ) commonValue = propertyValue;
|
||||
else if ( commonValue != propertyValue ) commonValue = _("(Multiple values)");
|
||||
}
|
||||
}
|
||||
|
||||
propertyGrid->SetPropertyValue(property, commonValue);
|
||||
// TODO: For now, all other properties are assumed to be booleans.
|
||||
auto & propertyName = propertyIterator.first;
|
||||
propertyGrid->Append(new wxBoolProperty(propertyName, propertyName, propertyIterator.second.GetValue() == "true"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -782,7 +773,7 @@ void ResourcesEditor::OnPropertyChanged(wxPropertyGridEvent& event)
|
||||
RenameInTree(resourcesTree->GetRootItem(), renamedItemOldName, propertyNewValue, "Image");
|
||||
}
|
||||
else
|
||||
project.GetResourcesManager().GetResource(data->GetSecondString()).ChangeProperty(project, propertyName, propertyNewValue);
|
||||
project.GetResourcesManager().GetResource(data->GetSecondString()).UpdateProperty(propertyName, propertyNewValue, project);
|
||||
|
||||
for ( std::size_t j = 0; j < project.GetUsedPlatforms().size();++j)
|
||||
project.GetUsedPlatforms()[j]->GetChangesNotifier().OnResourceModified(project, data->GetSecondString());
|
||||
|
@@ -45,7 +45,7 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(gd::Project & originalProject, A
|
||||
#endif
|
||||
|
||||
auto projectDirectory = fs.DirNameFrom(originalProject.GetProjectFile());
|
||||
std::cout << "Copying all ressources from " << projectDirectory << " to " << destinationDirectory;
|
||||
std::cout << "Copying all ressources from " << projectDirectory << " to " << destinationDirectory << "..." << std::endl;
|
||||
|
||||
//Get the resources to be copied
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(fs);
|
||||
|
21
Core/GDCore/Project/LoadingScreen.cpp
Normal file
21
Core/GDCore/Project/LoadingScreen.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2018 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "LoadingScreen.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
void LoadingScreen::SerializeTo(SerializerElement& element) const
|
||||
{
|
||||
element.SetAttribute("showGDevelopSplash", showGDevelopSplash);
|
||||
}
|
||||
|
||||
void LoadingScreen::UnserializeFrom(const SerializerElement& element)
|
||||
{
|
||||
showGDevelopSplash = element.GetBoolAttribute("showGDevelopSplash", true);
|
||||
}
|
||||
}
|
57
Core/GDCore/Project/LoadingScreen.h
Normal file
57
Core/GDCore/Project/LoadingScreen.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2018 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef GDCORE_LOADINGSCREEN_H
|
||||
#define GDCORE_LOADINGSCREEN_H
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Describe the content and set up of the loading screen
|
||||
*
|
||||
* \see gd::LoadingScreen
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API LoadingScreen {
|
||||
public:
|
||||
LoadingScreen(){};
|
||||
virtual ~LoadingScreen(){};
|
||||
|
||||
/**
|
||||
* \brief Set if the GDevelop splash should be shown while loading assets.
|
||||
*/
|
||||
void ShowGDevelopSplash(bool show) { showGDevelopSplash = show; };
|
||||
|
||||
/**
|
||||
* \brief Return true if the GDevelop splash should be shown while loading assets.
|
||||
*/
|
||||
bool IsGDevelopSplashShown() const { return showGDevelopSplash; };
|
||||
|
||||
/** \name Saving and loading
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize objects groups container.
|
||||
*/
|
||||
void SerializeTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize the objects groups container.
|
||||
*/
|
||||
void UnserializeFrom(const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
private:
|
||||
bool showGDevelopSplash;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GDCORE_LOADINGSCREEN_H
|
58
Core/GDCore/Project/PlatformSpecificAssets.cpp
Normal file
58
Core/GDCore/Project/PlatformSpecificAssets.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2018 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "PlatformSpecificAssets.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
gd::String PlatformSpecificAssets::badStr;
|
||||
|
||||
bool PlatformSpecificAssets::Has(const gd::String& platform, const gd::String& name) const
|
||||
{
|
||||
return assets.find(platform + "-" + name) != assets.end();
|
||||
}
|
||||
|
||||
const gd::String& PlatformSpecificAssets::Get(const gd::String& platform, const gd::String& name) const
|
||||
{
|
||||
const auto & it = assets.find(platform + "-" + name);
|
||||
return it != assets.end() ? it->second : badStr;
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::Remove(const gd::String& platform, const gd::String& name)
|
||||
{
|
||||
assets.erase(platform + "-" + name);
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::Set(const gd::String& platform, const gd::String& name, const gd::String& resourceName)
|
||||
{
|
||||
assets[platform + "-" + name] = resourceName;
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::SerializeTo(SerializerElement& element) const
|
||||
{
|
||||
for (auto& it : assets) {
|
||||
element.AddChild(it.first).SetValue(it.second);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::UnserializeFrom(const SerializerElement& element)
|
||||
{
|
||||
assets.clear();
|
||||
|
||||
for (auto& child : element.GetAllChildren()) {
|
||||
assets[child.first] = child.second->GetValue().GetString();
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::ExposeResources(gd::ArbitraryResourceWorker & worker)
|
||||
{
|
||||
for (auto& it : assets) {
|
||||
worker.ExposeImage(it.second);
|
||||
}
|
||||
}
|
||||
}
|
76
Core/GDCore/Project/PlatformSpecificAssets.h
Normal file
76
Core/GDCore/Project/PlatformSpecificAssets.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2018 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef GDCORE_PLATFORMASSETS_H
|
||||
#define GDCORE_PLATFORMASSETS_H
|
||||
#include "GDCore/String.h"
|
||||
#include <map>
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
}
|
||||
namespace gd {
|
||||
class ArbitraryResourceWorker;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Store the icons, splashscreens or reference to any other asset
|
||||
* that can be needed when exporting the game.
|
||||
*
|
||||
* \see gd::Project
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API PlatformSpecificAssets {
|
||||
public:
|
||||
PlatformSpecificAssets(){};
|
||||
virtual ~PlatformSpecificAssets(){};
|
||||
|
||||
/**
|
||||
* \brief Return true if the specified asset exists.
|
||||
*/
|
||||
bool Has(const gd::String& platform, const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Get the specified asset resource name.
|
||||
*/
|
||||
const gd::String& Get(const gd::String& platform, const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Remove the specified asset.
|
||||
*/
|
||||
void Remove(const gd::String& platform, const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Remove the specified asset.
|
||||
*/
|
||||
void Set(const gd::String& platform, const gd::String& name, const gd::String& resourceName);
|
||||
|
||||
void ExposeResources(gd::ArbitraryResourceWorker & worker);
|
||||
|
||||
/** \name Saving and loading
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize objects groups container.
|
||||
*/
|
||||
void SerializeTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize the objects groups container.
|
||||
*/
|
||||
void UnserializeFrom(const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
private:
|
||||
std::map<gd::String, gd::String> assets;
|
||||
|
||||
static gd::String badStr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GDCORE_PLATFORMASSETS_H
|
@@ -61,6 +61,7 @@ Project::Project() :
|
||||
#if defined(GD_IDE_ONLY)
|
||||
name(_("Project")),
|
||||
packageName("com.example.gamename"),
|
||||
orientation("landscape"),
|
||||
folderProject(false),
|
||||
#endif
|
||||
windowWidth(800),
|
||||
@@ -531,9 +532,12 @@ void Project::UnserializeFrom(const SerializerElement & element)
|
||||
#if defined(GD_IDE_ONLY)
|
||||
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
|
||||
SetPackageName(propElement.GetStringAttribute("packageName"));
|
||||
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
|
||||
SetFolderProject(propElement.GetBoolAttribute("folderProject"));
|
||||
SetProjectFile(propElement.GetStringAttribute("projectFile"));
|
||||
SetLastCompilationDirectory(propElement.GetChild("latestCompilationDirectory", 0, "LatestCompilationDirectory").GetValue().GetString());
|
||||
platformSpecificAssets.UnserializeFrom(propElement.GetChild("platformSpecificAssets"));
|
||||
loadingScreen.UnserializeFrom(propElement.GetChild("loadingScreen"));
|
||||
winExecutableFilename = propElement.GetStringAttribute("winExecutableFilename");
|
||||
winExecutableIconFile = propElement.GetStringAttribute("winExecutableIconFile");
|
||||
linuxExecutableFilename = propElement.GetStringAttribute("linuxExecutableFilename");
|
||||
@@ -743,6 +747,9 @@ void Project::SerializeTo(SerializerElement & element) const
|
||||
propElement.SetAttribute("projectFile", gameFile);
|
||||
propElement.SetAttribute("folderProject", folderProject);
|
||||
propElement.SetAttribute("packageName", packageName);
|
||||
propElement.SetAttribute("orientation", orientation);
|
||||
platformSpecificAssets.SerializeTo(propElement.AddChild("platformSpecificAssets"));
|
||||
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
|
||||
propElement.SetAttribute("winExecutableFilename", winExecutableFilename);
|
||||
propElement.SetAttribute("winExecutableIconFile", winExecutableIconFile);
|
||||
propElement.SetAttribute("linuxExecutableFilename", linuxExecutableFilename);
|
||||
@@ -815,6 +822,7 @@ void Project::ExposeResources(gd::ArbitraryResourceWorker & worker)
|
||||
{
|
||||
//Add project resources
|
||||
worker.ExposeResources(&GetResourcesManager());
|
||||
platformSpecificAssets.ExposeResources(worker);
|
||||
#if !defined(GD_NO_WX_GUI)
|
||||
gd::SafeYield::Do();
|
||||
#endif
|
||||
@@ -1019,8 +1027,11 @@ void Project::Init(const gd::Project & game)
|
||||
#if defined(GD_IDE_ONLY)
|
||||
author = game.author;
|
||||
packageName = game.packageName;
|
||||
orientation = game.orientation;
|
||||
folderProject = game.folderProject;
|
||||
latestCompilationDirectory = game.latestCompilationDirectory;
|
||||
platformSpecificAssets = game.platformSpecificAssets;
|
||||
loadingScreen = game.loadingScreen;
|
||||
objectGroups = game.objectGroups;
|
||||
|
||||
GDMajorVersion = game.GDMajorVersion;
|
||||
|
@@ -16,6 +16,8 @@ class TiXmlElement;
|
||||
#include "GDCore/Project/ChangesNotifier.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/Project/ResourcesManager.h"
|
||||
#include "GDCore/Project/PlatformSpecificAssets.h"
|
||||
#include "GDCore/Project/LoadingScreen.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
namespace gd { class Platform; }
|
||||
namespace gd { class Layout; }
|
||||
@@ -85,6 +87,18 @@ public:
|
||||
*/
|
||||
const gd::String & GetPackageName() const { return packageName; }
|
||||
|
||||
/**
|
||||
* \brief Change the project orientation (in particular when exported with Cordova).
|
||||
* This has no effect on desktop and web browsers.
|
||||
* \param orientation The orientation to use ("default", "landscape", "portrait").
|
||||
*/
|
||||
void SetOrientation(const gd::String & orientation_) { orientation = orientation_; };
|
||||
|
||||
/**
|
||||
* \brief Get project orientation ("default", "landscape", "portrait").
|
||||
*/
|
||||
const gd::String & GetOrientation() const { return orientation; }
|
||||
|
||||
/**
|
||||
* Called when project file has changed.
|
||||
*/
|
||||
@@ -118,6 +132,26 @@ public:
|
||||
* \see gd::Project::SetLastCompilationDirectory
|
||||
*/
|
||||
const gd::String & GetLastCompilationDirectory() const {return latestCompilationDirectory;}
|
||||
|
||||
/**
|
||||
* \brief Return a reference to platform assets of the project (icons, splashscreen...).
|
||||
*/
|
||||
gd::PlatformSpecificAssets & GetPlatformSpecificAssets() { return platformSpecificAssets; }
|
||||
|
||||
/**
|
||||
* \brief Return a reference to platform assets of the project (icons, splashscreen...).
|
||||
*/
|
||||
const gd::PlatformSpecificAssets & GetPlatformSpecificAssets() const { return platformSpecificAssets; }
|
||||
|
||||
/**
|
||||
* \brief Return a reference to loading screen setup for the project
|
||||
*/
|
||||
gd::LoadingScreen & GetLoadingScreen() { return loadingScreen; }
|
||||
|
||||
/**
|
||||
* \brief Return a reference to loading screen setup for the project
|
||||
*/
|
||||
const gd::LoadingScreen & GetLoadingScreen() const { return loadingScreen; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -737,10 +771,13 @@ private:
|
||||
gd::ObjectGroupsContainer objectGroups; ///< Global objects groups
|
||||
gd::String author; ///< Game author name
|
||||
gd::String packageName; ///< Game package name
|
||||
gd::String orientation; ///< Lock game orientation (on mobile devices). "default", "landscape" or "portrait".
|
||||
bool folderProject; ///< True if folder project, false if single file project.
|
||||
gd::String gameFile; ///< File of the game
|
||||
gd::String latestCompilationDirectory; ///< File of the game
|
||||
gd::Platform* currentPlatform; ///< The platform being used to edit the project.
|
||||
gd::PlatformSpecificAssets platformSpecificAssets;
|
||||
gd::LoadingScreen loadingScreen;
|
||||
std::vector < std::unique_ptr<gd::ExternalEvents> > externalEvents; ///< List of all externals events
|
||||
mutable unsigned int GDMajorVersion; ///< The GD major version used the last time the project was saved.
|
||||
mutable unsigned int GDMinorVersion; ///< The GD minor version used the last time the project was saved.
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
|
||||
#include "GDCore/IDE/wxTools/CommonBitmapProvider.h"
|
||||
#include <wx/filedlg.h>
|
||||
@@ -20,6 +21,7 @@
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/IDE/Dialogs/PropertyDescriptor.h"
|
||||
|
||||
namespace gd
|
||||
{
|
||||
@@ -114,8 +116,30 @@ std::vector<gd::String> ResourcesManager::GetAllResourceNames()
|
||||
return allResources;
|
||||
}
|
||||
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor> Resource::GetProperties(gd::Project & project) const
|
||||
{
|
||||
std::map<gd::String, gd::PropertyDescriptor> nothing;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> ImageResource::GetProperties(gd::Project & project) const
|
||||
{
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[_("Smooth the image")].SetValue(smooth ? "true" : "false").SetType("Boolean");
|
||||
properties[_("Always loaded in memory")].SetValue(alwaysLoaded ? "true" : "false").SetType("Boolean");
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
bool ImageResource::UpdateProperty(const gd::String & name, const gd::String & value, gd::Project & project)
|
||||
{
|
||||
if (name == _("Smooth the image")) smooth = value == "1";
|
||||
else if (name == _("Always loaded in memory")) alwaysLoaded = value == "1";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourcesManager::AddResource(const gd::Resource & resource)
|
||||
{
|
||||
if ( HasResource(resource.GetName()) ) return false;
|
||||
@@ -155,61 +179,6 @@ std::vector<gd::String> ResourceFolder::GetAllResourceNames()
|
||||
return allResources;
|
||||
}
|
||||
|
||||
bool ImageResource::EditProperty(gd::Project & project, const gd::String & property)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImageResource::ChangeProperty(gd::Project & project, const gd::String & property, const gd::String & newValue)
|
||||
{
|
||||
if ( property == "smooth" )
|
||||
smooth = (newValue == _("Yes"));
|
||||
else if ( property == "alwaysLoaded" )
|
||||
alwaysLoaded = (newValue == _("Yes"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImageResource::GetPropertyInformation(gd::Project & project, const gd::String & property, gd::String & userFriendlyName, gd::String & description) const
|
||||
{
|
||||
if ( property == "smooth" )
|
||||
{
|
||||
userFriendlyName = _("Smooth the image");
|
||||
description = _("Set this to \"Yes\" to set a smooth filter on the image");
|
||||
}
|
||||
else if ( property == "alwaysLoaded" )
|
||||
{
|
||||
userFriendlyName = _("Always loaded in memory");
|
||||
description = _("Set this to \"Yes\" to let the image always loaded in memory.\nUseful when the image is used by actions.");
|
||||
}
|
||||
}
|
||||
|
||||
gd::String ImageResource::GetProperty(gd::Project & project, const gd::String & property)
|
||||
{
|
||||
if ( property == "smooth" )
|
||||
{
|
||||
return smooth ? _("Yes") : _("No");
|
||||
}
|
||||
else if ( property == "alwaysLoaded" )
|
||||
{
|
||||
return alwaysLoaded ? _("Yes") : _("No");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a vector containing the name of all the properties of the resource
|
||||
*/
|
||||
std::vector<gd::String> ImageResource::GetAllProperties(gd::Project & project) const
|
||||
{
|
||||
std::vector<gd::String> allProperties;
|
||||
allProperties.push_back("smooth");
|
||||
allProperties.push_back("alwaysLoaded");
|
||||
|
||||
return allProperties;
|
||||
}
|
||||
|
||||
#if !defined(GD_NO_WX_GUI)
|
||||
void ImageResource::RenderPreview(wxPaintDC & dc, wxPanel & previewPanel, gd::Project & project)
|
||||
{
|
||||
|
@@ -6,11 +6,12 @@
|
||||
#ifndef GDCORE_RESOURCESMANAGER_H
|
||||
#define GDCORE_RESOURCESMANAGER_H
|
||||
#include <memory>
|
||||
#include "GDCore/String.h"
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
namespace gd { class Project; }
|
||||
namespace gd { class ResourceFolder; }
|
||||
namespace gd { class SerializerElement; }
|
||||
namespace gd { class PropertyDescriptor; }
|
||||
class wxPaintDC;
|
||||
class wxPanel;
|
||||
|
||||
@@ -83,37 +84,6 @@ public:
|
||||
*/
|
||||
gd::String GetAbsoluteFile(const gd::Project & game) const;
|
||||
|
||||
/**
|
||||
* \brief Called when a property must be edited (i.e: it was double clicked in a property grid)
|
||||
*
|
||||
* \return true if the resource was changed
|
||||
*/
|
||||
virtual bool EditProperty(gd::Project & project, const gd::String & property) { return true; };
|
||||
|
||||
/**
|
||||
* \brief Called when a property must be changed (i.e: its value was changed in a property grid)
|
||||
*
|
||||
* \return true if the resource was changed
|
||||
*/
|
||||
virtual bool ChangeProperty(gd::Project & project, const gd::String & property, const gd::String & newValue) { return true; };
|
||||
|
||||
/**
|
||||
* \brief Must return a description of the main property provided by the resource (example : "Image file")
|
||||
*/
|
||||
virtual void GetPropertyInformation(gd::Project & project, const gd::String & property, gd::String & userFriendlyName, gd::String & description) const { return; };
|
||||
|
||||
/**
|
||||
* \brief Called when a property must be changed ( i.e: its value was changed in the property grid )
|
||||
*
|
||||
* \return the value of the property
|
||||
*/
|
||||
virtual gd::String GetProperty(gd::Project & project, const gd::String & property) { return ""; };
|
||||
|
||||
/**
|
||||
* \brief Return a description of the main property provided by the resource ( Example : "Image file" )
|
||||
*/
|
||||
virtual std::vector<gd::String> GetAllProperties(gd::Project & project) const { std::vector<gd::String> noProperties; return noProperties; };
|
||||
|
||||
#if !defined(GD_NO_WX_GUI)
|
||||
/**
|
||||
* \brief Called when the resource must be rendered in a preview panel.
|
||||
@@ -121,6 +91,36 @@ public:
|
||||
virtual void RenderPreview(wxPaintDC & dc, wxPanel & previewPanel, gd::Project & game) {};
|
||||
#endif
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/** \name Resources properties
|
||||
* Reading and updating resources properties
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Called when the IDE wants to know about the custom properties of the resource.
|
||||
*
|
||||
* Usage example:
|
||||
\code
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[ToString(_("Text"))].SetValue("Hello world!");
|
||||
|
||||
return properties;
|
||||
\endcode
|
||||
*
|
||||
* \return a std::map with properties names as key.
|
||||
* \see gd::PropertyDescriptor
|
||||
*/
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(gd::Project & project) const;
|
||||
|
||||
/**
|
||||
* \brief Called when the IDE wants to update a custom property of the resource
|
||||
*
|
||||
* \return false if the new value cannot be set
|
||||
*/
|
||||
virtual bool UpdateProperty(const gd::String & name, const gd::String & value, gd::Project & project) {return false;};
|
||||
///@}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Serialize the object
|
||||
*/
|
||||
@@ -150,69 +150,41 @@ class GD_CORE_API ImageResource : public Resource
|
||||
public:
|
||||
ImageResource() : Resource(), smooth(true), alwaysLoaded(false) { SetKind("image"); };
|
||||
virtual ~ImageResource() {};
|
||||
virtual ImageResource* Clone() const { return new ImageResource(*this);}
|
||||
virtual ImageResource* Clone() const override { return new ImageResource(*this);}
|
||||
|
||||
/**
|
||||
* Return the file used by the resource.
|
||||
*/
|
||||
virtual const gd::String & GetFile() const {return file;};
|
||||
virtual const gd::String & GetFile() const override {return file;};
|
||||
|
||||
/**
|
||||
* Change the file of the resource.
|
||||
*/
|
||||
virtual void SetFile(const gd::String & newFile);
|
||||
virtual void SetFile(const gd::String & newFile) override;
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual bool UseFile() { return true; }
|
||||
virtual bool UseFile() override { return true; }
|
||||
|
||||
#if !defined(GD_NO_WX_GUI)
|
||||
/**
|
||||
* Called when the resource must be rendered in a preview panel.
|
||||
*/
|
||||
virtual void RenderPreview(wxPaintDC & dc, wxPanel & previewPanel, gd::Project & game);
|
||||
virtual void RenderPreview(wxPaintDC & dc, wxPanel & previewPanel, gd::Project & game) override;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Called when a property must be edited ( i.e: it was double clicked )
|
||||
*
|
||||
* \return true if the resource was changed
|
||||
*/
|
||||
virtual bool EditProperty(gd::Project & project, const gd::String & property);
|
||||
|
||||
/**
|
||||
* Called when a property must be changed ( i.e: its value was changed in the property grid )
|
||||
*
|
||||
* \return true if the resource was changed
|
||||
*/
|
||||
virtual bool ChangeProperty(gd::Project & project, const gd::String & property, const gd::String & newValue);
|
||||
|
||||
/**
|
||||
* Called when a property must be changed ( i.e: its value was changed in the property grid )
|
||||
*
|
||||
* \return the value of the property
|
||||
*/
|
||||
virtual gd::String GetProperty(gd::Project & project, const gd::String & property);
|
||||
|
||||
/**
|
||||
* Return a description of the main property provided by the resource ( Example : "Image file" )
|
||||
*/
|
||||
virtual void GetPropertyInformation(gd::Project & project, const gd::String & property, gd::String & userFriendlyName, gd::String & description) const;
|
||||
|
||||
/**
|
||||
* Return a vector containing the name of all the properties of the resource
|
||||
*/
|
||||
virtual std::vector<gd::String> GetAllProperties(gd::Project & project) const;
|
||||
std::map<gd::String, gd::PropertyDescriptor> GetProperties(gd::Project & project) const override;
|
||||
bool UpdateProperty(const gd::String & name, const gd::String & value, gd::Project & project) override;
|
||||
|
||||
/**
|
||||
* \brief Serialize the object
|
||||
*/
|
||||
void SerializeTo(SerializerElement & element) const;
|
||||
void SerializeTo(SerializerElement & element) const override;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Unserialize the objectt.
|
||||
*/
|
||||
void UnserializeFrom(const SerializerElement & element);
|
||||
void UnserializeFrom(const SerializerElement & element) override;
|
||||
|
||||
/**
|
||||
* \brief Return true if the image should be smoothed.
|
||||
@@ -242,17 +214,17 @@ class GD_CORE_API AudioResource : public Resource
|
||||
public:
|
||||
AudioResource() : Resource() { SetKind("audio"); };
|
||||
virtual ~AudioResource() {};
|
||||
virtual AudioResource* Clone() const { return new AudioResource(*this);}
|
||||
virtual AudioResource* Clone() const override { return new AudioResource(*this);}
|
||||
|
||||
virtual const gd::String & GetFile() const {return file;};
|
||||
virtual void SetFile(const gd::String & newFile);
|
||||
virtual const gd::String & GetFile() const override {return file;};
|
||||
virtual void SetFile(const gd::String & newFile) override;
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual bool UseFile() { return true; }
|
||||
void SerializeTo(SerializerElement & element) const;
|
||||
virtual bool UseFile() override { return true; }
|
||||
void SerializeTo(SerializerElement & element) const override;
|
||||
#endif
|
||||
|
||||
void UnserializeFrom(const SerializerElement & element);
|
||||
void UnserializeFrom(const SerializerElement & element) override;
|
||||
|
||||
private:
|
||||
gd::String file;
|
||||
|
@@ -106,6 +106,7 @@ BaseObjectExtension::BaseObjectExtension()
|
||||
GetAllConditions()["NbObjet"].SetFunctionName("PickedObjectsCount").SetManipulatedType("number").SetIncludeFile("GDCpp/Extensions/Builtin/ObjectTools.h");
|
||||
GetAllConditions()["CollisionNP"].SetFunctionName("HitBoxesCollision").SetIncludeFile("GDCpp/Extensions/Builtin/ObjectTools.h");
|
||||
GetAllConditions()["EstTourne"].SetFunctionName("ObjectsTurnedToward").SetIncludeFile("GDCpp/Extensions/Builtin/ObjectTools.h");
|
||||
GetAllConditions()["Raycast"].SetFunctionName("RaycastObject").SetIncludeFile("GDCpp/Extensions/Builtin/RuntimeSceneTools.h");
|
||||
|
||||
GetAllExpressions()["Count"].SetFunctionName("PickedObjectsCount").SetIncludeFile("GDCpp/Extensions/Builtin/ObjectTools.h");
|
||||
#endif
|
||||
|
@@ -22,6 +22,9 @@
|
||||
#include "GDCpp/Runtime/CommonTools.h"
|
||||
#include "GDCpp/Runtime/Project/Variable.h"
|
||||
#include "GDCpp/Extensions/CppPlatform.h"
|
||||
#include "GDCpp/Runtime/PolygonCollision.h"
|
||||
#include "GDCpp/Runtime/Polygon2d.h"
|
||||
#include <iostream>
|
||||
|
||||
gd::String GD_API GetSceneName(RuntimeScene & scene)
|
||||
{
|
||||
@@ -208,6 +211,48 @@ bool GD_API PickNearestObject(std::map <gd::String, std::vector<RuntimeObject*>
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GD_API RaycastObject(std::map <gd::String, std::vector<RuntimeObject*> *> pickedObjectLists, float x, float y, float angle, float dist, gd::Variable & varX, gd::Variable & varY, bool inverted)
|
||||
{
|
||||
RuntimeObject * matchObject = NULL;
|
||||
float testSqDist = inverted ? 0 : dist*dist;
|
||||
float resultX = 0.0f;
|
||||
float resultY = 0.0f;
|
||||
for (auto it = pickedObjectLists.begin(); it != pickedObjectLists.end(); ++it)
|
||||
{
|
||||
if ( it->second == NULL ) continue;
|
||||
auto list = *it->second;
|
||||
|
||||
for (std::size_t i = 0; i < list.size(); ++i)
|
||||
{
|
||||
|
||||
RaycastResult result = list[i]->RaycastTest(x, y, angle, dist, !inverted);
|
||||
|
||||
if( result.collision ) {
|
||||
if ( !inverted && (result.closeSqDist <= testSqDist) ) {
|
||||
testSqDist = result.closeSqDist;
|
||||
matchObject = list[i];
|
||||
resultX = result.closePoint.x;
|
||||
resultY = result.closePoint.y;
|
||||
}
|
||||
else if ( inverted && (result.farSqDist >= testSqDist) ) {
|
||||
testSqDist = result.farSqDist;
|
||||
matchObject = list[i];
|
||||
resultX = result.farPoint.x;
|
||||
resultY = result.farPoint.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !matchObject )
|
||||
return false;
|
||||
|
||||
PickOnly(pickedObjectLists, matchObject);
|
||||
varX.SetValue(resultX);
|
||||
varY.SetValue(resultY);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GD_API SceneVariableExists(RuntimeScene & scene, const gd::String & variable)
|
||||
{
|
||||
return scene.GetVariables().Has(variable);
|
||||
|
@@ -94,6 +94,11 @@ bool GD_API PickRandomObject(RuntimeScene & scene, std::map <gd::String, std::ve
|
||||
*/
|
||||
bool GD_API PickNearestObject(std::map <gd::String, std::vector<RuntimeObject*> *> pickedObjectLists, double x, double y, bool inverted);
|
||||
|
||||
/**
|
||||
* Only used internally by GD events generated code.
|
||||
*/
|
||||
bool GD_API RaycastObject(std::map <gd::String, std::vector<RuntimeObject*> *> pickedObjectLists, float x, float y, float angle, float dist, gd::Variable & varX, gd::Variable & varY, bool inverted);
|
||||
|
||||
/**
|
||||
* Only used internally by GD events generated code.
|
||||
*/
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include "GDCpp/Runtime/Polygon2d.h"
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -30,6 +31,13 @@ float dotProduct(const sf::Vector2f a, const sf::Vector2f b)
|
||||
return dp;
|
||||
}
|
||||
|
||||
float crossProduct(const sf::Vector2f a, const sf::Vector2f b)
|
||||
{
|
||||
float cp = a.x*b.y - a.y*b.x;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
void project(const sf::Vector2f axis, const Polygon2d & p, float& min, float& max)
|
||||
{
|
||||
float dp = dotProduct(axis, p.vertices[0]);
|
||||
@@ -129,6 +137,96 @@ CollisionResult GD_API PolygonCollisionTest(Polygon2d & p1, Polygon2d & p2)
|
||||
return result;
|
||||
}
|
||||
|
||||
RaycastResult GD_API PolygonRaycastTest(Polygon2d & poly, float startX, float startY, float endX, float endY)
|
||||
{
|
||||
RaycastResult result;
|
||||
result.collision = false;
|
||||
|
||||
if ( poly.vertices.size() < 2 )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
poly.ComputeEdges();
|
||||
sf::Vector2f p, q, r, s;
|
||||
float minSqDist = FLT_MAX;
|
||||
|
||||
// Ray segment: p + t*r, with p = start and r = end - start
|
||||
p.x = startX;
|
||||
p.y = startY;
|
||||
r.x = endX - startX;
|
||||
r.y = endY - startY;
|
||||
|
||||
for( int i=0; i<poly.edges.size(); i++ )
|
||||
{
|
||||
// Edge segment: q + u*s
|
||||
q = poly.vertices[i];
|
||||
s = poly.edges[i];
|
||||
sf::Vector2f deltaQP = q - p;
|
||||
float crossRS = crossProduct(r, s);
|
||||
float t = crossProduct(deltaQP, s) / crossRS;
|
||||
float u = crossProduct(deltaQP, r) / crossRS;
|
||||
|
||||
// Collinear
|
||||
if ( abs(crossRS) <= 0.0001 && abs(crossProduct(deltaQP, r)) <= 0.0001 )
|
||||
{
|
||||
// Project the ray and the edge to work on floats, keeping linearity through t
|
||||
sf::Vector2f axis(r.x, r.y);
|
||||
normalise(axis);
|
||||
float rayA = 0.0f;
|
||||
float rayB = dotProduct(axis, r);
|
||||
float edgeA = dotProduct(axis, deltaQP);
|
||||
float edgeB = dotProduct(axis, deltaQP + s);
|
||||
// Get overlapping range
|
||||
float minOverlap = std::max(std::min(rayA, rayB), std::min(edgeA, edgeB));
|
||||
float maxOverlap = std::min(std::max(rayA, rayB), std::max(edgeA, edgeB));
|
||||
if( minOverlap > maxOverlap ){
|
||||
return result;
|
||||
}
|
||||
result.collision = true;
|
||||
// Zero distance ray
|
||||
if( rayB == 0.0f ){
|
||||
result.closePoint = p;
|
||||
result.closeSqDist = 0.0f;
|
||||
result.farPoint = p;
|
||||
result.farSqDist = 0.0f;
|
||||
}
|
||||
float t1 = minOverlap / abs(rayB);
|
||||
float t2 = maxOverlap / abs(rayB);
|
||||
result.closePoint = p + t1*r;
|
||||
result.closeSqDist = t1*t1*(r.x*r.x + r.y*r.y);
|
||||
result.farPoint = p + t2*r;
|
||||
result.farSqDist = t2*t2*(r.x*r.x + r.y*r.y);
|
||||
|
||||
return result;
|
||||
}
|
||||
else if ( crossRS != 0 && 0<=t && t<=1 && 0<=u && u<=1 )
|
||||
{
|
||||
sf::Vector2f point = p + t*r;
|
||||
|
||||
float sqDist = (point.x-startX)*(point.x-startX) + (point.y-startY)*(point.y-startY);
|
||||
if ( sqDist < minSqDist )
|
||||
{
|
||||
if ( !result.collision ){
|
||||
result.farPoint = point;
|
||||
result.farSqDist = sqDist;
|
||||
}
|
||||
minSqDist = sqDist;
|
||||
result.closePoint = point;
|
||||
result.closeSqDist = sqDist;
|
||||
result.collision = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.farPoint = point;
|
||||
result.farSqDist = sqDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GD_API IsPointInsidePolygon(Polygon2d & poly, float x, float y)
|
||||
{
|
||||
bool inside = false;
|
||||
|
@@ -19,6 +19,15 @@ struct CollisionResult
|
||||
sf::Vector2f move_axis;
|
||||
};
|
||||
|
||||
struct RaycastResult
|
||||
{
|
||||
bool collision;
|
||||
sf::Vector2f closePoint;
|
||||
float closeSqDist;
|
||||
sf::Vector2f farPoint;
|
||||
float farSqDist;
|
||||
};
|
||||
|
||||
/**
|
||||
* Do a collision test between the two polygons.
|
||||
* \warning Polygons must convexes.
|
||||
@@ -33,6 +42,17 @@ struct CollisionResult
|
||||
*/
|
||||
CollisionResult GD_API PolygonCollisionTest(Polygon2d & p1, Polygon2d & p2);
|
||||
|
||||
/**
|
||||
* Do a raycast test.
|
||||
* \warning Polygon must be convex.
|
||||
* For some theory check "Find the Intersection Point of Two Line Segments" (https://www.codeproject.com/Tips/862988/Find-the-Intersection-Point-of-Two-Line-Segments)
|
||||
*
|
||||
* \return A raycast result with the contact points and distances
|
||||
*
|
||||
* \ingroup GameEngine
|
||||
*/
|
||||
RaycastResult GD_API PolygonRaycastTest(Polygon2d & poly, float startX, float startY, float endX, float endY);
|
||||
|
||||
/**
|
||||
* Check if a point is inside a polygon.
|
||||
*
|
||||
|
@@ -4,6 +4,7 @@
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCpp/Extensions/Builtin/MathematicalTools.h"
|
||||
#include "GDCpp/Runtime/RuntimeObject.h"
|
||||
@@ -390,6 +391,43 @@ bool RuntimeObject::IsCollidingWithPoint(float pointX, float pointY){
|
||||
return false;
|
||||
}
|
||||
|
||||
RaycastResult RuntimeObject::RaycastTest(float x, float y, float angle, float dist, bool closest){
|
||||
float objW = GetWidth();
|
||||
float objH = GetHeight();
|
||||
float diffX = GetDrawableX()+GetCenterX() - x;
|
||||
float diffY = GetDrawableY()+GetCenterY() - y;
|
||||
float boundingRadius = sqrt(objW*objW + objH*objH)/2.0;
|
||||
|
||||
RaycastResult result;
|
||||
result.collision = false;
|
||||
|
||||
if ( sqrt(diffX*diffX + diffY*diffY) > boundingRadius + dist )
|
||||
return result;
|
||||
|
||||
float endX = x + dist*cos(angle*3.14159/180.0);
|
||||
float endY = y + dist*sin(angle*3.14159/180.0);
|
||||
float testSqDist = closest ? dist*dist : 0.0f;
|
||||
|
||||
vector<Polygon2d> hitboxes = GetHitBoxes();
|
||||
for (std::size_t i = 0; i < hitboxes.size(); ++i)
|
||||
{
|
||||
RaycastResult res = PolygonRaycastTest(hitboxes[i], x, y, endX, endY);
|
||||
|
||||
if ( res.collision ) {
|
||||
if ( closest && (res.closeSqDist < testSqDist) ) {
|
||||
testSqDist = res.closeSqDist;
|
||||
result = res;
|
||||
}
|
||||
else if ( !closest && (res.farSqDist > testSqDist) && (res.farSqDist <= dist*dist) ) {
|
||||
testSqDist = res.farSqDist;
|
||||
result = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void RuntimeObject::SeparateObjectsWithoutForces( std::map <gd::String, std::vector<RuntimeObject*> *> pickedObjectLists)
|
||||
{
|
||||
vector<RuntimeObject*> objects2;
|
||||
|
@@ -20,6 +20,7 @@ namespace gd { class InitialInstance; }
|
||||
namespace gd { class Object; }
|
||||
namespace sf { class RenderTarget; }
|
||||
class Polygon2d;
|
||||
class RaycastResult;
|
||||
class RuntimeScene;
|
||||
|
||||
/**
|
||||
@@ -231,6 +232,17 @@ public:
|
||||
*/
|
||||
bool IsCollidingWithPoint(float pointX, float pointY);
|
||||
|
||||
/**
|
||||
* \brief Check if a ray intersect any object hitbox.
|
||||
* \param x The raycast source X
|
||||
* \param y The raycast source Y
|
||||
* \param angle The raycast angle
|
||||
* \param dist The raycast max distance
|
||||
* \param closest Get the closest or farthest collision mask result?
|
||||
* \return A raycast result with the contact points and distances
|
||||
*/
|
||||
RaycastResult RaycastTest(float x, float y, float angle, float dist, bool closest);
|
||||
|
||||
/**
|
||||
* \brief Check collision with each object of the list using their hitboxes, and move the object
|
||||
* according to the sum of the move vector returned by each collision test.
|
||||
|
@@ -101,6 +101,7 @@ BaseObjectExtension::BaseObjectExtension()
|
||||
GetAllConditions()["CollisionNP"]
|
||||
.AddCodeOnlyParameter("currentScene", "") //We need an extra parameter pointing to the scene.
|
||||
.SetFunctionName("gdjs.evtTools.object.hitBoxesCollisionTest");
|
||||
GetAllConditions()["Raycast"].SetFunctionName("gdjs.evtTools.object.raycastObject");
|
||||
GetAllConditions()["Distance"].SetFunctionName("gdjs.evtTools.object.distanceTest");
|
||||
GetAllConditions()["SeDirige"].SetFunctionName("gdjs.evtTools.object.movesTowardTest");
|
||||
GetAllConditions()["EstTourne"].SetFunctionName("gdjs.evtTools.object.turnedTowardTest");
|
||||
|
@@ -97,8 +97,9 @@ bool Exporter::ExportWholePixiProject(gd::Project & project, gd::String exportDi
|
||||
bool minify, bool exportForCordova)
|
||||
{
|
||||
ExporterHelper helper(fs, gdjsRoot, codeOutputDir);
|
||||
gd::Project exportedProject = project;
|
||||
|
||||
auto exportProject = [this, &project, &minify,
|
||||
auto exportProject = [this, &exportedProject, &minify,
|
||||
&exportForCordova, &helper](gd::String exportDir)
|
||||
{
|
||||
wxProgressDialog * progressDialogPtr = NULL;
|
||||
@@ -111,8 +112,6 @@ bool Exporter::ExportWholePixiProject(gd::Project & project, gd::String exportDi
|
||||
fs.MkDir(exportDir);
|
||||
std::vector<gd::String> includesFiles;
|
||||
|
||||
gd::Project exportedProject = project;
|
||||
|
||||
//Export the resources (before generating events as some resources filenames may be updated)
|
||||
helper.ExportResources(fs, exportedProject, exportDir, progressDialogPtr);
|
||||
|
||||
@@ -168,11 +167,12 @@ bool Exporter::ExportWholePixiProject(gd::Project & project, gd::String exportDi
|
||||
{
|
||||
//Prepare the export directory
|
||||
fs.MkDir(exportDir);
|
||||
if (!helper.ExportCordovaConfigFile(project, exportDir))
|
||||
return false;
|
||||
|
||||
if (!exportProject(exportDir + "/www"))
|
||||
return false;
|
||||
|
||||
if (!helper.ExportCordovaConfigFile(exportedProject, exportDir))
|
||||
return false;
|
||||
} else {
|
||||
if (!exportProject(exportDir))
|
||||
return false;
|
||||
|
@@ -58,9 +58,10 @@ static void GenerateFontsDeclaration(gd::AbstractFileSystem & fs, const gd::Stri
|
||||
css += urlPrefix + relativeFile;
|
||||
css +="') format('truetype'); }";
|
||||
|
||||
// Use the font for a dummy text to trigger immediate load of the font at game startup
|
||||
html += "<div style=\"font-family: 'gdjs_font_";
|
||||
html += relativeFile;
|
||||
html += "';\">.</div>";
|
||||
html += "'; color: black;\">.</div>";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +80,9 @@ bool ExporterHelper::ExportLayoutForPixiPreview(gd::Project & project, gd::Layou
|
||||
|
||||
gd::Project exportedProject = 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);
|
||||
//Generate events code
|
||||
@@ -156,10 +160,42 @@ bool ExporterHelper::ExportPixiIndexFile(gd::String source, gd::String exportDir
|
||||
|
||||
bool ExporterHelper::ExportCordovaConfigFile(const gd::Project & project, gd::String exportDir)
|
||||
{
|
||||
auto & platformSpecificAssets = project.GetPlatformSpecificAssets();
|
||||
auto & resourceManager = project.GetResourcesManager();
|
||||
auto getIconFilename = [&resourceManager, &platformSpecificAssets](const gd::String & platform, const gd::String & name) {
|
||||
const gd::String & file = resourceManager.GetResource(platformSpecificAssets.Get(platform, name)).GetFile();
|
||||
return file.empty() ? "" : "www/" + file;
|
||||
};
|
||||
|
||||
gd::String str = fs.ReadFile(gdjsRoot + "/Runtime/Cordova/config.xml")
|
||||
.FindAndReplace("GDJS_PROJECTNAME", project.GetName())
|
||||
.FindAndReplace("GDJS_PACKAGENAME", project.GetPackageName())
|
||||
.FindAndReplace("GDJS_ORIENTATION", "default");
|
||||
.FindAndReplace("GDJS_ORIENTATION", project.GetOrientation())
|
||||
// Android icons
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_36", getIconFilename("android", "icon-36"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_48", getIconFilename("android", "icon-48"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_72", getIconFilename("android", "icon-72"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_96", getIconFilename("android", "icon-96"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_144", getIconFilename("android", "icon-144"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_192", getIconFilename("android", "icon-192"))
|
||||
// iOS icons
|
||||
.FindAndReplace("GDJS_ICON_IOS_180", getIconFilename("ios", "icon-180"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_60", getIconFilename("ios", "icon-60"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_120", getIconFilename("ios", "icon-120"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_76", getIconFilename("ios", "icon-76"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_152", getIconFilename("ios", "icon-152"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_40", getIconFilename("ios", "icon-40"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_80", getIconFilename("ios", "icon-80"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_57", getIconFilename("ios", "icon-57"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_114", getIconFilename("ios", "icon-114"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_72", getIconFilename("ios", "icon-72"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_144", getIconFilename("ios", "icon-144"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_167", getIconFilename("ios", "icon-167"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_29", getIconFilename("ios", "icon-29"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_58", getIconFilename("ios", "icon-58"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_50", getIconFilename("ios", "icon-50"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_100", getIconFilename("ios", "icon-100"))
|
||||
;
|
||||
|
||||
if (!fs.WriteToFile(exportDir + "/config.xml", str))
|
||||
{
|
||||
@@ -196,14 +232,14 @@ bool ExporterHelper::ExportCocos2dFiles(const gd::Project & project, gd::String
|
||||
std::vector<gd::String> noIncludesInThisFile;
|
||||
if (!CompleteIndexFile(str, customCss, customHtml, exportDir, noIncludesInThisFile, ""))
|
||||
{
|
||||
lastError = "Unable to complete Cocos2d index.html file.";
|
||||
lastError = "Unable to complete Cocos2d-JS index.html file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
//Write the index.html file
|
||||
if (!fs.WriteToFile(exportDir + "/index.html", str))
|
||||
{
|
||||
lastError = "Unable to write Cocos2d index.html file.";
|
||||
lastError = "Unable to write Cocos2d-JS index.html file.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -229,7 +265,7 @@ bool ExporterHelper::ExportCocos2dFiles(const gd::Project & project, gd::String
|
||||
|
||||
if (!fs.WriteToFile(exportDir + "/project.json", str))
|
||||
{
|
||||
lastError = "Unable to write Cocos2d project.json file.";
|
||||
lastError = "Unable to write Cocos2d-JS project.json file.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -337,6 +373,7 @@ bool ExporterHelper::ExportEventsCode(gd::Project & project, gd::String outputDi
|
||||
//First, do not forget common includes (they must be included before events generated code files).
|
||||
InsertUnique(includesFiles, "libs/jshashtable.js");
|
||||
InsertUnique(includesFiles, "gd.js");
|
||||
InsertUnique(includesFiles, "gd-splash-image.js");
|
||||
InsertUnique(includesFiles, "libs/hshg.js");
|
||||
InsertUnique(includesFiles, "libs/rbush.js");
|
||||
InsertUnique(includesFiles, "inputmanager.js");
|
||||
|
@@ -12,10 +12,47 @@
|
||||
<allow-intent href="geo:*" />
|
||||
<platform name="android">
|
||||
<allow-intent href="market:*" />
|
||||
<icon src="GDJS_ICON_ANDROID_36" density="ldpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_48" density="mdpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_72" density="hdpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_96" density="xhdpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_144" density="xxhdpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_192" density="xxxhdpi" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
<!-- iOS 8.0+ -->
|
||||
<!-- iPhone 6 Plus -->
|
||||
<icon src="GDJS_ICON_IOS_180" width="180" height="180" />
|
||||
<!-- iOS 7.0+ -->
|
||||
<!-- iPhone / iPod Touch -->
|
||||
<icon src="GDJS_ICON_IOS_60" width="60" height="60" />
|
||||
<icon src="GDJS_ICON_IOS_120" width="120" height="120" />
|
||||
<!-- iPad -->
|
||||
<icon src="GDJS_ICON_IOS_76" width="76" height="76" />
|
||||
<icon src="GDJS_ICON_IOS_152" width="152" height="152" />
|
||||
<!-- Spotlight Icon -->
|
||||
<icon src="GDJS_ICON_IOS_40" width="40" height="40" />
|
||||
<icon src="GDJS_ICON_IOS_80" width="80" height="80" />
|
||||
<!-- iOS 6.1 -->
|
||||
<!-- iPhone / iPod Touch -->
|
||||
<icon src="GDJS_ICON_IOS_57" width="57" height="57" />
|
||||
<icon src="GDJS_ICON_IOS_114" width="114" height="114" />
|
||||
<!-- iPad -->
|
||||
<icon src="GDJS_ICON_IOS_72" width="72" height="72" />
|
||||
<icon src="GDJS_ICON_IOS_144" width="144" height="144" />
|
||||
<!-- iPad Pro -->
|
||||
<icon src="GDJS_ICON_IOS_167" width="167" height="167" />
|
||||
<!-- iPhone Spotlight and Settings Icon -->
|
||||
<icon src="GDJS_ICON_IOS_29" width="29" height="29" />
|
||||
<icon src="GDJS_ICON_IOS_58" width="58" height="58" />
|
||||
<!-- iPad Spotlight and Settings Icon -->
|
||||
<icon src="GDJS_ICON_IOS_50" width="50" height="50" />
|
||||
<icon src="GDJS_ICON_IOS_100" width="100" height="100" />
|
||||
<!-- iPad Pro -->
|
||||
<icon src="GDJS_ICON_IOS_167" width="167" height="167" />
|
||||
</platform>
|
||||
<preference name="orientation" value="GDJS_ORIENTATION" />
|
||||
<preference name="BackgroundColor" value="0xff000000"/>
|
||||
</widget>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
gdjs.LoadingScreenCocosRenderer = function(runtimeGamePixiRenderer)
|
||||
gdjs.LoadingScreenCocosRenderer = function(runtimeGamePixiRenderer, loadingScreenSetup)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -13,3 +13,7 @@ gdjs.LoadingScreenRenderer = gdjs.LoadingScreenCocosRenderer; //Register the cla
|
||||
gdjs.LoadingScreenCocosRenderer.prototype.render = function(percent) {
|
||||
console.log("Loading " + percent + "%");
|
||||
};
|
||||
|
||||
gdjs.LoadingScreenCocosRenderer.prototype.unload = function() {
|
||||
// Nothing to do
|
||||
};
|
||||
|
@@ -316,6 +316,47 @@ gdjs.evtTools.object.pickNearestObject = function(objectsLists, x, y, inverted)
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.raycastObject = function(objectsLists, x, y, angle, dist, varX, varY, inverted) {
|
||||
var matchObject = null;
|
||||
var testSqDist = inverted ? 0 : dist*dist;
|
||||
var resultX = 0;
|
||||
var resultY = 0;
|
||||
|
||||
var lists = gdjs.staticArray(gdjs.evtTools.object.raycastObject);
|
||||
objectsLists.values(lists);
|
||||
for (var i = 0; i < lists.length; i++) {
|
||||
var list = lists[i];
|
||||
|
||||
for (var j = 0; j < list.length; j++) {
|
||||
var object = list[j];
|
||||
var result = object.raycastTest(x, y, angle, dist, !inverted);
|
||||
|
||||
if( result.collision ) {
|
||||
if ( !inverted && (result.closeSqDist <= testSqDist) ) {
|
||||
testSqDist = result.closeSqDist;
|
||||
matchObject = object;
|
||||
resultX = result.closeX;
|
||||
resultY = result.closeY;
|
||||
}
|
||||
else if ( inverted && (result.farSqDist >= testSqDist) ) {
|
||||
testSqDist = result.farSqDist;
|
||||
matchObject = object;
|
||||
resultX = result.farX;
|
||||
resultY = result.farY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !matchObject )
|
||||
return false;
|
||||
|
||||
gdjs.evtTools.object.pickOnly(objectsLists, matchObject);
|
||||
varX.setNumber(resultX);
|
||||
varY.setNumber(resultY);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Do the work of creating a new object
|
||||
* @private
|
||||
|
@@ -108,13 +108,13 @@ gdjs.evtTools.runtimeScene.getTime = function(runtimeScene, what) {
|
||||
else if ( what === "sec" )
|
||||
return now.getSeconds();
|
||||
else if ( what === "mday" )
|
||||
return now.getdate();
|
||||
return now.getDate();
|
||||
else if ( what === "mon" )
|
||||
return now.getMonth();
|
||||
else if ( what === "year" )
|
||||
return now.getFullYear() - 1900; //Conform to the C way of returning years.
|
||||
else if ( what === "wday" )
|
||||
return now.getday();
|
||||
return now.getDay();
|
||||
else if ( what === "yday" ) {
|
||||
var start = new Date(now.getFullYear(), 0, 0);
|
||||
var diff = now - start;
|
||||
|
1
GDJS/Runtime/gd-splash-image.js
Normal file
1
GDJS/Runtime/gd-splash-image.js
Normal file
File diff suppressed because one or more lines are too long
@@ -2,6 +2,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
|
@@ -1,16 +1,70 @@
|
||||
gdjs.LoadingScreenPixiRenderer = function(runtimeGamePixiRenderer)
|
||||
{
|
||||
this._pixiRenderer = runtimeGamePixiRenderer.getPIXIRenderer();
|
||||
this._loadingScreen = new PIXI.Container();
|
||||
this._text = new PIXI.Text(" ", {font: "bold 60px Arial", fill: "#FFFFFF", align: "center"});
|
||||
this._loadingScreen.addChild(this._text);
|
||||
this._text.position.y = this._pixiRenderer.height/2;
|
||||
}
|
||||
gdjs.LoadingScreenPixiRenderer = function(runtimeGamePixiRenderer, loadingScreenSetup) {
|
||||
this._pixiRenderer = runtimeGamePixiRenderer.getPIXIRenderer();
|
||||
this._loadingScreen = new PIXI.Container();
|
||||
|
||||
this._progressText = new PIXI.Text(' ', {
|
||||
font: '30px Arial',
|
||||
fill: '#FFFFFF',
|
||||
align: 'center',
|
||||
});
|
||||
this._loadingScreen.addChild(this._progressText);
|
||||
|
||||
if (loadingScreenSetup && loadingScreenSetup.showGDevelopSplash) {
|
||||
this._madeWithText = new PIXI.Text('Made with', {
|
||||
font: '30px Arial',
|
||||
fill: '#FFFFFF',
|
||||
align: 'center',
|
||||
});
|
||||
this._madeWithText.position.y = this._pixiRenderer.height / 2 - 130;
|
||||
this._websiteText = new PIXI.Text('gdevelop-app.com', {
|
||||
font: '30px Arial',
|
||||
fill: '#FFFFFF',
|
||||
align: 'center',
|
||||
});
|
||||
this._websiteText.position.y = this._pixiRenderer.height / 2 + 100;
|
||||
|
||||
this._splashImage = new PIXI.Sprite.fromImage(gdjs.splashImage);
|
||||
this._splashImage.position.x = this._pixiRenderer.width / 2;
|
||||
this._splashImage.position.y = this._pixiRenderer.height / 2;
|
||||
this._splashImage.anchor.x = 0.5;
|
||||
this._splashImage.anchor.y = 0.5;
|
||||
this._splashImage.scale.x = this._pixiRenderer.width / 800;
|
||||
this._splashImage.scale.y = this._pixiRenderer.width / 800;
|
||||
this._loadingScreen.addChild(this._splashImage);
|
||||
this._loadingScreen.addChild(this._madeWithText);
|
||||
this._loadingScreen.addChild(this._websiteText);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.LoadingScreenRenderer = gdjs.LoadingScreenPixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.LoadingScreenPixiRenderer.prototype.render = function(percent) {
|
||||
this._text.text = percent + "%";
|
||||
this._text.position.x = this._pixiRenderer.width/2 - this._text.width/2;
|
||||
this._pixiRenderer.render(this._loadingScreen);
|
||||
var screenBorder = 10;
|
||||
|
||||
if (this._madeWithText) {
|
||||
this._madeWithText.position.x =
|
||||
this._pixiRenderer.width / 2 - this._madeWithText.width / 2;
|
||||
this._madeWithText.position.y =
|
||||
this._pixiRenderer.height / 2 -
|
||||
this._splashImage.height / 2 -
|
||||
this._madeWithText.height -
|
||||
20;
|
||||
}
|
||||
if (this._websiteText) {
|
||||
this._websiteText.position.x =
|
||||
this._pixiRenderer.width - this._websiteText.width - screenBorder;
|
||||
this._websiteText.position.y =
|
||||
this._pixiRenderer.height - this._websiteText.height - screenBorder;
|
||||
}
|
||||
|
||||
this._progressText.text = percent + '%';
|
||||
this._progressText.position.x = screenBorder;
|
||||
this._progressText.position.y =
|
||||
this._pixiRenderer.height - this._progressText.height - screenBorder;
|
||||
|
||||
this._pixiRenderer.render(this._loadingScreen);
|
||||
};
|
||||
|
||||
gdjs.LoadingScreenPixiRenderer.prototype.unload = function() {
|
||||
// Nothing to do
|
||||
};
|
||||
|
@@ -219,6 +219,146 @@ gdjs.Polygon.collisionTest._statics = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Do a raycast test.<br>
|
||||
* Please note that the polygon must be <b>convex</b>!
|
||||
* For some theory, check <a href="https://www.codeproject.com/Tips/862988/Find-the-Intersection-Point-of-Two-Line-Segments">Find the Intersection Point of Two Line Segments</a>
|
||||
*
|
||||
* @method raycastTest
|
||||
* @static
|
||||
* @param poly {Polygon} The polygon to test
|
||||
* @param startX {Number} The raycast start point X
|
||||
* @param startY {Number} The raycast start point Y
|
||||
* @param endX {Number} The raycast end point X
|
||||
* @param endY {Number} The raycast end point Y
|
||||
* @return A raycast result with the contact points and distances
|
||||
*/
|
||||
gdjs.Polygon.raycastTest = function(poly, startX, startY, endX, endY)
|
||||
{
|
||||
var result = gdjs.Polygon.raycastTest._statics.result;
|
||||
result.collision = false;
|
||||
|
||||
if ( poly.vertices.length < 2 )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
poly.computeEdges();
|
||||
var p = gdjs.Polygon.raycastTest._statics.p;
|
||||
var q = gdjs.Polygon.raycastTest._statics.q;
|
||||
var r = gdjs.Polygon.raycastTest._statics.r;
|
||||
var s = gdjs.Polygon.raycastTest._statics.s;
|
||||
var minSqDist = Number.MAX_VALUE;
|
||||
|
||||
// Ray segment: p + t*r, with p = start and r = end - start
|
||||
p[0] = startX;
|
||||
p[1] = startY;
|
||||
r[0] = endX - startX;
|
||||
r[1] = endY - startY;
|
||||
|
||||
for(var i=0; i<poly.edges.length; i++)
|
||||
{
|
||||
// Edge segment: q + u*s
|
||||
q[0] = poly.vertices[i][0];
|
||||
q[1] = poly.vertices[i][1];
|
||||
s[0] = poly.edges[i][0];
|
||||
s[1] = poly.edges[i][1];
|
||||
var deltaQP = gdjs.Polygon.raycastTest._statics.deltaQP;
|
||||
deltaQP[0] = q[0] - p[0];
|
||||
deltaQP[1] = q[1] - p[1];
|
||||
var crossRS = gdjs.Polygon.crossProduct(r, s);
|
||||
var t = gdjs.Polygon.crossProduct(deltaQP, s) / crossRS;
|
||||
var u = gdjs.Polygon.crossProduct(deltaQP, r) / crossRS;
|
||||
|
||||
// Collinear
|
||||
if ( Math.abs(crossRS) <= 0.0001 && Math.abs(gdjs.Polygon.crossProduct(deltaQP, r)) <= 0.0001 )
|
||||
{
|
||||
// Project the ray and the edge to work on floats, keeping linearity through t
|
||||
var axis = gdjs.Polygon.raycastTest._statics.axis;
|
||||
axis[0] = r[0];
|
||||
axis[1] = r[1];
|
||||
gdjs.Polygon.normalise(axis);
|
||||
var rayA = 0;
|
||||
var rayB = gdjs.Polygon.dotProduct(axis, r);
|
||||
var edgeA = gdjs.Polygon.dotProduct(axis, deltaQP);
|
||||
var edgeB = gdjs.Polygon.dotProduct(axis, [deltaQP[0] + s[0], deltaQP[1] + s[1]]);
|
||||
// Get overlapping range
|
||||
var minOverlap = Math.max(Math.min(rayA, rayB), Math.min(edgeA, edgeB));
|
||||
var maxOverlap = Math.min(Math.max(rayA, rayB), Math.max(edgeA, edgeB));
|
||||
if( minOverlap > maxOverlap ){
|
||||
return result;
|
||||
}
|
||||
result.collision = true;
|
||||
// Zero distance ray
|
||||
if( rayB === 0 ){
|
||||
result.closeX = startX;
|
||||
result.closeY = startY;
|
||||
result.closeSqDist = 0;
|
||||
result.farX = startX;
|
||||
result.farY = startY;
|
||||
result.farSqDist = 0;
|
||||
}
|
||||
var t1 = minOverlap / Math.abs(rayB);
|
||||
var t2 = maxOverlap / Math.abs(rayB);
|
||||
result.closeX = startX + t1*r[0];
|
||||
result.closeY = startY + t1*r[1];
|
||||
result.closeSqDist = t1*t1*(r[0]*r[0] + r[1]*r[1]);
|
||||
result.farX = startX + t2*r[0];
|
||||
result.farY = startY + t2*r[1];
|
||||
result.farSqDist = t2*t2*(r[0]*r[0] + r[1]*r[1]);
|
||||
|
||||
return result;
|
||||
}
|
||||
// One point intersection
|
||||
else if ( crossRS !== 0 && 0<=t && t<=1 && 0<=u && u<=1 )
|
||||
{
|
||||
var x = p[0] + t*r[0];
|
||||
var y = p[1] + t*r[1];
|
||||
|
||||
var sqDist = (x-startX)*(x-startX) + (y-startY)*(y-startY);
|
||||
if ( sqDist < minSqDist )
|
||||
{
|
||||
if ( !result.collision ){
|
||||
result.farX = x;
|
||||
result.farY = y;
|
||||
result.farSqDist = sqDist;
|
||||
}
|
||||
minSqDist = sqDist;
|
||||
result.closeX = x;
|
||||
result.closeY = y;
|
||||
result.closeSqDist = sqDist;
|
||||
result.collision = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.farX = x;
|
||||
result.farY = y;
|
||||
result.farSqDist = sqDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
gdjs.Polygon.raycastTest._statics = {
|
||||
p: [0,0],
|
||||
q: [0,0],
|
||||
r: [0,0],
|
||||
s: [0,0],
|
||||
deltaQP: [0,0],
|
||||
axis: [0,0],
|
||||
result: {
|
||||
collision: false,
|
||||
closeX: 0,
|
||||
closeY: 0,
|
||||
closeSqDist: 0,
|
||||
farX: 0,
|
||||
farY: 0,
|
||||
farSqDist: 0
|
||||
}
|
||||
}
|
||||
|
||||
//Tools functions :
|
||||
gdjs.Polygon.normalise = function(v)
|
||||
{
|
||||
@@ -237,6 +377,13 @@ gdjs.Polygon.dotProduct = function(a, b)
|
||||
return dp;
|
||||
}
|
||||
|
||||
gdjs.Polygon.crossProduct = function(a, b)
|
||||
{
|
||||
var cp = a[0]*b[1] - a[1]*b[0];
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
gdjs.Polygon.project = function(axis, p, result)
|
||||
{
|
||||
var dp = gdjs.Polygon.dotProduct(axis, p.vertices[0]);
|
||||
|
@@ -13,38 +13,44 @@
|
||||
* @param data The object (usually stored in data.json) containing the full project data
|
||||
* @param spec Optional object for specifiying additional options: {forceFullscreen: ...}
|
||||
*/
|
||||
gdjs.RuntimeGame = function(data, spec)
|
||||
{
|
||||
spec = spec || {};
|
||||
gdjs.RuntimeGame = function(data, spec) {
|
||||
spec = spec || {};
|
||||
|
||||
this._variables = new gdjs.VariablesContainer(data.variables);
|
||||
this._data = data;
|
||||
this._imageManager = new gdjs.ImageManager(data.resources ? data.resources.resources : undefined);
|
||||
this._soundManager = new gdjs.SoundManager(data.resources ? data.resources.resources : undefined);
|
||||
this._minFPS = data ? parseInt(data.properties.minFPS, 10) : 15;
|
||||
this._variables = new gdjs.VariablesContainer(data.variables);
|
||||
this._data = data;
|
||||
this._imageManager = new gdjs.ImageManager(
|
||||
data.resources ? data.resources.resources : undefined
|
||||
);
|
||||
this._soundManager = new gdjs.SoundManager(
|
||||
data.resources ? data.resources.resources : undefined
|
||||
);
|
||||
this._minFPS = data ? parseInt(data.properties.minFPS, 10) : 15;
|
||||
|
||||
this._defaultWidth = data.properties.windowWidth; //Default size for scenes cameras
|
||||
this._defaultHeight = data.properties.windowHeight;
|
||||
this._originalWidth = data.properties.windowWidth; //Original size of the game, won't be changed.
|
||||
this._originalHeight = data.properties.windowHeight;
|
||||
this._renderer = new gdjs.RuntimeGameRenderer(this,
|
||||
this._defaultWidth, this._defaultHeight,
|
||||
spec.forceFullscreen || false);
|
||||
this._defaultWidth = data.properties.windowWidth; //Default size for scenes cameras
|
||||
this._defaultHeight = data.properties.windowHeight;
|
||||
this._originalWidth = data.properties.windowWidth; //Original size of the game, won't be changed.
|
||||
this._originalHeight = data.properties.windowHeight;
|
||||
this._renderer = new gdjs.RuntimeGameRenderer(
|
||||
this,
|
||||
this._defaultWidth,
|
||||
this._defaultHeight,
|
||||
spec.forceFullscreen || false
|
||||
);
|
||||
|
||||
//Game loop management (see startGameLoop method)
|
||||
this._sceneStack = new gdjs.SceneStack(this);
|
||||
this._notifySceneForResize = false; //When set to true, the current scene is notified that canvas size changed.
|
||||
//Game loop management (see startGameLoop method)
|
||||
this._sceneStack = new gdjs.SceneStack(this);
|
||||
this._notifySceneForResize = false; //When set to true, the current scene is notified that canvas size changed.
|
||||
|
||||
//Inputs :
|
||||
this._inputManager = new gdjs.InputManager();
|
||||
//Inputs :
|
||||
this._inputManager = new gdjs.InputManager();
|
||||
|
||||
//Allow to specify an external layout to insert in the first scene:
|
||||
this._injectExternalLayout = spec.injectExternalLayout || "";
|
||||
//Allow to specify an external layout to insert in the first scene:
|
||||
this._injectExternalLayout = spec.injectExternalLayout || '';
|
||||
};
|
||||
|
||||
gdjs.RuntimeGame.prototype.getRenderer = function() {
|
||||
return this._renderer;
|
||||
}
|
||||
return this._renderer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the variables of the RuntimeGame.
|
||||
@@ -52,7 +58,7 @@ gdjs.RuntimeGame.prototype.getRenderer = function() {
|
||||
* @return a variablesContainer object.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getVariables = function() {
|
||||
return this._variables;
|
||||
return this._variables;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -61,7 +67,7 @@ gdjs.RuntimeGame.prototype.getVariables = function() {
|
||||
* @return {gdjs.SoundManager} The sound manager.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getSoundManager = function() {
|
||||
return this._soundManager;
|
||||
return this._soundManager;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -70,7 +76,7 @@ gdjs.RuntimeGame.prototype.getSoundManager = function() {
|
||||
* @return {gdjs.ImageManager} The image manager.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getImageManager = function() {
|
||||
return this._imageManager;
|
||||
return this._imageManager;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -79,7 +85,7 @@ gdjs.RuntimeGame.prototype.getImageManager = function() {
|
||||
* @return The input manager owned by the game
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getInputManager = function() {
|
||||
return this._inputManager;
|
||||
return this._inputManager;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -88,7 +94,7 @@ gdjs.RuntimeGame.prototype.getInputManager = function() {
|
||||
* @return The object associated to the game.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getGameData = function() {
|
||||
return this._data;
|
||||
return this._data;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,20 +105,20 @@ gdjs.RuntimeGame.prototype.getGameData = function() {
|
||||
* @return The data associated to the scene.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getSceneData = function(sceneName) {
|
||||
var scene = undefined;
|
||||
for(var i = 0, len = this._data.layouts.length;i<len;++i) {
|
||||
var sceneData = this._data.layouts[i];
|
||||
var scene = undefined;
|
||||
for (var i = 0, len = this._data.layouts.length; i < len; ++i) {
|
||||
var sceneData = this._data.layouts[i];
|
||||
|
||||
if ( sceneName === undefined || sceneData.name === sceneName ) {
|
||||
scene = sceneData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sceneName === undefined || sceneData.name === sceneName) {
|
||||
scene = sceneData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( scene === undefined )
|
||||
console.warn("The game has no scene called \""+sceneName+"\"");
|
||||
if (scene === undefined)
|
||||
console.warn('The game has no scene called "' + sceneName + '"');
|
||||
|
||||
return scene;
|
||||
return scene;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -123,17 +129,17 @@ gdjs.RuntimeGame.prototype.getSceneData = function(sceneName) {
|
||||
* @return true if the scene exists. If sceneName is undefined, true if the game has a scene.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.hasScene = function(sceneName) {
|
||||
var isTrue = false;
|
||||
for(var i = 0, len = this._data.layouts.length;i<len;++i) {
|
||||
var sceneData = this._data.layouts[i];
|
||||
var isTrue = false;
|
||||
for (var i = 0, len = this._data.layouts.length; i < len; ++i) {
|
||||
var sceneData = this._data.layouts[i];
|
||||
|
||||
if ( sceneName === undefined || sceneData.name == sceneName ) {
|
||||
isTrue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sceneName === undefined || sceneData.name == sceneName) {
|
||||
isTrue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isTrue;
|
||||
return isTrue;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -144,17 +150,17 @@ gdjs.RuntimeGame.prototype.hasScene = function(sceneName) {
|
||||
* @return The data associated to the external layout or null if not found.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getExternalLayoutData = function(name) {
|
||||
var externalLayout = null;
|
||||
for(var i = 0, len = this._data.externalLayouts.length;i<len;++i) {
|
||||
var layoutData = this._data.externalLayouts[i];
|
||||
var externalLayout = null;
|
||||
for (var i = 0, len = this._data.externalLayouts.length; i < len; ++i) {
|
||||
var layoutData = this._data.externalLayouts[i];
|
||||
|
||||
if ( layoutData.name === name ) {
|
||||
externalLayout = layoutData;
|
||||
break;
|
||||
}
|
||||
if (layoutData.name === name) {
|
||||
externalLayout = layoutData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return externalLayout;
|
||||
return externalLayout;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -163,7 +169,7 @@ gdjs.RuntimeGame.prototype.getExternalLayoutData = function(name) {
|
||||
* @return The data associated to the global objects.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getInitialObjectsData = function() {
|
||||
return this._data.objects || [];
|
||||
return this._data.objects || [];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -173,7 +179,7 @@ gdjs.RuntimeGame.prototype.getInitialObjectsData = function() {
|
||||
* @method getOriginalWidth
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getOriginalWidth = function() {
|
||||
return this._originalWidth;
|
||||
return this._originalWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -183,7 +189,7 @@ gdjs.RuntimeGame.prototype.getOriginalWidth = function() {
|
||||
* @method getOriginalHeight
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getOriginalHeight = function() {
|
||||
return this._originalHeight;
|
||||
return this._originalHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -192,7 +198,7 @@ gdjs.RuntimeGame.prototype.getOriginalHeight = function() {
|
||||
* @method getDefaultWidth
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getDefaultWidth = function() {
|
||||
return this._defaultWidth;
|
||||
return this._defaultWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -201,7 +207,7 @@ gdjs.RuntimeGame.prototype.getDefaultWidth = function() {
|
||||
* @method getDefaultHeight
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getDefaultHeight = function() {
|
||||
return this._defaultHeight;
|
||||
return this._defaultHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -211,7 +217,7 @@ gdjs.RuntimeGame.prototype.getDefaultHeight = function() {
|
||||
* @param width {Number} The new default width
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.setDefaultWidth = function(width) {
|
||||
this._defaultWidth = width;
|
||||
this._defaultWidth = width;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -221,7 +227,7 @@ gdjs.RuntimeGame.prototype.setDefaultWidth = function(width) {
|
||||
* @param height {Number} The new default height
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.setDefaultHeight = function(height) {
|
||||
this._defaultHeight = height;
|
||||
this._defaultHeight = height;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -230,7 +236,7 @@ gdjs.RuntimeGame.prototype.setDefaultHeight = function(height) {
|
||||
* @method getMinimalFramerate
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getMinimalFramerate = function() {
|
||||
return this._minFPS;
|
||||
return this._minFPS;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -238,60 +244,73 @@ gdjs.RuntimeGame.prototype.getMinimalFramerate = function() {
|
||||
* @method loadAllAssets
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.loadAllAssets = function(callback) {
|
||||
var loadingScreen = new gdjs.LoadingScreenRenderer(this.getRenderer());
|
||||
var allAssetsTotal = this._data.resources.resources.length;
|
||||
var loadingScreen = new gdjs.LoadingScreenRenderer(
|
||||
this.getRenderer(),
|
||||
this._data.properties.loadingScreen
|
||||
);
|
||||
var allAssetsTotal = this._data.resources.resources.length;
|
||||
|
||||
var that = this;
|
||||
this._imageManager.loadTextures(function (count, total) {
|
||||
loadingScreen.render(Math.floor(count / allAssetsTotal * 100));
|
||||
}, function() {
|
||||
that._soundManager.preloadAudio(function (count, total) {
|
||||
loadingScreen.render(Math.floor((allAssetsTotal - total + count)
|
||||
/ allAssetsTotal * 100));
|
||||
}, function() {
|
||||
callback();
|
||||
});
|
||||
});
|
||||
var that = this;
|
||||
this._imageManager.loadTextures(
|
||||
function(count, total) {
|
||||
loadingScreen.render(Math.floor(count / allAssetsTotal * 100));
|
||||
},
|
||||
function() {
|
||||
that._soundManager.preloadAudio(
|
||||
function(count, total) {
|
||||
loadingScreen.render(
|
||||
Math.floor((allAssetsTotal - total + count) / allAssetsTotal * 100)
|
||||
);
|
||||
},
|
||||
function() {
|
||||
loadingScreen.unload();
|
||||
callback();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
gdjs.RuntimeGame.prototype.startGameLoop = function() {
|
||||
if ( !this.hasScene() ) {
|
||||
console.log("The game has no scene.");
|
||||
return;
|
||||
if (!this.hasScene()) {
|
||||
console.log('The game has no scene.');
|
||||
return;
|
||||
}
|
||||
|
||||
//Load the first scene
|
||||
var firstSceneName = gdjs.projectData.firstLayout;
|
||||
this._sceneStack.push(
|
||||
this.hasScene(firstSceneName) ? firstSceneName : this.getSceneData().name,
|
||||
this._injectExternalLayout
|
||||
);
|
||||
|
||||
//Uncomment to profile the first x frames of the game.
|
||||
// var x = 500;
|
||||
// var startTime = Date.now();
|
||||
// console.profile("Stepping for " + x + " frames")
|
||||
// for(var i = 0; i < x; ++i) {
|
||||
// this._sceneStack.step(16);
|
||||
// }
|
||||
// console.profileEnd();
|
||||
// var time = Date.now() - startTime;
|
||||
// console.log("Took", time, "ms");
|
||||
// return;
|
||||
|
||||
//The standard game loop
|
||||
var that = this;
|
||||
this._renderer.startGameLoop(function(elapsedTime) {
|
||||
//Manage resize events.
|
||||
if (that._notifySceneForResize) {
|
||||
that._sceneStack.onRendererResized();
|
||||
that._notifySceneForResize = false;
|
||||
}
|
||||
|
||||
//Load the first scene
|
||||
var firstSceneName = gdjs.projectData.firstLayout;
|
||||
this._sceneStack.push(this.hasScene(firstSceneName) ? firstSceneName : this.getSceneData().name,
|
||||
this._injectExternalLayout);
|
||||
//Render and step the scene.
|
||||
if (that._sceneStack.step(elapsedTime)) {
|
||||
that.getInputManager().onFrameEnded();
|
||||
return true;
|
||||
}
|
||||
|
||||
//Uncomment to profile the first x frames of the game.
|
||||
// var x = 500;
|
||||
// var startTime = Date.now();
|
||||
// console.profile("Stepping for " + x + " frames")
|
||||
// for(var i = 0; i < x; ++i) {
|
||||
// this._sceneStack.step(16);
|
||||
// }
|
||||
// console.profileEnd();
|
||||
// var time = Date.now() - startTime;
|
||||
// console.log("Took", time, "ms");
|
||||
// return;
|
||||
|
||||
//The standard game loop
|
||||
var that = this;
|
||||
this._renderer.startGameLoop(function(elapsedTime) {
|
||||
//Manage resize events.
|
||||
if (that._notifySceneForResize) {
|
||||
that._sceneStack.onRendererResized();
|
||||
that._notifySceneForResize = false;
|
||||
}
|
||||
|
||||
//Render and step the scene.
|
||||
if (that._sceneStack.step(elapsedTime)) {
|
||||
that.getInputManager().onFrameEnded();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
@@ -1151,12 +1151,47 @@ gdjs.RuntimeObject.collisionTest = function(obj1, obj2) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Check the distance between two objects.
|
||||
* @method distanceTest
|
||||
* @static
|
||||
* @method raycastTest
|
||||
* @param x {Number} The raycast source X
|
||||
* @param y {Number} The raycast source Y
|
||||
* @param angle {Number} The raycast angle
|
||||
* @param dist {Number} The raycast max distance
|
||||
* @param closest {Boolean} Get the closest or farthest collision mask result?
|
||||
* @return A raycast result with the contact points and distances
|
||||
*/
|
||||
gdjs.RuntimeObject.distanceTest = function(obj1, obj2, distance) {
|
||||
return obj1.getSqDistanceToObject(obj2) <= distance;
|
||||
gdjs.RuntimeObject.prototype.raycastTest = function(x, y, angle, dist, closest) {
|
||||
var objW = this.getWidth();
|
||||
var objH = this.getHeight();
|
||||
var diffX = this.getDrawableX()+this.getCenterX() - x;
|
||||
var diffY = this.getDrawableY()+this.getCenterY() - y;
|
||||
var boundingRadius = Math.sqrt(objW*objW + objH*objH)/2.0;
|
||||
|
||||
var result = gdjs.Polygon.raycastTest._statics.result;
|
||||
result.collision = false;
|
||||
|
||||
if ( Math.sqrt(diffX*diffX + diffY*diffY) > boundingRadius + dist )
|
||||
return result;
|
||||
|
||||
var endX = x + dist*Math.cos(angle*Math.PI/180.0);
|
||||
var endY = y + dist*Math.sin(angle*Math.PI/180.0);
|
||||
var testSqDist = closest ? dist*dist : 0;
|
||||
|
||||
var hitBoxes = this.getHitBoxes();
|
||||
for (var i=0; i<hitBoxes.length; i++) {
|
||||
var res = gdjs.Polygon.raycastTest(hitBoxes[i], x, y, endX, endY);
|
||||
if ( res.collision ) {
|
||||
if ( closest && (res.closeSqDist < testSqDist) ) {
|
||||
testSqDist = res.closeSqDist;
|
||||
result = res;
|
||||
}
|
||||
else if ( !closest && (res.farSqDist > testSqDist) && (res.farSqDist <= dist*dist) ) {
|
||||
testSqDist = res.farSqDist;
|
||||
result = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1174,6 +1209,15 @@ gdjs.RuntimeObject.prototype.insideObject = function(x, y) {
|
||||
&& this.getDrawableY() + this.getHeight() >= y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the distance between two objects.
|
||||
* @method distanceTest
|
||||
* @static
|
||||
*/
|
||||
gdjs.RuntimeObject.distanceTest = function(obj1, obj2, distance) {
|
||||
return obj1.getSqDistanceToObject(obj2) <= distance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the cursor, or any touch, is on the object.
|
||||
*
|
||||
|
@@ -161,7 +161,6 @@ gdjs.RuntimeScene.prototype.unloadScene = function() {
|
||||
this._allInstancesList = [];
|
||||
this._instancesRemoved = [];
|
||||
|
||||
this._renderer = new gdjs.RuntimeSceneRenderer(this, this._runtimeGame ? this._runtimeGame.getRenderer() : null);
|
||||
this._lastId = 0;
|
||||
this._eventsContext = null;
|
||||
|
||||
|
606
GDJS/tests/games/Raycast.gdg
Normal file
606
GDJS/tests/games/Raycast.gdg
Normal file
@@ -0,0 +1,606 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<project firstLayout="">
|
||||
<gdVersion build="96" major="4" minor="0" revision="140" />
|
||||
<properties folderProject="false" linuxExecutableFilename="" macExecutableFilename="" packageName="com.example.gamename" projectFile="C:\Users\Maciel\Programacion\gits\GD\GDJS\tests\games\Raycast.gdg" useExternalSourceFiles="false" winExecutableFilename="" winExecutableIconFile="">
|
||||
<name>Project</name>
|
||||
<author></author>
|
||||
<windowWidth>800</windowWidth>
|
||||
<windowHeight>600</windowHeight>
|
||||
<latestCompilationDirectory></latestCompilationDirectory>
|
||||
<maxFPS>60</maxFPS>
|
||||
<minFPS>10</minFPS>
|
||||
<verticalSync>false</verticalSync>
|
||||
<extensions>
|
||||
<extension name="BuiltinObject" />
|
||||
<extension name="BuiltinAudio" />
|
||||
<extension name="BuiltinVariables" />
|
||||
<extension name="BuiltinTime" />
|
||||
<extension name="BuiltinMouse" />
|
||||
<extension name="BuiltinKeyboard" />
|
||||
<extension name="BuiltinJoystick" />
|
||||
<extension name="BuiltinCamera" />
|
||||
<extension name="BuiltinWindow" />
|
||||
<extension name="BuiltinFile" />
|
||||
<extension name="BuiltinNetwork" />
|
||||
<extension name="BuiltinScene" />
|
||||
<extension name="BuiltinAdvanced" />
|
||||
<extension name="Sprite" />
|
||||
<extension name="BuiltinCommonInstructions" />
|
||||
<extension name="BuiltinCommonConversions" />
|
||||
<extension name="BuiltinStringInstructions" />
|
||||
<extension name="BuiltinMathematicalTools" />
|
||||
<extension name="BuiltinExternalLayouts" />
|
||||
<extension name="PrimitiveDrawing" />
|
||||
</extensions>
|
||||
<platforms>
|
||||
<platform name="GDevelop JS platform" />
|
||||
<platform name="GDevelop C++ platform" />
|
||||
</platforms>
|
||||
<currentPlatform>GDevelop JS platform</currentPlatform>
|
||||
</properties>
|
||||
<resources>
|
||||
<resources>
|
||||
<resource alwaysLoaded="false" file="Grass.png" kind="image" name="Grass.png" smoothed="true" userAdded="true" />
|
||||
<resource alwaysLoaded="false" file="spship2.png" kind="image" name="spship2.png" smoothed="true" userAdded="false" />
|
||||
<resource alwaysLoaded="false" file="Shape1.png" kind="image" name="Shape1.png" smoothed="true" userAdded="false" />
|
||||
<resource alwaysLoaded="false" file="Pea-Happy.png" kind="image" name="Pea-Happy.png" smoothed="true" userAdded="false" />
|
||||
</resources>
|
||||
<resourceFolders />
|
||||
</resources>
|
||||
<objects />
|
||||
<objectsGroups />
|
||||
<variables />
|
||||
<layouts>
|
||||
<layout b="30" disableInputWhenNotFocused="true" mangledName="Scene" name="Scene" oglFOV="90.000000" oglZFar="500.000000" oglZNear="1.000000" r="30" standardSortMethod="false" stopSoundsOnStartup="true" title="" v="30">
|
||||
<uiSettings grid="false" gridB="80" gridG="80" gridHeight="70" gridOffsetX="0" gridOffsetY="20" gridR="80" gridWidth="70" snap="true" windowMask="false" zoomFactor="0.754867" />
|
||||
<objectsGroups>
|
||||
<group name="Colliders">
|
||||
<objects>
|
||||
<object name="Quad" />
|
||||
<object name="Line" />
|
||||
<object name="Triangle" />
|
||||
<object name="Polygon" />
|
||||
</objects>
|
||||
</group>
|
||||
</objectsGroups>
|
||||
<variables />
|
||||
<instances>
|
||||
<instance angle="0.000000" customSize="true" height="64.000000" layer="" locked="false" name="Player" width="64.000000" x="400.000000" y="300.000000" zOrder="2">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="160.000000" layer="" locked="false" name="Quad" width="160.000000" x="511.653046" y="426.899933" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="40.000000" layer="" locked="false" name="Quad" width="40.000000" x="642.864868" y="20.479473" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="40.000000" layer="" locked="false" name="Quad" width="40.000000" x="340.216797" y="524.969666" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="40.000000" layer="" locked="false" name="Quad" width="40.000000" x="52.560143" y="106.766174" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="40.000000" layer="" locked="false" name="Quad" width="40.000000" x="202.182587" y="29.507092" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="40.000000" layer="" locked="false" name="Quad" width="40.000000" x="550.417358" y="268.102661" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="40.000000" layer="" locked="false" name="Quad" width="40.000000" x="259.622040" y="466.785736" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="40.000000" layer="" locked="false" name="Quad" width="40.000000" x="640.945984" y="188.205658" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="80.000000" layer="" locked="false" name="Quad" width="80.000000" x="330.563049" y="69.781281" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="40.000000" layer="" locked="false" name="Quad" width="40.000000" x="13.854311" y="547.935608" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="4.000000" layer="" locked="false" name="Line" width="200.000000" x="42.436562" y="348.760468" zOrder="4">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="2.000000" layer="" locked="false" name="Line" width="64.000000" x="22.880741" y="14.281113" zOrder="4">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="2.000000" layer="" locked="false" name="Line" width="64.000000" x="694.834351" y="81.030281" zOrder="4">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="2.000000" layer="" locked="false" name="Line" width="64.000000" x="321.407257" y="436.969086" zOrder="4">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="Drawer" width="0.000000" x="467.504181" y="221.897949" zOrder="5">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="40.000000" layer="" locked="false" name="Quad" width="40.000000" x="687.335632" y="495.352386" zOrder="1">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="2.000000" layer="" locked="false" name="Line" width="64.000000" x="231.507050" y="134.513855" zOrder="4">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="60.000000" layer="" locked="false" name="Triangle" width="60.000000" x="432.457794" y="17.137005" zOrder="3">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="60.000000" layer="" locked="false" name="Triangle" width="60.000000" x="119.492599" y="63.272141" zOrder="3">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="60.000000" layer="" locked="false" name="Triangle" width="60.000000" x="625.232666" y="333.912476" zOrder="3">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="60.000000" layer="" locked="false" name="Triangle" width="60.000000" x="476.822723" y="355.556915" zOrder="3">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="60.000000" layer="" locked="false" name="Triangle" width="60.000000" x="404.859680" y="522.776550" zOrder="3">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="80.000000" layer="" locked="false" name="Polygon" width="100.000000" x="19.355259" y="383.442963" zOrder="4">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="50.000000" layer="" locked="false" name="Polygon" width="100.000000" x="723.214783" y="536.209045" zOrder="4">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
<instance angle="0.000000" customSize="true" height="140.000000" layer="" locked="false" name="Polygon" width="60.000000" x="517.953369" y="37.765617" zOrder="4">
|
||||
<numberProperties />
|
||||
<stringProperties />
|
||||
<initialVariables />
|
||||
</instance>
|
||||
</instances>
|
||||
<objects>
|
||||
<object name="Player" type="Sprite" updateIfNotVisible="true">
|
||||
<variables />
|
||||
<behaviors />
|
||||
<animations>
|
||||
<animation name="" useMultipleDirections="false">
|
||||
<directions>
|
||||
<direction looping="false" timeBetweenFrames="1.000000">
|
||||
<sprites>
|
||||
<sprite hasCustomCollisionMask="false" image="Pea-Happy.png">
|
||||
<points />
|
||||
<originPoint name="origine" x="18.000000" y="18.000000" />
|
||||
<centerPoint automatic="true" name="centre" x="18.500000" y="18.500000" />
|
||||
<customCollisionMask>
|
||||
<polygon>
|
||||
<vertice x="0.000000" y="0.000000" />
|
||||
<vertice x="37.000000" y="0.000000" />
|
||||
<vertice x="37.000000" y="37.000000" />
|
||||
<vertice x="0.000000" y="37.000000" />
|
||||
</polygon>
|
||||
</customCollisionMask>
|
||||
</sprite>
|
||||
</sprites>
|
||||
</direction>
|
||||
</directions>
|
||||
</animation>
|
||||
</animations>
|
||||
</object>
|
||||
<object name="Quad" type="Sprite" updateIfNotVisible="true">
|
||||
<variables />
|
||||
<behaviors />
|
||||
<animations>
|
||||
<animation name="" useMultipleDirections="false">
|
||||
<directions>
|
||||
<direction looping="false" timeBetweenFrames="1.000000">
|
||||
<sprites>
|
||||
<sprite hasCustomCollisionMask="false" image="Grass.png">
|
||||
<points />
|
||||
<originPoint name="origine" x="0.000000" y="0.000000" />
|
||||
<centerPoint automatic="true" name="centre" x="35.000000" y="35.000000" />
|
||||
<customCollisionMask>
|
||||
<polygon>
|
||||
<vertice x="0.000000" y="0.000000" />
|
||||
<vertice x="70.000000" y="0.000000" />
|
||||
<vertice x="70.000000" y="70.000000" />
|
||||
<vertice x="0.000000" y="70.000000" />
|
||||
</polygon>
|
||||
</customCollisionMask>
|
||||
</sprite>
|
||||
</sprites>
|
||||
</direction>
|
||||
</directions>
|
||||
</animation>
|
||||
</animations>
|
||||
</object>
|
||||
<object name="Line" type="Sprite" updateIfNotVisible="false">
|
||||
<variables />
|
||||
<behaviors />
|
||||
<animations>
|
||||
<animation name="" useMultipleDirections="false">
|
||||
<directions>
|
||||
<direction looping="false" timeBetweenFrames="1.000000">
|
||||
<sprites>
|
||||
<sprite hasCustomCollisionMask="true" image="Pea-Happy.png">
|
||||
<points />
|
||||
<originPoint name="origine" x="0.000000" y="0.000000" />
|
||||
<centerPoint automatic="true" name="centre" x="18.500000" y="18.500000" />
|
||||
<customCollisionMask>
|
||||
<polygon>
|
||||
<vertice x="0.000000" y="18.000000" />
|
||||
<vertice x="36.000000" y="18.000000" />
|
||||
</polygon>
|
||||
</customCollisionMask>
|
||||
</sprite>
|
||||
</sprites>
|
||||
</direction>
|
||||
</directions>
|
||||
</animation>
|
||||
</animations>
|
||||
</object>
|
||||
<object name="Drawer" type="PrimitiveDrawing::Drawer">
|
||||
<variables />
|
||||
<behaviors />
|
||||
<fillOpacity>255</fillOpacity>
|
||||
<outlineSize>0</outlineSize>
|
||||
<outlineOpacity>0</outlineOpacity>
|
||||
<fillColor b="255" g="255" r="255" />
|
||||
<outlineColor b="255" g="34" r="250" />
|
||||
<absoluteCoordinates>true</absoluteCoordinates>
|
||||
</object>
|
||||
<object name="Triangle" type="Sprite" updateIfNotVisible="false">
|
||||
<variables />
|
||||
<behaviors />
|
||||
<animations>
|
||||
<animation name="" useMultipleDirections="false">
|
||||
<directions>
|
||||
<direction looping="false" timeBetweenFrames="1.000000">
|
||||
<sprites>
|
||||
<sprite hasCustomCollisionMask="true" image="spship2.png">
|
||||
<points />
|
||||
<originPoint name="origine" x="0.000000" y="0.000000" />
|
||||
<centerPoint automatic="true" name="centre" x="41.500000" y="41.500000" />
|
||||
<customCollisionMask>
|
||||
<polygon>
|
||||
<vertice x="0.000000" y="0.000000" />
|
||||
<vertice x="83.000000" y="42.000000" />
|
||||
<vertice x="0.000000" y="83.000000" />
|
||||
</polygon>
|
||||
</customCollisionMask>
|
||||
</sprite>
|
||||
</sprites>
|
||||
</direction>
|
||||
</directions>
|
||||
</animation>
|
||||
</animations>
|
||||
</object>
|
||||
<object name="Polygon" type="Sprite" updateIfNotVisible="false">
|
||||
<variables />
|
||||
<behaviors />
|
||||
<animations>
|
||||
<animation name="" useMultipleDirections="false">
|
||||
<directions>
|
||||
<direction looping="false" timeBetweenFrames="1.000000">
|
||||
<sprites>
|
||||
<sprite hasCustomCollisionMask="true" image="Shape1.png">
|
||||
<points />
|
||||
<originPoint name="origine" x="0.000000" y="0.000000" />
|
||||
<centerPoint automatic="true" name="centre" x="89.000000" y="64.500000" />
|
||||
<customCollisionMask>
|
||||
<polygon>
|
||||
<vertice x="0.000000" y="47.000000" />
|
||||
<vertice x="77.000000" y="0.000000" />
|
||||
<vertice x="175.000000" y="11.000000" />
|
||||
<vertice x="178.000000" y="56.000000" />
|
||||
<vertice x="160.000000" y="96.000000" />
|
||||
<vertice x="104.000000" y="129.000000" />
|
||||
<vertice x="7.000000" y="103.000000" />
|
||||
</polygon>
|
||||
</customCollisionMask>
|
||||
</sprite>
|
||||
</sprites>
|
||||
</direction>
|
||||
</directions>
|
||||
</animation>
|
||||
</animations>
|
||||
</object>
|
||||
</objects>
|
||||
<events>
|
||||
<event disabled="false" folded="false">
|
||||
<type>BuiltinCommonInstructions::Standard</type>
|
||||
<conditions>
|
||||
<instruction>
|
||||
<type inverted="false" value="DepartScene" />
|
||||
<parameters>
|
||||
<parameter></parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
</conditions>
|
||||
<actions />
|
||||
<events>
|
||||
<event disabled="false" folded="false">
|
||||
<type>BuiltinCommonInstructions::Repeat</type>
|
||||
<repeatExpression>300</repeatExpression>
|
||||
<conditions />
|
||||
<actions>
|
||||
<instruction>
|
||||
<type inverted="false" value="Create" />
|
||||
<parameters>
|
||||
<parameter></parameter>
|
||||
<parameter>Polygon</parameter>
|
||||
<parameter>Random(200)</parameter>
|
||||
<parameter>Random(200)</parameter>
|
||||
<parameter></parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="Create" />
|
||||
<parameters>
|
||||
<parameter></parameter>
|
||||
<parameter>Line</parameter>
|
||||
<parameter>500 + Random(300)</parameter>
|
||||
<parameter>400 + Random(200)</parameter>
|
||||
<parameter></parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="ChangeScaleHeight" />
|
||||
<parameters>
|
||||
<parameter>Line</parameter>
|
||||
<parameter>=</parameter>
|
||||
<parameter>0.1</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
</actions>
|
||||
<events />
|
||||
</event>
|
||||
</events>
|
||||
</event>
|
||||
<event disabled="false" folded="false">
|
||||
<type>BuiltinCommonInstructions::Standard</type>
|
||||
<conditions />
|
||||
<actions>
|
||||
<instruction>
|
||||
<type inverted="false" value="ChangeColor" />
|
||||
<parameters>
|
||||
<parameter>Colliders</parameter>
|
||||
<parameter>"255;255;255"</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::FillColor" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>"200;200;250"</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::FillOpacity" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>=</parameter>
|
||||
<parameter>50</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::Circle" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>Player.X()</parameter>
|
||||
<parameter>Player.Y()</parameter>
|
||||
<parameter>400</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
</actions>
|
||||
<events />
|
||||
</event>
|
||||
<event disabled="false" folded="false">
|
||||
<type>BuiltinCommonInstructions::Standard</type>
|
||||
<conditions>
|
||||
<instruction>
|
||||
<type inverted="true" value="Raycast" />
|
||||
<parameters>
|
||||
<parameter>Colliders</parameter>
|
||||
<parameter>Player.X()</parameter>
|
||||
<parameter>Player.Y()</parameter>
|
||||
<parameter>ToDeg(atan2(MouseY("",0)-Player.Y(), MouseX("",0)-Player.X()))</parameter>
|
||||
<parameter>400</parameter>
|
||||
<parameter>x</parameter>
|
||||
<parameter>y</parameter>
|
||||
<parameter></parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
</conditions>
|
||||
<actions>
|
||||
<instruction>
|
||||
<type inverted="false" value="SetAngle" />
|
||||
<parameters>
|
||||
<parameter>Colliders</parameter>
|
||||
<parameter>+</parameter>
|
||||
<parameter>10 * TimeDelta()</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::FillColor" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>"255;0;0"</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::FillOpacity" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>=</parameter>
|
||||
<parameter>100</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::Line" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>Player.X()</parameter>
|
||||
<parameter>Player.Y()</parameter>
|
||||
<parameter>Variable(x)</parameter>
|
||||
<parameter>Variable(y)</parameter>
|
||||
<parameter>1</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::Circle" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>Variable(x)</parameter>
|
||||
<parameter>Variable(y)</parameter>
|
||||
<parameter>3</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
</actions>
|
||||
<events />
|
||||
</event>
|
||||
<event disabled="false" folded="false">
|
||||
<type>BuiltinCommonInstructions::Standard</type>
|
||||
<conditions>
|
||||
<instruction>
|
||||
<type inverted="false" value="Raycast" />
|
||||
<parameters>
|
||||
<parameter>Colliders</parameter>
|
||||
<parameter>Player.X()</parameter>
|
||||
<parameter>Player.Y()</parameter>
|
||||
<parameter>ToDeg(atan2(MouseY("",0)-Player.Y(), MouseX("",0)-Player.X()))</parameter>
|
||||
<parameter>400</parameter>
|
||||
<parameter>x</parameter>
|
||||
<parameter>y</parameter>
|
||||
<parameter></parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
</conditions>
|
||||
<actions>
|
||||
<instruction>
|
||||
<type inverted="false" value="ChangeColor" />
|
||||
<parameters>
|
||||
<parameter>Colliders</parameter>
|
||||
<parameter>"50;100;50"</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::FillColor" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>"0;255;0"</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::FillOpacity" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>=</parameter>
|
||||
<parameter>100</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::Line" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>Player.X()</parameter>
|
||||
<parameter>Player.Y()</parameter>
|
||||
<parameter>Variable(x)</parameter>
|
||||
<parameter>Variable(y)</parameter>
|
||||
<parameter>1</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
<instruction>
|
||||
<type inverted="false" value="PrimitiveDrawing::Circle" />
|
||||
<parameters>
|
||||
<parameter>Drawer</parameter>
|
||||
<parameter>Variable(x)</parameter>
|
||||
<parameter>Variable(y)</parameter>
|
||||
<parameter>2</parameter>
|
||||
</parameters>
|
||||
<subInstructions />
|
||||
</instruction>
|
||||
</actions>
|
||||
<events />
|
||||
</event>
|
||||
</events>
|
||||
<layers>
|
||||
<layer name="" visibility="true">
|
||||
<cameras>
|
||||
<camera defaultSize="true" defaultViewport="true" height="0.000000" viewportBottom="1.000000" viewportLeft="0.000000" viewportRight="1.000000" viewportTop="0.000000" width="0.000000" />
|
||||
</cameras>
|
||||
<effects />
|
||||
</layer>
|
||||
<layer name="Debug" visibility="true">
|
||||
<cameras>
|
||||
<camera defaultSize="true" defaultViewport="true" height="0.000000" viewportBottom="1.000000" viewportLeft="0.000000" viewportRight="1.000000" viewportTop="0.000000" width="0.000000" />
|
||||
</cameras>
|
||||
<effects />
|
||||
</layer>
|
||||
</layers>
|
||||
<behaviorsSharedData />
|
||||
</layout>
|
||||
</layouts>
|
||||
<externalEvents />
|
||||
<externalLayouts />
|
||||
<externalSourceFiles>
|
||||
<sourceFile filename="C:\Users\Maciel\AppData\Local\Temp/GDTemporaries/GD0x67750b8SourceFile.cpp" gdManaged="true" language="C++" />
|
||||
<sourceFile filename="C:\Users\Maciel\AppData\Local\Temp/GDTemporaries/GD0x6830850SourceFile.cpp" gdManaged="true" language="C++" />
|
||||
</externalSourceFiles>
|
||||
</project>
|
BIN
GDJS/tests/games/Shape1.png
Normal file
BIN
GDJS/tests/games/Shape1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@@ -108,6 +108,8 @@ This new editor is still in development and is missing some features:
|
||||
|
||||
You can contribute by picking anything here or anything that you think is missing or could be improved in GD5! If you don't know how to start, it's a good idea to play a bit with the editor and see if there is something that is unavailable and that you can add or fix.
|
||||
|
||||
See also [the roadmap for ideas and features planned](https://trello.com/b/qf0lM7k8/gdevelop-roadmap).
|
||||
|
||||
## Additional help
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). Check out their documentation for common tasks or help about using it.
|
||||
|
9
newIDE/app/flow-typed/libGD.js
vendored
9
newIDE/app/flow-typed/libGD.js
vendored
@@ -14,6 +14,15 @@ declare type gdSerializerElement = EmscriptenObject;
|
||||
declare type gdInitialInstance = EmscriptenObject;
|
||||
declare type gdBaseEvent = EmscriptenObject;
|
||||
declare type gdResource = EmscriptenObject;
|
||||
declare type gdObject = EmscriptenObject;
|
||||
|
||||
declare type gdInstruction = EmscriptenObject;
|
||||
declare type gdInstructionMetadata = EmscriptenObject;
|
||||
declare type gdInstructionsList = EmscriptenObject;
|
||||
declare type gdParameterMetadata = EmscriptenObject;
|
||||
|
||||
declare type gdVariable = EmscriptenObject;
|
||||
declare type gdVariablesContainer = EmscriptenObject;
|
||||
|
||||
//Represents all objects that have serializeTo and unserializeFrom methods.
|
||||
declare type gdSerializable = EmscriptenObject;
|
||||
|
@@ -18,6 +18,7 @@
|
||||
"axios": "^0.16.1",
|
||||
"blueimp-md5": "^2.10.0",
|
||||
"classnames": "2.2.5",
|
||||
"create-react-context": "^0.1.6",
|
||||
"date-fns": "^1.29.0",
|
||||
"element-closest": "2.0.2",
|
||||
"firebase": "^4.8.2",
|
||||
|
File diff suppressed because one or more lines are too long
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
export const selectableArea = 'selectable';
|
||||
export const selectedArea = 'selected';
|
||||
|
||||
@@ -9,6 +10,7 @@ export const actionsContainer = 'actions-container';
|
||||
export const conditionsContainer = 'conditions-container';
|
||||
export const subInstructionsContainer = 'sub-instructions-container';
|
||||
export const instructionParameter = 'instruction-parameter';
|
||||
export const disabledText = 'disabled-text';
|
||||
|
||||
export const background = 'background';
|
||||
|
||||
|
@@ -1,9 +1,19 @@
|
||||
import React, { Component } from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { mapFor } from '../../Utils/MapFor';
|
||||
import classNames from 'classnames';
|
||||
import { selectedArea, selectableArea, subInstructionsContainer, instructionParameter } from './ClassNames';
|
||||
import InstructionsList from './InstructionsList';
|
||||
import {
|
||||
selectedArea,
|
||||
selectableArea,
|
||||
subInstructionsContainer,
|
||||
instructionParameter,
|
||||
disabledText,
|
||||
} from './ClassNames';
|
||||
import InstructionsList, {
|
||||
type InstructionsListContext,
|
||||
type InstructionContext,
|
||||
} from './InstructionsList';
|
||||
const gd = global.gd;
|
||||
const instrFormatter = gd.InstructionSentenceFormatter.get();
|
||||
instrFormatter.loadTypesFormattingFromConfig();
|
||||
@@ -22,28 +32,42 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class Instruction extends Component {
|
||||
static propTypes = {
|
||||
instruction: PropTypes.object.isRequired,
|
||||
isCondition: PropTypes.bool.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
type Props = {
|
||||
instruction: gdInstruction,
|
||||
isCondition: boolean,
|
||||
onClick: Function,
|
||||
selected: boolean,
|
||||
disabled: boolean,
|
||||
onDoubleClick: () => void,
|
||||
onContextMenu: (x: number, y: number) => void,
|
||||
|
||||
// For potential sub-instructions list:
|
||||
selection: PropTypes.object,
|
||||
onAddNewSubInstruction: PropTypes.func,
|
||||
onSubInstructionClick: PropTypes.func,
|
||||
onSubInstructionDoubleClick: PropTypes.func,
|
||||
onSubInstructionsListContextMenu: PropTypes.func,
|
||||
onSubParameterClick: PropTypes.func,
|
||||
};
|
||||
// For potential sub-instructions list:
|
||||
selection: PropTypes.object,
|
||||
onAddNewSubInstruction: Function,
|
||||
onSubInstructionClick: Function,
|
||||
onSubInstructionDoubleClick: Function,
|
||||
onSubInstructionsListContextMenu: (
|
||||
x: number,
|
||||
y: number,
|
||||
instructionsListContext: InstructionsListContext
|
||||
) => void,
|
||||
onSubParameterClick: Function,
|
||||
onSubInstructionContextMenu: (
|
||||
x: number,
|
||||
y: number,
|
||||
instructionContext: InstructionContext
|
||||
) => void,
|
||||
onParameterClick: (event: any, parameterIndex: number) => void,
|
||||
};
|
||||
|
||||
export default class Instruction extends React.Component<Props, *> {
|
||||
/**
|
||||
* Render the different parts of the text of the instruction.
|
||||
* Parameter can have formatting, be hovered and clicked. The rest
|
||||
* has not particular styling.
|
||||
*/
|
||||
_renderInstructionText = metadata => {
|
||||
const { instruction } = this.props;
|
||||
_renderInstructionText = (metadata: gdInstructionMetadata) => {
|
||||
const { instruction, disabled } = this.props;
|
||||
const formattedTexts = instrFormatter.getAsFormattedText(
|
||||
instruction,
|
||||
metadata
|
||||
@@ -51,7 +75,11 @@ export default class Instruction extends Component {
|
||||
const parametersCount = metadata.getParametersCount();
|
||||
|
||||
return (
|
||||
<span>
|
||||
<span
|
||||
className={classNames({
|
||||
[disabledText]: disabled,
|
||||
})}
|
||||
>
|
||||
{mapFor(0, formattedTexts.size(), i => {
|
||||
const formatting = formattedTexts.getTextFormatting(i);
|
||||
const parameterIndex = formatting.getUserData();
|
||||
@@ -147,6 +175,7 @@ export default class Instruction extends Component {
|
||||
}
|
||||
onParameterClick={this.props.onSubParameterClick}
|
||||
addButtonLabel="Add a sub-condition"
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import Instruction from './Instruction';
|
||||
import { mapFor } from '../../Utils/MapFor';
|
||||
import { isInstructionSelected } from '../SelectionHandler';
|
||||
@@ -11,20 +11,42 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class InstructionsList extends Component {
|
||||
static propTypes = {
|
||||
instrsList: PropTypes.object.isRequired,
|
||||
areConditions: PropTypes.bool.isRequired,
|
||||
onAddNewInstruction: PropTypes.func.isRequired,
|
||||
onInstructionClick: PropTypes.func.isRequired,
|
||||
onInstructionDoubleClick: PropTypes.func.isRequired,
|
||||
onInstructionContextMenu: PropTypes.func.isRequired,
|
||||
onInstructionsListContextMenu: PropTypes.func.isRequired,
|
||||
onParameterClick: PropTypes.func.isRequired,
|
||||
selection: PropTypes.object.isRequired,
|
||||
addButtonLabel: PropTypes.string,
|
||||
};
|
||||
export type InstructionsListContext = {
|
||||
instrsList: gdInstructionsList,
|
||||
isCondition: boolean,
|
||||
};
|
||||
|
||||
export type InstructionContext = InstructionsListContext & {
|
||||
instruction: gdInstruction,
|
||||
indexInList: number,
|
||||
};
|
||||
|
||||
export type ParameterContext = InstructionContext & {
|
||||
parameterIndex: number,
|
||||
domEvent: any,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
instrsList: gdInstructionsList,
|
||||
areConditions: boolean,
|
||||
onAddNewInstruction: InstructionsListContext => void,
|
||||
onInstructionClick: InstructionContext => void,
|
||||
onInstructionDoubleClick: InstructionContext => void,
|
||||
onInstructionContextMenu: (x: number, y: number, InstructionContext) => void,
|
||||
onInstructionsListContextMenu: (
|
||||
x: number,
|
||||
y: number,
|
||||
InstructionsListContext
|
||||
) => void,
|
||||
onParameterClick: ParameterContext => void,
|
||||
selection: any,
|
||||
addButtonLabel?: string,
|
||||
extraClassName?: string,
|
||||
style?: Object,
|
||||
disabled: boolean,
|
||||
};
|
||||
|
||||
export default class InstructionsList extends React.Component<Props, *> {
|
||||
onAddNewInstruction = () => {
|
||||
if (this.props.onAddNewInstruction)
|
||||
this.props.onAddNewInstruction({
|
||||
@@ -47,6 +69,7 @@ export default class InstructionsList extends Component {
|
||||
onParameterClick,
|
||||
selection,
|
||||
style,
|
||||
disabled,
|
||||
} = this.props;
|
||||
|
||||
const instructions = mapFor(0, instrsList.size(), i => {
|
||||
@@ -67,8 +90,7 @@ export default class InstructionsList extends Component {
|
||||
key={instruction.ptr}
|
||||
selected={isInstructionSelected(selection, instruction)}
|
||||
onClick={() => onInstructionClick(instructionContext)}
|
||||
onDoubleClick={() =>
|
||||
onInstructionDoubleClick(instructionContext)}
|
||||
onDoubleClick={() => onInstructionDoubleClick(instructionContext)}
|
||||
onContextMenu={(x, y) =>
|
||||
onInstructionContextMenu(x, y, instructionContext)}
|
||||
onParameterClick={(domEvent, parameterIndex) =>
|
||||
@@ -82,10 +104,9 @@ export default class InstructionsList extends Component {
|
||||
onSubInstructionClick={onInstructionClick}
|
||||
onSubInstructionDoubleClick={onInstructionDoubleClick}
|
||||
onSubInstructionContextMenu={onInstructionContextMenu}
|
||||
onSubInstructionsListContextMenu={
|
||||
onInstructionsListContextMenu
|
||||
}
|
||||
onSubInstructionsListContextMenu={onInstructionsListContextMenu}
|
||||
onSubParameterClick={onParameterClick}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import { type EventRendererProps } from './EventRenderer.flow'
|
||||
import { rgbToHex } from '../../../Utils/ColorTransformer';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
@@ -31,33 +32,46 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class CommentEvent extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
type State = {|
|
||||
editing: boolean,
|
||||
height: number,
|
||||
|};
|
||||
|
||||
export default class CommentEvent extends React.Component<EventRendererProps, State> {
|
||||
state = {
|
||||
editing: false,
|
||||
height: 0,
|
||||
};
|
||||
|
||||
_container: ?any;
|
||||
_input: ?any;
|
||||
|
||||
edit = () => {
|
||||
if (!this._container) return;
|
||||
|
||||
this.setState(
|
||||
{
|
||||
editing: true,
|
||||
height: this._container.offsetHeight,
|
||||
},
|
||||
() => {
|
||||
const input = ReactDOM.findDOMNode(this._input);
|
||||
input.focus();
|
||||
input.value = gd.asCommentEvent(this.props.event).getComment();
|
||||
// $FlowFixMe
|
||||
const input: ?HTMLInputElement = ReactDOM.findDOMNode(this._input);
|
||||
if (input) {
|
||||
input.focus();
|
||||
input.value = gd.asCommentEvent(this.props.event).getComment();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
endEditing = () => {
|
||||
const commentEvent = gd.asCommentEvent(this.props.event);
|
||||
commentEvent.setComment(ReactDOM.findDOMNode(this._input).value);
|
||||
|
||||
// $FlowFixMe
|
||||
const input: ?HTMLInputElement = ReactDOM.findDOMNode(this._input);
|
||||
if (input) commentEvent.setComment(input.value);
|
||||
|
||||
this.setState(
|
||||
{
|
||||
editing: false,
|
||||
|
@@ -0,0 +1,33 @@
|
||||
// @flow
|
||||
import {
|
||||
type InstructionsListContext,
|
||||
type InstructionContext,
|
||||
type ParameterContext,
|
||||
} from '../InstructionsList';
|
||||
|
||||
export type EventRendererProps = {
|
||||
project: gdProject,
|
||||
layout: gdLayout,
|
||||
event: gdBaseEvent,
|
||||
disabled: boolean,
|
||||
|
||||
onUpdate: () => void,
|
||||
selected: boolean,
|
||||
onAddNewInstruction: Function,
|
||||
onInstructionClick: InstructionContext => void,
|
||||
onInstructionDoubleClick: InstructionContext => void,
|
||||
onInstructionContextMenu: (x: number, y: number, InstructionContext) => void,
|
||||
onInstructionsListContextMenu: (
|
||||
x: number,
|
||||
y: number,
|
||||
InstructionsListContext
|
||||
) => void,
|
||||
onParameterClick: ParameterContext => void,
|
||||
selection: any,
|
||||
onUpdate: () => void,
|
||||
|
||||
onOpenLayout: (string) => void,
|
||||
onOpenExternalEvents: (string) => void,
|
||||
|
||||
leftIndentWidth: number,
|
||||
};
|
@@ -1,15 +1,17 @@
|
||||
import React, { Component } from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import InstructionsList from '../InstructionsList.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
selectableArea,
|
||||
executableEventContainer,
|
||||
disabledText,
|
||||
} from '../ClassNames';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import ObjectField from '../../InstructionEditor/ParameterFields/ObjectField';
|
||||
import { type EventRendererProps } from './EventRenderer.flow';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
@@ -25,29 +27,13 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class ForEachEvent extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object.isRequired,
|
||||
onAddNewInstruction: PropTypes.func.isRequired,
|
||||
onInstructionClick: PropTypes.func.isRequired,
|
||||
onInstructionDoubleClick: PropTypes.func.isRequired,
|
||||
onInstructionContextMenu: PropTypes.func.isRequired,
|
||||
onInstructionsListContextMenu: PropTypes.func.isRequired,
|
||||
onParameterClick: PropTypes.func.isRequired,
|
||||
selection: PropTypes.object.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
export default class ForEachEvent extends React.Component<EventRendererProps, *> {
|
||||
state = {
|
||||
editing: false,
|
||||
anchorEl: null,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
editing: false,
|
||||
anchorEl: null,
|
||||
};
|
||||
}
|
||||
|
||||
edit = domEvent => {
|
||||
edit = (domEvent: any) => {
|
||||
this.setState({
|
||||
editing: true,
|
||||
anchorEl: domEvent.currentTarget,
|
||||
@@ -81,6 +67,7 @@ export default class ForEachEvent extends Component {
|
||||
<div
|
||||
className={classNames({
|
||||
[selectableArea]: true,
|
||||
[disabledText]: this.props.disabled,
|
||||
})}
|
||||
onClick={this.edit}
|
||||
>
|
||||
@@ -104,6 +91,7 @@ export default class ForEachEvent extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
<InstructionsList
|
||||
instrsList={forEachEvent.getActions()}
|
||||
@@ -118,6 +106,7 @@ export default class ForEachEvent extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</div>
|
||||
<InlinePopover
|
||||
|
@@ -1,12 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
selectableArea,
|
||||
disabledText,
|
||||
} from '../ClassNames';
|
||||
import { type EventRendererProps } from './EventRenderer.flow';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
@@ -22,18 +24,11 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class GroupEvent extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object.isRequired,
|
||||
export default class GroupEvent extends React.Component<EventRendererProps, *> {
|
||||
state = {
|
||||
editing: false,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
editing: false,
|
||||
};
|
||||
}
|
||||
_textField: ?TextField = null;
|
||||
|
||||
edit = () => {
|
||||
this.setState(
|
||||
@@ -98,6 +93,7 @@ export default class GroupEvent extends Component {
|
||||
<span
|
||||
className={classNames({
|
||||
[selectableArea]: true,
|
||||
[disabledText]: this.props.disabled,
|
||||
})}
|
||||
style={{ ...styles.title, color: textColor }}
|
||||
>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
@@ -7,9 +8,11 @@ import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
selectableArea,
|
||||
disabledText,
|
||||
} from '../ClassNames';
|
||||
import { getHelpLink } from '../../../Utils/HelpLink';
|
||||
import Window from '../../../Utils/Window';
|
||||
import { type EventRendererProps } from './EventRenderer.flow';
|
||||
const gd = global.gd;
|
||||
|
||||
const fontFamily = '"Lucida Console", Monaco, monospace';
|
||||
@@ -54,29 +57,45 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class JsCodeEvent extends Component {
|
||||
export default class JsCodeEvent extends React.Component<
|
||||
EventRendererProps,
|
||||
*
|
||||
> {
|
||||
state = {
|
||||
editing: false,
|
||||
editingObject: false,
|
||||
anchorEl: null,
|
||||
};
|
||||
|
||||
_input: ?any;
|
||||
_container: ?any;
|
||||
|
||||
edit = () => {
|
||||
if (!this._container) return;
|
||||
|
||||
this.setState(
|
||||
{
|
||||
editing: true,
|
||||
height: this._container.offsetHeight,
|
||||
},
|
||||
() => {
|
||||
const input = ReactDOM.findDOMNode(this._input);
|
||||
input.focus();
|
||||
input.value = gd.asJsCodeEvent(this.props.event).getInlineCode();
|
||||
// $FlowFixMe
|
||||
const input: ?HTMLInputElement = ReactDOM.findDOMNode(this._input);
|
||||
if (input) {
|
||||
input.focus();
|
||||
input.value = gd.asJsCodeEvent(this.props.event).getInlineCode();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
endEditing = () => {
|
||||
const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
|
||||
jsCodeEvent.setInlineCode(ReactDOM.findDOMNode(this._input).value);
|
||||
|
||||
// $FlowFixMe
|
||||
const input: ?HTMLInputElement = ReactDOM.findDOMNode(this._input);
|
||||
if (input) jsCodeEvent.setInlineCode(input.value);
|
||||
|
||||
this.setState(
|
||||
{
|
||||
editing: false,
|
||||
@@ -85,7 +104,7 @@ export default class JsCodeEvent extends Component {
|
||||
);
|
||||
};
|
||||
|
||||
editObject = domEvent => {
|
||||
editObject = (domEvent: any) => {
|
||||
this.setState({
|
||||
editingObject: true,
|
||||
anchorEl: domEvent.currentTarget,
|
||||
@@ -153,6 +172,7 @@ export default class JsCodeEvent extends Component {
|
||||
<p
|
||||
className={classNames({
|
||||
[selectableArea]: true,
|
||||
[disabledText]: this.props.disabled,
|
||||
})}
|
||||
onClick={this.edit}
|
||||
key="p"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import OpenInNew from 'material-ui/svg-icons/action/open-in-new';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import classNames from 'classnames';
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import ExternalEventsField from '../../InstructionEditor/ParameterFields/ExternalEventsField';
|
||||
import { showWarningBox } from '../../../UI/Messages/MessageBox';
|
||||
import { type EventRendererProps } from './EventRenderer.flow';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
@@ -25,23 +26,15 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class LinkEvent extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default class LinkEvent extends React.Component<EventRendererProps, *> {
|
||||
_externalEventsField = null;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
state = {
|
||||
editing: false,
|
||||
anchorEl: null,
|
||||
};
|
||||
|
||||
this.state = {
|
||||
editing: false,
|
||||
anchorEl: null,
|
||||
};
|
||||
}
|
||||
|
||||
edit = domEvent => {
|
||||
edit = (domEvent: any) => {
|
||||
this.setState(
|
||||
{
|
||||
editing: true,
|
||||
|
@@ -1,15 +1,17 @@
|
||||
import React, { Component } from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import InstructionsList from '../InstructionsList.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
selectableArea,
|
||||
executableEventContainer,
|
||||
disabledText,
|
||||
} from '../ClassNames';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import DefaultField from '../../InstructionEditor/ParameterFields/DefaultField';
|
||||
import { type EventRendererProps } from './EventRenderer.flow';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
@@ -25,29 +27,13 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class RepeatEvent extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object.isRequired,
|
||||
onAddNewInstruction: PropTypes.func.isRequired,
|
||||
onInstructionClick: PropTypes.func.isRequired,
|
||||
onInstructionDoubleClick: PropTypes.func.isRequired,
|
||||
onInstructionContextMenu: PropTypes.func.isRequired,
|
||||
onInstructionsListContextMenu: PropTypes.func.isRequired,
|
||||
onParameterClick: PropTypes.func.isRequired,
|
||||
selection: PropTypes.object.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
export default class RepeatEvent extends React.Component<EventRendererProps, *> {
|
||||
state = {
|
||||
editing: false,
|
||||
anchorEl: null,
|
||||
};
|
||||
}
|
||||
|
||||
edit = domEvent => {
|
||||
edit = (domEvent: any) => {
|
||||
this.setState({
|
||||
editing: true,
|
||||
anchorEl: domEvent.currentTarget,
|
||||
@@ -81,6 +67,7 @@ export default class RepeatEvent extends Component {
|
||||
<div
|
||||
className={classNames({
|
||||
[selectableArea]: true,
|
||||
[disabledText]: this.props.disabled,
|
||||
})}
|
||||
onClick={this.edit}
|
||||
>
|
||||
@@ -104,6 +91,7 @@ export default class RepeatEvent extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
<InstructionsList
|
||||
instrsList={repeatEvent.getActions()}
|
||||
@@ -118,6 +106,7 @@ export default class RepeatEvent extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</div>
|
||||
<InlinePopover
|
||||
|
@@ -1,12 +1,13 @@
|
||||
import React, { Component } from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
executableEventContainer,
|
||||
} from '../ClassNames';
|
||||
import { type EventRendererProps } from './EventRenderer.flow';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
@@ -18,18 +19,10 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class StandardEvent extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object.isRequired,
|
||||
onAddNewInstruction: PropTypes.func.isRequired,
|
||||
onInstructionClick: PropTypes.func.isRequired,
|
||||
onInstructionDoubleClick: PropTypes.func.isRequired,
|
||||
onInstructionContextMenu: PropTypes.func.isRequired,
|
||||
onInstructionsListContextMenu: PropTypes.func.isRequired,
|
||||
onParameterClick: PropTypes.func.isRequired,
|
||||
selection: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default class StandardEvent extends React.Component<
|
||||
EventRendererProps,
|
||||
*
|
||||
> {
|
||||
render() {
|
||||
var standardEvent = gd.asStandardEvent(this.props.event);
|
||||
|
||||
@@ -59,6 +52,7 @@ export default class StandardEvent extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
<InstructionsList
|
||||
instrsList={standardEvent.getActions()}
|
||||
@@ -73,6 +67,7 @@ export default class StandardEvent extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@@ -1,13 +1,10 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { largeSelectedArea, largeSelectableArea } from '../ClassNames';
|
||||
import { type EventRendererProps } from './EventRenderer.flow';
|
||||
|
||||
export default class UnknownEvent extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default class UnknownEvent extends React.Component<EventRendererProps, *> {
|
||||
render() {
|
||||
return (
|
||||
<p
|
||||
|
@@ -1,12 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
executableEventContainer,
|
||||
disabledText,
|
||||
} from '../ClassNames';
|
||||
import { type EventRendererProps } from './EventRenderer.flow';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
@@ -22,19 +24,10 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class ForEachEvent extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object.isRequired,
|
||||
onAddNewInstruction: PropTypes.func.isRequired,
|
||||
onInstructionClick: PropTypes.func.isRequired,
|
||||
onInstructionDoubleClick: PropTypes.func.isRequired,
|
||||
onInstructionContextMenu: PropTypes.func.isRequired,
|
||||
onInstructionsListContextMenu: PropTypes.func.isRequired,
|
||||
onParameterClick: PropTypes.func.isRequired,
|
||||
selection: PropTypes.object.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default class ForEachEvent extends React.Component<
|
||||
EventRendererProps,
|
||||
*
|
||||
> {
|
||||
render() {
|
||||
var whileEvent = gd.asWhileEvent(this.props.event);
|
||||
|
||||
@@ -51,7 +44,13 @@ export default class ForEachEvent extends Component {
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<div>While these conditions are true:</div>
|
||||
<div
|
||||
className={classNames({
|
||||
[disabledText]: this.props.disabled,
|
||||
})}
|
||||
>
|
||||
While these conditions are true:
|
||||
</div>
|
||||
<InstructionsList
|
||||
instrsList={whileEvent.getWhileConditions()}
|
||||
selection={this.props.selection}
|
||||
@@ -60,9 +59,19 @@ export default class ForEachEvent extends Component {
|
||||
onInstructionClick={this.props.onInstructionClick}
|
||||
onInstructionDoubleClick={this.props.onInstructionDoubleClick}
|
||||
onInstructionContextMenu={this.props.onInstructionContextMenu}
|
||||
onInstructionsListContextMenu={
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
<div>Repeat these:</div>
|
||||
<div
|
||||
className={classNames({
|
||||
[disabledText]: this.props.disabled,
|
||||
})}
|
||||
>
|
||||
Repeat these:
|
||||
</div>
|
||||
<div style={styles.instructionsContainer}>
|
||||
<InstructionsList
|
||||
instrsList={whileEvent.getConditions()}
|
||||
@@ -77,6 +86,7 @@ export default class ForEachEvent extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
<InstructionsList
|
||||
instrsList={whileEvent.getActions()}
|
||||
@@ -91,6 +101,7 @@ export default class ForEachEvent extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -40,7 +40,7 @@ class EventContainer extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { event, project, layout } = this.props;
|
||||
const { event, project, layout, disabled } = this.props;
|
||||
const EventComponent = EventsRenderingService.getEventComponent(event);
|
||||
|
||||
return (
|
||||
@@ -68,6 +68,9 @@ class EventContainer extends Component {
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
onOpenExternalEvents={this.props.onOpenExternalEvents}
|
||||
onOpenLayout={this.props.onOpenLayout}
|
||||
disabled={
|
||||
disabled /* Use disabled (not event.disabled) as it is true if a parent event is disabled*/
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -140,17 +143,25 @@ export default class ThemableEventsTree extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
_eventsToTreeData = (eventsList, flatData = [], depth = 0) => {
|
||||
_eventsToTreeData = (
|
||||
eventsList,
|
||||
flatData = [],
|
||||
depth = 0,
|
||||
parentDisabled = false
|
||||
) => {
|
||||
const treeData = mapFor(0, eventsList.getEventsCount(), i => {
|
||||
const event = eventsList.getEventAt(i);
|
||||
flatData.push(event);
|
||||
|
||||
const disabled = parentDisabled || event.isDisabled();
|
||||
|
||||
return {
|
||||
title: this._renderEvent,
|
||||
event,
|
||||
eventsList,
|
||||
indexInList: i,
|
||||
expanded: !event.isFolded(),
|
||||
disabled,
|
||||
depth,
|
||||
key: event.ptr, //TODO: useless?
|
||||
children: this._eventsToTreeData(
|
||||
@@ -158,7 +169,8 @@ export default class ThemableEventsTree extends Component {
|
||||
// flatData is a flat representation of events, one for each line.
|
||||
// Hence it should not contain the folded events.
|
||||
!event.isFolded() ? flatData : [],
|
||||
depth + 1
|
||||
depth + 1,
|
||||
disabled
|
||||
).treeData,
|
||||
};
|
||||
});
|
||||
@@ -212,7 +224,7 @@ export default class ThemableEventsTree extends Component {
|
||||
};
|
||||
|
||||
_renderEvent = ({ node }) => {
|
||||
const { event, depth } = node;
|
||||
const { event, depth, disabled } = node;
|
||||
|
||||
return (
|
||||
<EventContainer
|
||||
@@ -233,6 +245,9 @@ export default class ThemableEventsTree extends Component {
|
||||
onInstructionsListContextMenu={this.props.onInstructionsListContextMenu}
|
||||
onOpenExternalEvents={this.props.onOpenExternalEvents}
|
||||
onOpenLayout={this.props.onOpenLayout}
|
||||
disabled={
|
||||
disabled /* Use node.disabled (not event.disabled) as it is true if a parent event is disabled*/
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@@ -125,3 +125,11 @@
|
||||
box-sizing: border-box;
|
||||
border: 1px transparent solid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disabled text
|
||||
*/
|
||||
.gd-events-sheet .disabled-text {
|
||||
text-decoration: line-through;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
@@ -138,6 +138,11 @@ Object {
|
||||
"fullGroupName": "Common conditions for all objects/Collision",
|
||||
"type": "CollisionPoint",
|
||||
},
|
||||
"Raycast": Object {
|
||||
"displayedName": "Raycast",
|
||||
"fullGroupName": "Common conditions for all objects/Collision",
|
||||
"type": "Raycast",
|
||||
},
|
||||
},
|
||||
"Layer": Object {
|
||||
"Compare layer": Object {
|
||||
@@ -2073,6 +2078,11 @@ Array [
|
||||
"fullGroupName": "Common conditions for all objects/Objects",
|
||||
"type": "PickNearest",
|
||||
},
|
||||
Object {
|
||||
"displayedName": "Raycast",
|
||||
"fullGroupName": "Common conditions for all objects/Collision",
|
||||
"type": "Raycast",
|
||||
},
|
||||
Object {
|
||||
"displayedName": "An object is moving toward another",
|
||||
"fullGroupName": "Common conditions for all objects/Movement",
|
||||
|
@@ -87,11 +87,20 @@ export default class InstructionParametersEditor extends Component {
|
||||
<div key={type} style={styles.parametersContainer}>
|
||||
{mapFor(0, instructionMetadata.getParametersCount(), i => {
|
||||
const parameterMetadata = instructionMetadata.getParameter(i);
|
||||
const parameterMetadataType = parameterMetadata.getType();
|
||||
const ParameterComponent = ParameterRenderingService.getParameterComponent(
|
||||
parameterMetadata.getType()
|
||||
parameterMetadataType
|
||||
);
|
||||
|
||||
if (parameterMetadata.isCodeOnly()) return null;
|
||||
if (!ParameterComponent) {
|
||||
console.warn(
|
||||
'Missing parameter component for',
|
||||
parameterMetadataType
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ParameterComponent
|
||||
parameterMetadata={parameterMetadata}
|
||||
|
@@ -13,11 +13,11 @@ const fuzzyFilterOrEmpty = (searchText, key) => {
|
||||
};
|
||||
|
||||
export default class BehaviorField extends Component {
|
||||
state = { errorText: null, focused: false, text: null };
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { errorText: null };
|
||||
|
||||
const { parameterMetadata } = this.props;
|
||||
this._description = parameterMetadata
|
||||
? parameterMetadata.getDescription()
|
||||
@@ -61,8 +61,8 @@ export default class BehaviorField extends Component {
|
||||
if (this._field) this._field.focus();
|
||||
}
|
||||
|
||||
_getError = () => {
|
||||
if (!this.props.value) return null;
|
||||
_getError = (value?: string) => {
|
||||
if (!value && !this.props.value) return null;
|
||||
|
||||
const isValidChoice =
|
||||
this._behaviorNames.filter(choice => this.props.value === choice)
|
||||
@@ -73,8 +73,8 @@ export default class BehaviorField extends Component {
|
||||
return null;
|
||||
};
|
||||
|
||||
_doValidation = () => {
|
||||
this.setState({ errorText: this._getError() });
|
||||
_doValidation = (value?: string) => {
|
||||
this.setState({ errorText: this._getError(value) });
|
||||
};
|
||||
|
||||
componentDidUpdate() {
|
||||
@@ -115,12 +115,27 @@ export default class BehaviorField extends Component {
|
||||
? noBehaviorErrorText
|
||||
: this.state.errorText
|
||||
}
|
||||
searchText={this.props.value}
|
||||
onUpdateInput={value => {
|
||||
this.setState({ errorText: null });
|
||||
this.props.onChange(value);
|
||||
searchText={this.state.focused ? this.state.text : this.props.value}
|
||||
onFocus={() => {
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: this.props.value,
|
||||
});
|
||||
}}
|
||||
onUpdateInput={value => {
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: value,
|
||||
});
|
||||
}}
|
||||
onBlur={event => {
|
||||
this.props.onChange(event.target.value);
|
||||
this.setState({
|
||||
focused: false,
|
||||
text: null,
|
||||
});
|
||||
this._doValidation(event.target.value);
|
||||
}}
|
||||
onBlur={this._doValidation}
|
||||
onNewRequest={data => {
|
||||
// Note that data may be a string or a {text, value} object.
|
||||
if (typeof data === 'string') {
|
||||
|
@@ -1,7 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import TextField from 'material-ui/TextField';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import SemiControlledTextField from '../../../UI/SemiControlledTextField';
|
||||
import { type ParameterFieldProps } from './ParameterFieldProps.flow';
|
||||
|
||||
export default class DefaultField extends React.Component<
|
||||
ParameterFieldProps,
|
||||
void
|
||||
> {
|
||||
_field: ?any = null;
|
||||
|
||||
export default class DefaultField extends Component {
|
||||
focus() {
|
||||
if (this._field) this._field.focus();
|
||||
}
|
||||
@@ -13,10 +20,11 @@ export default class DefaultField extends Component {
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<TextField
|
||||
<SemiControlledTextField
|
||||
commitOnBlur
|
||||
value={this.props.value}
|
||||
floatingLabelText={description}
|
||||
onChange={(e, text) => this.props.onChange(text)}
|
||||
onChange={(text: string) => this.props.onChange(text)}
|
||||
ref={field => (this._field = field)}
|
||||
fullWidth
|
||||
/>
|
||||
|
@@ -0,0 +1,46 @@
|
||||
// @flow
|
||||
import flatten from 'lodash/flatten';
|
||||
import { mapFor } from '../../../Utils/MapFor';
|
||||
|
||||
export const enumerateVariables = (
|
||||
variablesContainer: ?gdVariablesContainer
|
||||
) => {
|
||||
if (!variablesContainer) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const enumerateVariableAndChildrenNames = (
|
||||
fullName: string,
|
||||
variable: gdVariable
|
||||
): Array<string> => {
|
||||
const names = [fullName];
|
||||
if (!variable.isStructure()) return names;
|
||||
|
||||
variable
|
||||
.getAllChildren()
|
||||
.keys()
|
||||
.toJSArray()
|
||||
.forEach(childName => {
|
||||
enumerateVariableAndChildrenNames(
|
||||
`${fullName}.${childName}`,
|
||||
variable.getChild(childName)
|
||||
).forEach(name => {
|
||||
names.push(name);
|
||||
});
|
||||
});
|
||||
|
||||
return names;
|
||||
};
|
||||
|
||||
return flatten(
|
||||
mapFor(0, variablesContainer.count(), i => {
|
||||
if (!variablesContainer) return [];
|
||||
|
||||
const variableAndName = variablesContainer.getAt(i);
|
||||
return enumerateVariableAndChildrenNames(
|
||||
variableAndName.getName(),
|
||||
variableAndName.getVariable()
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
@@ -0,0 +1,31 @@
|
||||
// @flow
|
||||
import { enumerateVariables } from './EnumerateVariables';
|
||||
const gd = global.gd;
|
||||
|
||||
describe('EnumerateObjects', () => {
|
||||
it('can enumerate variables, including children', () => {
|
||||
const container = new gd.VariablesContainer();
|
||||
container
|
||||
.insert('Variable1', new gd.Variable(), 0)
|
||||
.setString('A multiline\nstr value');
|
||||
container.insert('Variable2', new gd.Variable(), 1).setString('123456');
|
||||
const variable3 = new gd.Variable();
|
||||
variable3.getChild('Child1').setString('Child1 str value');
|
||||
variable3.getChild('Child2').setString('7891011');
|
||||
variable3
|
||||
.getChild('Child3')
|
||||
.getChild('SubChild1')
|
||||
.setString('Hello\nMultiline\nWorld');
|
||||
container.insert('Variable3', variable3, 2);
|
||||
|
||||
const allNames = enumerateVariables(container);
|
||||
expect(allNames).toHaveLength(7);
|
||||
expect(allNames).toContain('Variable1');
|
||||
expect(allNames).toContain('Variable2');
|
||||
expect(allNames).toContain('Variable3');
|
||||
expect(allNames).toContain('Variable3.Child1');
|
||||
expect(allNames).toContain('Variable3.Child2');
|
||||
expect(allNames).toContain('Variable3.Child3');
|
||||
expect(allNames).toContain('Variable3.Child3.SubChild1');
|
||||
});
|
||||
});
|
@@ -1,12 +1,25 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import GenericExpressionField from './GenericExpressionField';
|
||||
import { type ParameterFieldProps } from './ParameterFieldProps.flow';
|
||||
|
||||
export default class ExpressionField extends Component<
|
||||
ParameterFieldProps,
|
||||
void
|
||||
> {
|
||||
_field: ?GenericExpressionField;
|
||||
|
||||
export default class ExpressionField extends Component {
|
||||
focus() {
|
||||
if (this._field) this._field.focus();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <GenericExpressionField expressionType="number" {...this.props} />;
|
||||
return (
|
||||
<GenericExpressionField
|
||||
expressionType="number"
|
||||
ref={field => (this._field = field)}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import AutoComplete from 'material-ui/AutoComplete';
|
||||
import Divider from 'material-ui/Divider';
|
||||
@@ -5,6 +6,7 @@ import {
|
||||
enumerateLayouts,
|
||||
enumerateExternalEvents,
|
||||
} from '../../../ProjectManager/EnumerateProjectItems';
|
||||
import { type ParameterFieldProps } from './ParameterFieldProps.flow';
|
||||
|
||||
const styles = {
|
||||
autoCompleteTextField: {
|
||||
@@ -16,8 +18,22 @@ const fuzzyFilterOrEmpty = (searchText, key) => {
|
||||
return !key || AutoComplete.fuzzyFilter(searchText, key);
|
||||
};
|
||||
|
||||
export default class ExternalEventsField extends Component {
|
||||
constructor(props) {
|
||||
type State = {|
|
||||
focused: boolean,
|
||||
text: ?string,
|
||||
|};
|
||||
|
||||
export default class ExternalEventsField extends Component<
|
||||
ParameterFieldProps,
|
||||
State
|
||||
> {
|
||||
state = { focused: false, text: null };
|
||||
|
||||
_description: ?string = undefined;
|
||||
_fullList: Array<{ text: string, value: string }> = [];
|
||||
_field: ?any;
|
||||
|
||||
constructor(props: ParameterFieldProps) {
|
||||
super(props);
|
||||
|
||||
const { parameterMetadata } = this.props;
|
||||
@@ -32,15 +48,14 @@ export default class ExternalEventsField extends Component {
|
||||
if (this._field) this._field.focus();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
componentWillReceiveProps(newProps: ParameterFieldProps) {
|
||||
if (newProps.project !== this.props.project) {
|
||||
this._loadNamesFrom(newProps);
|
||||
}
|
||||
}
|
||||
|
||||
_loadNamesFrom(props) {
|
||||
_loadNamesFrom(props: ParameterFieldProps) {
|
||||
if (!props.project) {
|
||||
this._externalEventsNames = [];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,9 +86,25 @@ export default class ExternalEventsField extends Component {
|
||||
menuProps={{
|
||||
maxHeight: 250,
|
||||
}}
|
||||
searchText={this.props.value}
|
||||
searchText={this.state.focused ? this.state.text : this.props.value}
|
||||
onFocus={() => {
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: this.props.value,
|
||||
});
|
||||
}}
|
||||
onUpdateInput={value => {
|
||||
this.props.onChange(value);
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: value,
|
||||
});
|
||||
}}
|
||||
onBlur={event => {
|
||||
this.props.onChange(event.target.value);
|
||||
this.setState({
|
||||
focused: false,
|
||||
text: null,
|
||||
});
|
||||
}}
|
||||
onNewRequest={data => {
|
||||
// Note that data may be a string or a {text, value} object.
|
||||
|
@@ -1,12 +1,14 @@
|
||||
// @flow weak
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import Popover, { PopoverAnimationVertical } from 'material-ui/Popover';
|
||||
import Functions from 'material-ui/svg-icons/editor/functions';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import SemiControlledTextField from '../../../../UI/SemiControlledTextField';
|
||||
import ExpressionSelector from '../../InstructionOrExpressionSelector/ExpressionSelector';
|
||||
import ExpressionParametersEditorDialog from './ExpressionParametersEditorDialog';
|
||||
import ExpressionParametersEditorDialog, {
|
||||
type ParameterValues,
|
||||
} from './ExpressionParametersEditorDialog';
|
||||
import { formatExpressionCall } from './FormatExpressionCall';
|
||||
import { type InstructionOrExpressionInformation } from '../../InstructionOrExpressionSelector/InstructionOrExpressionInformation.flow.js';
|
||||
const gd = global.gd;
|
||||
@@ -44,8 +46,8 @@ type State = {|
|
||||
|};
|
||||
|
||||
export default class ExpressionField extends Component<*, State> {
|
||||
_field = null;
|
||||
_fieldElement = null;
|
||||
_field: ?any = null;
|
||||
_fieldElement: ?any = null;
|
||||
_inputElement = null;
|
||||
|
||||
state = {
|
||||
@@ -58,7 +60,7 @@ export default class ExpressionField extends Component<*, State> {
|
||||
componentDidMount() {
|
||||
if (this._field) {
|
||||
this._fieldElement = ReactDOM.findDOMNode(this._field);
|
||||
this._inputElement = this._field.getInputNode();
|
||||
this._inputElement = this._field ? this._field.getInputNode() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,34 +74,32 @@ export default class ExpressionField extends Component<*, State> {
|
||||
});
|
||||
};
|
||||
|
||||
_handleFocus = event => {
|
||||
_handleFocus = (event: any) => {
|
||||
// This prevents ghost click.
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
_handleBlur = () => {
|
||||
this._doValidation();
|
||||
this.setState({
|
||||
popoverOpen: false,
|
||||
});
|
||||
};
|
||||
|
||||
_handleRequestClose = () => {
|
||||
this.setState({
|
||||
popoverOpen: false,
|
||||
});
|
||||
};
|
||||
|
||||
_handleChange = (e, text) => {
|
||||
if (this.props.onChange) this.props.onChange(text);
|
||||
_handleChange = (value: string) => {
|
||||
if (this.props.onChange) this.props.onChange(value);
|
||||
|
||||
this._doValidation(value);
|
||||
this.setState({
|
||||
popoverOpen: false,
|
||||
});
|
||||
};
|
||||
|
||||
_handleMenuMouseDown = event => {
|
||||
_handleMenuMouseDown = (event: any) => {
|
||||
// Keep the TextField focused
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
_handleExpressionChosen = expressionInfo => {
|
||||
_handleExpressionChosen = (expressionInfo: InstructionOrExpressionInformation) => {
|
||||
this.setState({
|
||||
popoverOpen: false,
|
||||
parametersDialogOpen: true,
|
||||
@@ -107,7 +107,10 @@ export default class ExpressionField extends Component<*, State> {
|
||||
});
|
||||
};
|
||||
|
||||
insertExpression = (expressionInfo, parameterValues) => {
|
||||
insertExpression = (
|
||||
expressionInfo: InstructionOrExpressionInformation,
|
||||
parameterValues: ParameterValues
|
||||
) => {
|
||||
if (!this._inputElement) return;
|
||||
const cursorPosition = this._inputElement.selectionStart;
|
||||
|
||||
@@ -133,14 +136,16 @@ export default class ExpressionField extends Component<*, State> {
|
||||
}, 5);
|
||||
};
|
||||
|
||||
_getError = () => {
|
||||
_getError = (value?: string) => {
|
||||
const { project, layout, expressionType } = this.props;
|
||||
|
||||
const callbacks = new gd.CallbacksForExpressionCorrectnessTesting(
|
||||
project,
|
||||
layout
|
||||
);
|
||||
const parser = new gd.ExpressionParser(this.props.value);
|
||||
const parser = new gd.ExpressionParser(
|
||||
value === undefined ? this.props.value : value
|
||||
);
|
||||
|
||||
const parseFunction =
|
||||
expressionType === 'string'
|
||||
@@ -161,8 +166,8 @@ export default class ExpressionField extends Component<*, State> {
|
||||
return isValid ? null : error;
|
||||
};
|
||||
|
||||
_doValidation = () => {
|
||||
this.setState({ errorText: this._getError() });
|
||||
_doValidation = (value?: string) => {
|
||||
this.setState({ errorText: this._getError(value) });
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -185,14 +190,14 @@ export default class ExpressionField extends Component<*, State> {
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<div style={styles.textFieldContainer}>
|
||||
<TextField
|
||||
<SemiControlledTextField
|
||||
commitOnBlur
|
||||
value={value}
|
||||
floatingLabelText={description}
|
||||
inputStyle={styles.input}
|
||||
onChange={this._handleChange}
|
||||
ref={field => (this._field = field)}
|
||||
onFocus={this._handleFocus}
|
||||
onBlur={this._handleBlur}
|
||||
errorText={this.state.errorText}
|
||||
fullWidth
|
||||
/>
|
||||
|
@@ -30,7 +30,10 @@ export default class GlobalVariableField extends Component {
|
||||
open={this.state.editorOpen}
|
||||
variablesContainer={project.getVariables()}
|
||||
onCancel={() => this.setState({ editorOpen: false })}
|
||||
onApply={() => this.setState({ editorOpen: false })}
|
||||
onApply={() => {
|
||||
this.setState({ editorOpen: false });
|
||||
if (this._field) this._field.forceUpdateVariables();
|
||||
}}
|
||||
emptyExplanationMessage="Global variables are variables that are persisted across the scenes during the game."
|
||||
/>
|
||||
)}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import AutoComplete from 'material-ui/AutoComplete';
|
||||
import { type ParameterFieldProps } from './ParameterFieldProps.flow';
|
||||
|
||||
const styles = {
|
||||
autoCompleteTextField: {
|
||||
@@ -11,8 +13,19 @@ const fuzzyFilterOrEmpty = (searchText, key) => {
|
||||
return !key || AutoComplete.fuzzyFilter(searchText, key);
|
||||
};
|
||||
|
||||
export default class KeyField extends Component {
|
||||
constructor(props) {
|
||||
type State = {|
|
||||
focused: boolean,
|
||||
text: ?string,
|
||||
|};
|
||||
|
||||
export default class KeyField extends Component<ParameterFieldProps, State> {
|
||||
state = { focused: false, text: null };
|
||||
|
||||
_description: ?string;
|
||||
_field: ?any;
|
||||
_keyNames: Array<string> = [];
|
||||
|
||||
constructor(props: ParameterFieldProps) {
|
||||
super(props);
|
||||
|
||||
const { parameterMetadata } = this.props;
|
||||
@@ -124,9 +137,25 @@ export default class KeyField extends Component {
|
||||
menuProps={{
|
||||
maxHeight: 250,
|
||||
}}
|
||||
searchText={this.props.value}
|
||||
searchText={this.state.focused ? this.state.text : this.props.value}
|
||||
onFocus={() => {
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: this.props.value,
|
||||
});
|
||||
}}
|
||||
onUpdateInput={value => {
|
||||
this.props.onChange(value);
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: value,
|
||||
});
|
||||
}}
|
||||
onBlur={event => {
|
||||
this.props.onChange(event.target.value);
|
||||
this.setState({
|
||||
focused: false,
|
||||
text: null,
|
||||
});
|
||||
}}
|
||||
onNewRequest={data => {
|
||||
// Note that data may be a string or a {text, value} object.
|
||||
|
@@ -1,6 +1,8 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import AutoComplete from 'material-ui/AutoComplete';
|
||||
import { mapFor } from '../../../Utils/MapFor';
|
||||
import { type ParameterFieldProps } from './ParameterFieldProps.flow';
|
||||
|
||||
const styles = {
|
||||
autoCompleteTextField: {
|
||||
@@ -12,8 +14,19 @@ const fuzzyFilterOrEmpty = (searchText, key) => {
|
||||
return !key || AutoComplete.fuzzyFilter(searchText, key);
|
||||
};
|
||||
|
||||
export default class LayerField extends Component {
|
||||
constructor(props) {
|
||||
type State = {|
|
||||
focused: boolean,
|
||||
text: ?string,
|
||||
|};
|
||||
|
||||
export default class LayerField extends Component<ParameterFieldProps, State> {
|
||||
state = { focused: false, text: null };
|
||||
|
||||
_description: ?string;
|
||||
_field: ?any;
|
||||
_layersNames: Array<string> = [];
|
||||
|
||||
constructor(props: ParameterFieldProps) {
|
||||
super(props);
|
||||
|
||||
const { parameterMetadata } = this.props;
|
||||
@@ -28,20 +41,21 @@ export default class LayerField extends Component {
|
||||
if (this._field) this._field.focus();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
componentWillReceiveProps(newProps: ParameterFieldProps) {
|
||||
if (newProps.layout !== this.props.layout) {
|
||||
this._loadNamesFrom(newProps);
|
||||
}
|
||||
}
|
||||
|
||||
_loadNamesFrom(props) {
|
||||
if (!props.layout) {
|
||||
_loadNamesFrom(props: ParameterFieldProps) {
|
||||
const layout = props.layout;
|
||||
if (!layout) {
|
||||
this._layersNames = [];
|
||||
return;
|
||||
}
|
||||
|
||||
this._layersNames = mapFor(0, props.layout.getLayersCount(), i => {
|
||||
const layer = props.layout.getLayerAt(i);
|
||||
this._layersNames = mapFor(0, layout.getLayersCount(), i => {
|
||||
const layer = layout.getLayerAt(i);
|
||||
return layer.getName();
|
||||
});
|
||||
}
|
||||
@@ -55,9 +69,25 @@ export default class LayerField extends Component {
|
||||
menuProps={{
|
||||
maxHeight: 250,
|
||||
}}
|
||||
searchText={this.props.value}
|
||||
searchText={this.state.focused ? this.state.text : this.props.value}
|
||||
onFocus={() => {
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: this.props.value,
|
||||
});
|
||||
}}
|
||||
onUpdateInput={value => {
|
||||
this.props.onChange(value);
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: value,
|
||||
});
|
||||
}}
|
||||
onBlur={event => {
|
||||
this.props.onChange(event.target.value);
|
||||
this.setState({
|
||||
focused: false,
|
||||
text: null,
|
||||
});
|
||||
}}
|
||||
onNewRequest={data => {
|
||||
// Note that data may be a string or a {text, value} object.
|
||||
|
@@ -43,7 +43,10 @@ export default class ObjectVariableField extends Component {
|
||||
open={this.state.editorOpen}
|
||||
variablesContainer={variablesContainer}
|
||||
onCancel={() => this.setState({ editorOpen: false })}
|
||||
onApply={() => this.setState({ editorOpen: false })}
|
||||
onApply={() => {
|
||||
this.setState({ editorOpen: false });
|
||||
if (this._field) this._field.forceUpdateVariables();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -0,0 +1,13 @@
|
||||
// @flow
|
||||
export type ParameterFieldProps = {|
|
||||
parameterMetadata?: gdParameterMetadata,
|
||||
project: gdProject,
|
||||
layout?: gdLayout,
|
||||
onChange: string => void,
|
||||
value: string,
|
||||
isInline?: boolean,
|
||||
parameterRenderingService?: {
|
||||
components: any,
|
||||
getParameterComponent: (type: string) => any,
|
||||
},
|
||||
|};
|
@@ -30,7 +30,10 @@ export default class SceneVariableField extends Component {
|
||||
open={this.state.editorOpen}
|
||||
variablesContainer={layout.getVariables()}
|
||||
onCancel={() => this.setState({ editorOpen: false })}
|
||||
onApply={() => this.setState({ editorOpen: false })}
|
||||
onApply={() => {
|
||||
this.setState({ editorOpen: false });
|
||||
if (this._field) this._field.forceUpdateVariables();
|
||||
}}
|
||||
emptyExplanationMessage="Scene variables can be used to store any value or text during the game."
|
||||
emptyExplanationSecondMessage="For example, you can have a variable called Score representing the current score of the player."
|
||||
/>
|
||||
|
@@ -1,12 +1,22 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import GenericExpressionField from './GenericExpressionField';
|
||||
import { type ParameterFieldProps } from './ParameterFieldProps.flow';
|
||||
|
||||
export default class StringField extends Component<ParameterFieldProps, void> {
|
||||
_field: ?GenericExpressionField;
|
||||
|
||||
export default class StringField extends Component {
|
||||
focus() {
|
||||
if (this._field) this._field.focus();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <GenericExpressionField expressionType="string" {...this.props} />;
|
||||
return (
|
||||
<GenericExpressionField
|
||||
expressionType="string"
|
||||
ref={field => (this._field = field)}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import AutoComplete from 'material-ui/AutoComplete';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import { mapFor } from '../../../Utils/MapFor';
|
||||
import { enumerateVariables } from './EnumerateVariables';
|
||||
import { type ParameterFieldProps } from './ParameterFieldProps.flow';
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
@@ -20,11 +22,25 @@ const fuzzyFilterOrEmpty = (searchText, key) => {
|
||||
return !key || AutoComplete.fuzzyFilter(searchText, key);
|
||||
};
|
||||
|
||||
export default class VariableField extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
type Props = ParameterFieldProps & {
|
||||
variablesContainer: ?gdVariablesContainer,
|
||||
onOpenDialog: () => void,
|
||||
};
|
||||
|
||||
this.state = { errorText: null };
|
||||
type State = {|
|
||||
focused: boolean,
|
||||
text: ?string,
|
||||
|};
|
||||
|
||||
export default class VariableField extends Component<Props, State> {
|
||||
state = { focused: false, text: null };
|
||||
|
||||
_description: ?string;
|
||||
_field: ?AutoComplete;
|
||||
_variableNames: Array<string> = [];
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
const { parameterMetadata } = this.props;
|
||||
this._description = parameterMetadata
|
||||
@@ -34,26 +50,22 @@ export default class VariableField extends Component {
|
||||
this._loadNamesFrom(props);
|
||||
}
|
||||
|
||||
forceUpdateVariables() {
|
||||
this._loadNamesFrom(this.props);
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this._field) this._field.focus();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
componentWillReceiveProps(newProps: Props) {
|
||||
if (newProps.variablesContainer !== this.props.variablesContainer) {
|
||||
this._loadNamesFrom(newProps);
|
||||
}
|
||||
}
|
||||
|
||||
_loadNamesFrom(props) {
|
||||
if (!props.variablesContainer) {
|
||||
this._variableNames = [];
|
||||
return;
|
||||
}
|
||||
|
||||
this._variableNames = mapFor(0, props.variablesContainer.count(), i => {
|
||||
const variableAndName = props.variablesContainer.getAt(i);
|
||||
return variableAndName.getName();
|
||||
});
|
||||
_loadNamesFrom(props: Props) {
|
||||
this._variableNames = enumerateVariables(props.variablesContainer);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -66,10 +78,25 @@ export default class VariableField extends Component {
|
||||
menuProps={{
|
||||
maxHeight: 250,
|
||||
}}
|
||||
searchText={this.props.value}
|
||||
searchText={this.state.focused ? this.state.text : this.props.value}
|
||||
onFocus={() => {
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: this.props.value,
|
||||
});
|
||||
}}
|
||||
onUpdateInput={value => {
|
||||
this.setState({ errorText: null });
|
||||
this.props.onChange(value);
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: value,
|
||||
});
|
||||
}}
|
||||
onBlur={event => {
|
||||
this.props.onChange(event.target.value);
|
||||
this.setState({
|
||||
focused: false,
|
||||
text: null,
|
||||
});
|
||||
}}
|
||||
onNewRequest={data => {
|
||||
// Note that data may be a string or a {text, value} object.
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
import DefaultField from './ParameterFields/DefaultField';
|
||||
import RelationalOperatorField from './ParameterFields/RelationalOperatorField';
|
||||
import OperatorField from './ParameterFields/OperatorField';
|
||||
@@ -15,35 +16,37 @@ import ObjectVariableField from './ParameterFields/ObjectVariableField';
|
||||
import LayerField from './ParameterFields/LayerField';
|
||||
const gd = global.gd;
|
||||
|
||||
const components = {
|
||||
default: DefaultField,
|
||||
mouse: MouseField,
|
||||
object: ObjectField,
|
||||
relationalOperator: RelationalOperatorField,
|
||||
operator: OperatorField,
|
||||
yesorno: YesNoField,
|
||||
trueorfalse: TrueFalseField,
|
||||
expression: ExpressionField,
|
||||
string: StringField,
|
||||
behavior: BehaviorField,
|
||||
scenevar: SceneVariableField,
|
||||
globalvar: GlobalVariableField,
|
||||
objectvar: ObjectVariableField,
|
||||
layer: LayerField,
|
||||
key: KeyField,
|
||||
file: DefaultField, //TODO
|
||||
musicfile: DefaultField, //TODO
|
||||
soundfile: DefaultField, //TODO
|
||||
color: DefaultField, //TODO
|
||||
police: DefaultField, //TODO
|
||||
joyaxis: DefaultField, //TODO
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
default: DefaultField,
|
||||
mouse: MouseField,
|
||||
object: ObjectField,
|
||||
relationalOperator: RelationalOperatorField,
|
||||
operator: OperatorField,
|
||||
yesorno: YesNoField,
|
||||
trueorfalse: TrueFalseField,
|
||||
expression: ExpressionField,
|
||||
string: StringField,
|
||||
behavior: BehaviorField,
|
||||
scenevar: SceneVariableField,
|
||||
globalvar: GlobalVariableField,
|
||||
objectvar: ObjectVariableField,
|
||||
layer: LayerField,
|
||||
key: KeyField,
|
||||
file: DefaultField, //TODO
|
||||
musicfile: DefaultField, //TODO
|
||||
soundfile: DefaultField, //TODO
|
||||
color: DefaultField, //TODO
|
||||
police: DefaultField, //TODO
|
||||
joyaxis: DefaultField, //TODO
|
||||
},
|
||||
getParameterComponent: function(type) {
|
||||
components,
|
||||
getParameterComponent: (type: string) => {
|
||||
const fieldType = gd.ParameterMetadata.isObject(type) ? 'object' : type;
|
||||
|
||||
if (this.components.hasOwnProperty(fieldType))
|
||||
return this.components[fieldType];
|
||||
else return this.components.default;
|
||||
if (components.hasOwnProperty(fieldType))
|
||||
return components[fieldType];
|
||||
else return components.default;
|
||||
},
|
||||
};
|
||||
|
@@ -46,6 +46,13 @@ export class Toolbar extends PureComponent {
|
||||
tooltip={t('Launch a preview of the scene')}
|
||||
/>
|
||||
)}
|
||||
{this.props.showNetworkPreviewButton && (
|
||||
<ToolbarIcon
|
||||
onClick={this.props.onNetworkPreview}
|
||||
src="res/ribbon_default/networkpreview32.png"
|
||||
tooltip={t('Preview the scene on a mobile or tablet connected to your wifi')}
|
||||
/>
|
||||
)}
|
||||
{this.props.showPreviewButton && <ToolbarSeparator />}
|
||||
<ToolbarIcon
|
||||
onClick={this.props.onAddStandardEvent}
|
||||
|
@@ -102,7 +102,9 @@ export default class EventsSheet extends Component {
|
||||
canRemove={hasSomethingSelected(this.state.selection)}
|
||||
onRemove={this.deleteSelection}
|
||||
showPreviewButton={this.props.showPreviewButton}
|
||||
onPreview={this.props.onPreview}
|
||||
showNetworkPreviewButton={this.props.showNetworkPreviewButton}
|
||||
onPreview={() => this.props.onPreview({})}
|
||||
onNetworkPreview={() => this.props.onPreview({networkPreview: true})}
|
||||
canUndo={canUndo(this.state.history)}
|
||||
canRedo={canRedo(this.state.history)}
|
||||
undo={this.undo}
|
||||
@@ -296,6 +298,13 @@ export default class EventsSheet extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
toggleDisabled = () => {
|
||||
getSelectedEvents(this.state.selection).forEach(event =>
|
||||
event.setDisabled(!event.isDisabled())
|
||||
);
|
||||
this._saveChangesToHistory(() => this._eventsTree.forceEventsUpdate());
|
||||
}
|
||||
|
||||
deleteSelection = () => {
|
||||
const { events } = this.props;
|
||||
const eventsRemover = new gd.EventsRemover();
|
||||
@@ -528,6 +537,10 @@ export default class EventsSheet extends Component {
|
||||
accelerator: 'CmdOrCtrl+V',
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Toggle disabled',
|
||||
click: () => this.toggleDisabled(),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
click: () => this.deleteSelection(),
|
||||
|
@@ -5,7 +5,7 @@ import Window from '../../Utils/Window';
|
||||
|
||||
export default class BrowserExport extends Component {
|
||||
openWebsite = () => {
|
||||
Window.openExternalURL('http://compilgames.net');
|
||||
Window.openExternalURL('http://gdevelop-app.com');
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import indexHTML from './GDJSindex.html.js';
|
||||
|
||||
const gdjsRoot =
|
||||
'https://s3-eu-west-1.amazonaws.com/gdevelop-resources/GDJS-4095';
|
||||
'https://s3-eu-west-1.amazonaws.com/gdevelop-resources/GDJS-5.0.0-beta26';
|
||||
|
||||
export const findGDJS = cb => {
|
||||
return cb({
|
||||
|
@@ -1,9 +1,10 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import BrowserS3FileSystem from './BrowserS3FileSystem';
|
||||
import BrowserPreviewLinkDialog from './BrowserPreviewLinkDialog';
|
||||
import { findGDJS } from './BrowserS3GDJSFinder';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import { type PreviewOptions } from '../PreviewLauncher.flow';
|
||||
import { GDevelopGamesPreview } from '../../Utils/GDevelopServices/ApiConfigs';
|
||||
import { makeTimestampedId } from '../../Utils/TimestampedId';
|
||||
const awsS3 = require('aws-sdk/clients/s3');
|
||||
@@ -24,8 +25,25 @@ const awsS3Client = new awsS3({
|
||||
correctClockSkew: true,
|
||||
});
|
||||
|
||||
export default class BrowserS3PreviewLauncher {
|
||||
static _openPreviewWindow = (project: gdProject, url: string): any => {
|
||||
type State = {|
|
||||
showPreviewLinkDialog: boolean,
|
||||
url: ?string,
|
||||
|};
|
||||
|
||||
type Props = {};
|
||||
|
||||
export default class BrowserS3PreviewLauncher extends React.Component<
|
||||
Props,
|
||||
State
|
||||
> {
|
||||
canDoNetworkPreview = () => false;
|
||||
|
||||
state = {
|
||||
showPreviewLinkDialog: false,
|
||||
url: null,
|
||||
};
|
||||
|
||||
_openPreviewWindow = (project: gdProject, url: string): any => {
|
||||
const windowObjectReference = window.open(url, `_blank`);
|
||||
return {
|
||||
url,
|
||||
@@ -33,7 +51,7 @@ export default class BrowserS3PreviewLauncher {
|
||||
};
|
||||
};
|
||||
|
||||
static _prepareExporter = (): Promise<any> => {
|
||||
_prepareExporter = (): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
findGDJS(({ gdjsRoot, filesContent }) => {
|
||||
if (!gdjsRoot) {
|
||||
@@ -68,13 +86,14 @@ export default class BrowserS3PreviewLauncher {
|
||||
});
|
||||
};
|
||||
|
||||
static launchLayoutPreview = (
|
||||
launchLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout
|
||||
layout: gdLayout,
|
||||
options: PreviewOptions
|
||||
): Promise<any> => {
|
||||
if (!project || !layout) return Promise.reject();
|
||||
|
||||
return BrowserS3PreviewLauncher._prepareExporter().then(
|
||||
return this._prepareExporter().then(
|
||||
({ exporter, outputDir, browserS3FileSystem }) => {
|
||||
exporter.exportLayoutForPixiPreview(project, layout, outputDir);
|
||||
exporter.delete();
|
||||
@@ -82,25 +101,59 @@ export default class BrowserS3PreviewLauncher {
|
||||
.uploadPendingObjects()
|
||||
.then(() => {
|
||||
const finalUrl = outputDir + '/index.html';
|
||||
return BrowserS3PreviewLauncher._openPreviewWindow(
|
||||
project,
|
||||
finalUrl
|
||||
);
|
||||
return this._openPreviewWindow(project, finalUrl);
|
||||
})
|
||||
.then(({ url, windowObjectReference }) => {
|
||||
if (!windowObjectReference) {
|
||||
return { dialog: <BrowserPreviewLinkDialog url={url} /> };
|
||||
this.setState({
|
||||
showPreviewLinkDialog: true,
|
||||
url,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
static launchExternalLayoutPreview = (
|
||||
launchExternalLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout,
|
||||
externalLayout: gdExternalLayout
|
||||
externalLayout: gdExternalLayout,
|
||||
options: PreviewOptions
|
||||
): Promise<any> => {
|
||||
return Promise.reject('Not implemented');
|
||||
if (!project || !layout || !externalLayout) return Promise.reject();
|
||||
|
||||
return this._prepareExporter().then(
|
||||
({ exporter, outputDir, browserS3FileSystem }) => {
|
||||
exporter.exportExternalLayoutForPixiPreview(
|
||||
project,
|
||||
layout,
|
||||
externalLayout,
|
||||
outputDir
|
||||
);
|
||||
exporter.delete();
|
||||
return browserS3FileSystem
|
||||
.uploadPendingObjects()
|
||||
.then(() => {
|
||||
const finalUrl = outputDir + '/index.html';
|
||||
return this._openPreviewWindow(project, finalUrl);
|
||||
})
|
||||
.then(({ url, windowObjectReference }) => {
|
||||
if (!windowObjectReference) {
|
||||
this.setState({
|
||||
showPreviewLinkDialog: true,
|
||||
url,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { showPreviewLinkDialog, url } = this.state;
|
||||
if (!showPreviewLinkDialog) return null;
|
||||
|
||||
return <BrowserPreviewLinkDialog url={url} />;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,13 @@
|
||||
//TODO: Download the file then read it instead of hardcoding it.
|
||||
|
||||
export default `<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
|
@@ -10,10 +10,9 @@ import {
|
||||
getUrl,
|
||||
getBuild,
|
||||
} from '../../../Utils/GDevelopServices/Build';
|
||||
import {
|
||||
withUserProfile,
|
||||
type WithUserProfileProps,
|
||||
} from '../../../Profile/UserProfileContainer';
|
||||
import UserProfileContext, {
|
||||
type UserProfile,
|
||||
} from '../../../Profile/UserProfileContext';
|
||||
import { Column, Line } from '../../../UI/Grid';
|
||||
import { showErrorBox } from '../../../UI/Messages/MessageBox';
|
||||
import { findGDJS } from '../LocalGDJSFinder';
|
||||
@@ -28,6 +27,7 @@ import LimitDisplayer from '../../../Profile/LimitDisplayer';
|
||||
import { displaySanityCheck } from '../../SanityChecker';
|
||||
import { getSanityMessages } from '../../SanityChecker/CordovaSanityChecker';
|
||||
import { translate, type TranslatorProps } from 'react-i18next';
|
||||
import { type Limit } from '../../../Utils/GDevelopServices/Usage';
|
||||
const path = optionalRequire('path');
|
||||
const os = optionalRequire('os');
|
||||
const electron = optionalRequire('electron');
|
||||
@@ -54,7 +54,7 @@ type State = {
|
||||
errored: boolean,
|
||||
};
|
||||
|
||||
type Props = WithUserProfileProps & TranslatorProps & {
|
||||
type Props = TranslatorProps & {
|
||||
project: gdProject,
|
||||
onChangeSubscription: Function,
|
||||
};
|
||||
@@ -154,13 +154,15 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
});
|
||||
};
|
||||
|
||||
launchBuild = (uploadBucketKey: string): Promise<string> => {
|
||||
const { authentification, profile } = this.props;
|
||||
if (!profile || !authentification)
|
||||
return Promise.reject(new Error('User is not authenticated'));
|
||||
launchBuild = (
|
||||
userProfile: UserProfile,
|
||||
uploadBucketKey: string
|
||||
): Promise<string> => {
|
||||
const { getAuthorizationHeader, profile } = userProfile;
|
||||
if (!profile) return Promise.reject(new Error('User is not authenticated'));
|
||||
|
||||
return buildCordovaAndroid(
|
||||
authentification,
|
||||
getAuthorizationHeader,
|
||||
profile.uid,
|
||||
uploadBucketKey
|
||||
).then(build => {
|
||||
@@ -168,10 +170,12 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
});
|
||||
};
|
||||
|
||||
pollBuild = async (buildId: string): Promise<Build> => {
|
||||
const { authentification, profile } = this.props;
|
||||
if (!profile || !authentification)
|
||||
return Promise.reject(new Error('User is not authenticated'));
|
||||
pollBuild = async (
|
||||
userProfile: UserProfile,
|
||||
buildId: string
|
||||
): Promise<Build> => {
|
||||
const { getAuthorizationHeader, profile } = userProfile;
|
||||
if (!profile) return Promise.reject(new Error('User is not authenticated'));
|
||||
|
||||
try {
|
||||
let build = null;
|
||||
@@ -180,7 +184,7 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
const maxWaitTime = 200000;
|
||||
do {
|
||||
await delay(waitTime);
|
||||
build = await getBuild(authentification, profile.uid, buildId);
|
||||
build = await getBuild(getAuthorizationHeader, profile.uid, buildId);
|
||||
this.setState({
|
||||
build,
|
||||
buildMax: maxWaitTime,
|
||||
@@ -200,12 +204,11 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
launchWholeExport = () => {
|
||||
launchWholeExport = (userProfile: UserProfile) => {
|
||||
const { t, project } = this.props;
|
||||
sendExportLaunched('local-online-cordova');
|
||||
|
||||
if (!displaySanityCheck(t, getSanityMessages(t, project)))
|
||||
return;
|
||||
if (!displaySanityCheck(t, getSanityMessages(t, project))) return;
|
||||
|
||||
const handleError = (message: string) => err => {
|
||||
if (!this.state.errored) {
|
||||
@@ -244,21 +247,21 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
this.setState({
|
||||
exportStep: 'waiting-for-build',
|
||||
});
|
||||
return this.launchBuild(uploadBucketKey);
|
||||
return this.launchBuild(userProfile, uploadBucketKey);
|
||||
}, handleError(t('Error while uploading the game. Check your internet connection or try again later.')))
|
||||
.then(buildId => {
|
||||
this.setState({
|
||||
exportStep: 'build',
|
||||
});
|
||||
|
||||
return this.pollBuild(buildId);
|
||||
return this.pollBuild(userProfile, buildId);
|
||||
}, handleError(t('Error while lauching the build of the game.')))
|
||||
.then(build => {
|
||||
this.setState({
|
||||
exportStep: 'done',
|
||||
build,
|
||||
});
|
||||
this.props.onRefreshUserProfile();
|
||||
userProfile.onRefreshUserProfile();
|
||||
}, handleError(t('Error while building the game.')));
|
||||
};
|
||||
|
||||
@@ -276,12 +279,6 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
Window.openExternalURL(getUrl(build.logsKey));
|
||||
};
|
||||
|
||||
_onChangeSubscription = () => {
|
||||
const { onChangeSubscription } = this.props;
|
||||
|
||||
onChangeSubscription();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
exportStep,
|
||||
@@ -292,68 +289,75 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
buildProgress,
|
||||
errored,
|
||||
} = this.state;
|
||||
const {
|
||||
project,
|
||||
authenticated,
|
||||
onLogin,
|
||||
subscription,
|
||||
limits,
|
||||
t,
|
||||
} = this.props;
|
||||
const { project, t } = this.props;
|
||||
if (!project) return null;
|
||||
|
||||
const buildLimit = limits ? limits['cordova-build'] : null;
|
||||
const disableBuild =
|
||||
(!errored && exportStep !== '' && exportStep !== 'done') ||
|
||||
(buildLimit && buildLimit.limitReached);
|
||||
const getBuildLimit = (userProfile: UserProfile): ?Limit =>
|
||||
userProfile.limits ? userProfile.limits['cordova-build'] : null;
|
||||
const canLaunchBuild = (userProfile: UserProfile) => {
|
||||
if (!errored && exportStep !== '' && exportStep !== 'done') return false;
|
||||
|
||||
const limit: ?Limit = getBuildLimit(userProfile);
|
||||
if (limit && limit.limitReached) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return (
|
||||
<Column noMargin>
|
||||
<Line>
|
||||
{t("Packaging your game for Android will create an APK file that can be installed on Android phones, based on Cordova framework.")}
|
||||
</Line>
|
||||
{authenticated && (
|
||||
<Line justifyContent="center">
|
||||
<RaisedButton
|
||||
label={t("Package for Android")}
|
||||
primary
|
||||
onClick={this.launchWholeExport}
|
||||
disabled={disableBuild}
|
||||
/>
|
||||
</Line>
|
||||
<UserProfileContext.Consumer>
|
||||
{(userProfile: UserProfile) => (
|
||||
<Column noMargin>
|
||||
<Line>
|
||||
{t(
|
||||
'Packaging your game for Android will create an APK file that can be installed on Android phones, based on Cordova framework.'
|
||||
)}
|
||||
</Line>
|
||||
{userProfile.authenticated && (
|
||||
<Line justifyContent="center">
|
||||
<RaisedButton
|
||||
label={t('Package for Android')}
|
||||
primary
|
||||
onClick={() => this.launchWholeExport(userProfile)}
|
||||
disabled={!canLaunchBuild(userProfile)}
|
||||
/>
|
||||
</Line>
|
||||
)}
|
||||
{userProfile.authenticated && (
|
||||
<LimitDisplayer
|
||||
subscription={userProfile.subscription}
|
||||
limit={getBuildLimit(userProfile)}
|
||||
onChangeSubscription={this.props.onChangeSubscription}
|
||||
/>
|
||||
)}
|
||||
{!userProfile.authenticated && (
|
||||
<CreateProfile
|
||||
message={t(
|
||||
'Create an account to build your game for Android in one-click:'
|
||||
)}
|
||||
onLogin={userProfile.onLogin}
|
||||
/>
|
||||
)}
|
||||
<Line>
|
||||
<Progress
|
||||
exportStep={exportStep}
|
||||
downloadUrl={
|
||||
build && build.apkKey ? getUrl(build.apkKey) : null
|
||||
}
|
||||
logsUrl={build && build.logsKey ? getUrl(build.logsKey) : null}
|
||||
onDownload={this._download}
|
||||
onDownloadLogs={this._downloadLogs}
|
||||
uploadMax={uploadMax}
|
||||
uploadProgress={uploadProgress}
|
||||
buildMax={buildMax}
|
||||
buildProgress={buildProgress}
|
||||
errored={errored}
|
||||
/>
|
||||
</Line>
|
||||
</Column>
|
||||
)}
|
||||
{authenticated && (
|
||||
<LimitDisplayer
|
||||
subscription={subscription}
|
||||
limit={buildLimit}
|
||||
onChangeSubscription={this._onChangeSubscription}
|
||||
/>
|
||||
)}
|
||||
{!authenticated && (
|
||||
<CreateProfile
|
||||
message={t("Create an account to build your game for Android in one-click:")}
|
||||
onLogin={onLogin}
|
||||
/>
|
||||
)}
|
||||
<Line>
|
||||
<Progress
|
||||
exportStep={exportStep}
|
||||
downloadUrl={build && build.apkKey ? getUrl(build.apkKey) : null}
|
||||
logsUrl={build && build.logsKey ? getUrl(build.logsKey) : null}
|
||||
onDownload={this._download}
|
||||
onDownloadLogs={this._downloadLogs}
|
||||
uploadMax={uploadMax}
|
||||
uploadProgress={uploadProgress}
|
||||
buildMax={buildMax}
|
||||
buildProgress={buildProgress}
|
||||
errored={errored}
|
||||
/>
|
||||
</Line>
|
||||
</Column>
|
||||
</UserProfileContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate()(withUserProfile({ fetchLimits: true, fetchSubscription: true })(
|
||||
LocalOnlineCordovaExport
|
||||
));
|
||||
export default translate()(LocalOnlineCordovaExport);
|
||||
|
@@ -1,95 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import localFileSystem from './LocalFileSystem';
|
||||
import optionalRequire from '../../Utils/OptionalRequire';
|
||||
import { timeFunction } from '../../Utils/TimeFunction';
|
||||
import { findGDJS } from './LocalGDJSFinder';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
const electron = optionalRequire('electron');
|
||||
const path = optionalRequire('path');
|
||||
const BrowserWindow = electron ? electron.remote.BrowserWindow : null;
|
||||
const gd = global.gd;
|
||||
|
||||
export default class LocalPreviewLauncher {
|
||||
static _openPreviewWindow = (project: gdProject, gamePath: string): void => {
|
||||
if (!BrowserWindow) return;
|
||||
|
||||
const win = new BrowserWindow({
|
||||
width: project.getMainWindowDefaultWidth(),
|
||||
height: project.getMainWindowDefaultHeight(),
|
||||
title: `Preview of ${project.getName()}`,
|
||||
});
|
||||
win.loadURL(`file://${gamePath}/index.html`);
|
||||
};
|
||||
|
||||
static _prepareExporter = (): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
findGDJS(gdjsRoot => {
|
||||
if (!gdjsRoot) {
|
||||
//TODO
|
||||
console.error('Could not find GDJS');
|
||||
return reject();
|
||||
}
|
||||
console.info('GDJS found in ', gdjsRoot);
|
||||
|
||||
const fileSystem = assignIn(
|
||||
new gd.AbstractFileSystemJS(),
|
||||
localFileSystem
|
||||
);
|
||||
const outputDir = path.join(fileSystem.getTempDir(), 'preview');
|
||||
const exporter = new gd.Exporter(fileSystem, gdjsRoot);
|
||||
|
||||
resolve({
|
||||
outputDir,
|
||||
exporter,
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
static launchLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout
|
||||
): Promise<any> => {
|
||||
if (!project || !layout) return Promise.reject();
|
||||
|
||||
return LocalPreviewLauncher._prepareExporter().then(
|
||||
({ outputDir, exporter }) => {
|
||||
timeFunction(
|
||||
() => {
|
||||
exporter.exportLayoutForPixiPreview(project, layout, outputDir);
|
||||
exporter.delete();
|
||||
LocalPreviewLauncher._openPreviewWindow(project, outputDir);
|
||||
},
|
||||
time => console.info(`Preview took ${time}ms`)
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
static launchExternalLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout,
|
||||
externalLayout: gdExternalLayout
|
||||
): Promise<any> => {
|
||||
if (!project || !externalLayout) return Promise.reject();
|
||||
|
||||
return LocalPreviewLauncher._prepareExporter().then(
|
||||
({ outputDir, exporter }) => {
|
||||
timeFunction(
|
||||
() => {
|
||||
exporter.exportExternalLayoutForPixiPreview(
|
||||
project,
|
||||
layout,
|
||||
externalLayout,
|
||||
outputDir
|
||||
);
|
||||
exporter.delete();
|
||||
LocalPreviewLauncher._openPreviewWindow(project, outputDir);
|
||||
},
|
||||
time => console.info(`Preview took ${time}ms`)
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
// @flow
|
||||
|
||||
export const findLocalIp = (ipAddresses: Array<string>): ?string => {
|
||||
if (!ipAddresses.length) return null;
|
||||
|
||||
let firstLocalIp = ipAddresses.find((ipAddress) => ipAddress.indexOf("192.168") === 0);
|
||||
if (firstLocalIp) return firstLocalIp;
|
||||
|
||||
firstLocalIp = ipAddresses.find( (ipAddress) => ipAddress.indexOf("192") === 0);
|
||||
if (firstLocalIp) return firstLocalIp;
|
||||
|
||||
return ipAddresses[0];
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import { findLocalIp } from './LocalIpFinder';
|
||||
|
||||
describe('LocalIpFinder', () => {
|
||||
it('can find the local ip in an array of ips', () => {
|
||||
expect(findLocalIp(['192.168.0.1'])).toBe('192.168.0.1');
|
||||
expect(findLocalIp(['127.0.0.1', '192.168.0.1'])).toBe('192.168.0.1');
|
||||
expect(findLocalIp(['96.0.0.1', '192.168.0.1'])).toBe('192.168.0.1');
|
||||
expect(findLocalIp(['96.0.0.1', '192.100.0.1'])).toBe('192.100.0.1');
|
||||
expect(findLocalIp(['96.0.0.1', '2'])).toBe('96.0.0.1');
|
||||
expect(findLocalIp([])).toBe(null);
|
||||
});
|
||||
});
|
@@ -0,0 +1,74 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import Dialog from '../../../UI/Dialog';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import { Line } from '../../../UI/Grid';
|
||||
import PlaceholderLoader from '../../../UI/PlaceholderLoader';
|
||||
|
||||
type Props = {|
|
||||
open: boolean,
|
||||
url: ?string,
|
||||
onClose: () => void,
|
||||
onExport: ?() => void,
|
||||
onRunPreviewLocally: () => void,
|
||||
error: ?any,
|
||||
|};
|
||||
|
||||
export default class LocalNetworkDialog extends React.Component<Props, {}> {
|
||||
render() {
|
||||
const {
|
||||
url,
|
||||
open,
|
||||
error,
|
||||
onExport,
|
||||
onClose,
|
||||
onRunPreviewLocally,
|
||||
} = this.props;
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
actions={[
|
||||
<FlatButton key="close" label="Close" primary onClick={onClose} />,
|
||||
]}
|
||||
secondaryActions={[
|
||||
onExport && (
|
||||
<FlatButton key="export" label="Export game" onClick={onExport} />
|
||||
),
|
||||
<FlatButton
|
||||
key="run-preview-locally"
|
||||
label="Run on this computer"
|
||||
onClick={onRunPreviewLocally}
|
||||
/>,
|
||||
]}
|
||||
modal
|
||||
open={open}
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
{error && (
|
||||
<Line>
|
||||
Unable to start the server for the preview! Make sure that you are
|
||||
authorized to run servers on this computer. Otherwise, use classic
|
||||
preview to test your game.
|
||||
</Line>
|
||||
)}
|
||||
{!error && !url && <PlaceholderLoader />}
|
||||
{!error &&
|
||||
url && (
|
||||
<div>
|
||||
<Line>
|
||||
Your preview is ready! On your mobile or tablet, open your
|
||||
browser and enter in the address bar:
|
||||
</Line>
|
||||
<TextField value={url} fullWidth />
|
||||
<Line>
|
||||
Please note that your device should be connected on the same
|
||||
network as this computer.
|
||||
</Line>
|
||||
</div>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,229 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import localFileSystem from '../LocalFileSystem';
|
||||
import optionalRequire from '../../../Utils/OptionalRequire';
|
||||
import { timeFunction } from '../../../Utils/TimeFunction';
|
||||
import { findGDJS } from '../LocalGDJSFinder';
|
||||
import LocalNetworkPreviewDialog from './LocalNetworkPreviewDialog';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import { type PreviewOptions } from '../../PreviewLauncher.flow';
|
||||
import { findLocalIp } from './LocalIpFinder';
|
||||
import SubscriptionChecker from '../../../Profile/SubscriptionChecker';
|
||||
const electron = optionalRequire('electron');
|
||||
const path = optionalRequire('path');
|
||||
const ipcRenderer = electron ? electron.ipcRenderer : null;
|
||||
const BrowserWindow = electron ? electron.remote.BrowserWindow : null;
|
||||
const gd = global.gd;
|
||||
|
||||
type Props = {|
|
||||
onExport?: () => void,
|
||||
onChangeSubscription?: () => void,
|
||||
|};
|
||||
|
||||
type State = {
|
||||
networkPreviewDialogOpen: boolean,
|
||||
networkPreviewHost: ?string,
|
||||
networkPreviewPort: ?number,
|
||||
networkPreviewError: ?any,
|
||||
previewGamePath: ?string,
|
||||
previewBrowserWindowConfig: ?{
|
||||
width: number,
|
||||
height: number,
|
||||
title: string,
|
||||
backgroundColor: string,
|
||||
},
|
||||
};
|
||||
|
||||
export default class LocalPreviewLauncher extends React.Component<
|
||||
Props,
|
||||
State
|
||||
> {
|
||||
canDoNetworkPreview = () => true;
|
||||
|
||||
state = {
|
||||
networkPreviewDialogOpen: false,
|
||||
networkPreviewHost: null,
|
||||
networkPreviewPort: null,
|
||||
networkPreviewError: null,
|
||||
previewGamePath: null,
|
||||
previewBrowserWindowConfig: null,
|
||||
};
|
||||
_subscriptionChecker: ?SubscriptionChecker = null;
|
||||
|
||||
_openPreviewBrowserWindow = () => {
|
||||
if (
|
||||
!BrowserWindow ||
|
||||
!this.state.previewBrowserWindowConfig ||
|
||||
!this.state.previewGamePath
|
||||
)
|
||||
return;
|
||||
|
||||
const win = new BrowserWindow(this.state.previewBrowserWindowConfig);
|
||||
win.loadURL(`file://${this.state.previewGamePath}/index.html`);
|
||||
};
|
||||
|
||||
_openPreviewWindow = (
|
||||
project: gdProject,
|
||||
gamePath: string,
|
||||
options: PreviewOptions
|
||||
): void => {
|
||||
this.setState(
|
||||
{
|
||||
previewBrowserWindowConfig: {
|
||||
width: project.getMainWindowDefaultWidth(),
|
||||
height: project.getMainWindowDefaultHeight(),
|
||||
title: `Preview of ${project.getName()}`,
|
||||
backgroundColor: '#000000',
|
||||
},
|
||||
previewGamePath: gamePath,
|
||||
},
|
||||
() => {
|
||||
if (!options.networkPreview) {
|
||||
this._openPreviewBrowserWindow();
|
||||
} else {
|
||||
if (!ipcRenderer) return;
|
||||
|
||||
ipcRenderer.removeAllListeners('serve-folder-done');
|
||||
ipcRenderer.removeAllListeners('local-network-ips');
|
||||
ipcRenderer.on('serve-folder-done', (event, err, serverParams) => {
|
||||
if (err) {
|
||||
this.setState({
|
||||
networkPreviewDialogOpen: true,
|
||||
networkPreviewPort: null,
|
||||
networkPreviewHost: null,
|
||||
networkPreviewError: err,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
networkPreviewDialogOpen: true,
|
||||
networkPreviewPort: serverParams.port,
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => this._checkSubscription());
|
||||
});
|
||||
ipcRenderer.on('local-network-ips', (event, ipAddresses) => {
|
||||
this.setState({
|
||||
networkPreviewHost: findLocalIp(ipAddresses),
|
||||
});
|
||||
});
|
||||
ipcRenderer.send('serve-folder', {
|
||||
root: gamePath,
|
||||
});
|
||||
ipcRenderer.send('get-local-network-ips');
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
_prepareExporter = (): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
findGDJS(gdjsRoot => {
|
||||
if (!gdjsRoot) {
|
||||
//TODO
|
||||
console.error('Could not find GDJS');
|
||||
return reject();
|
||||
}
|
||||
console.info('GDJS found in ', gdjsRoot);
|
||||
|
||||
const fileSystem = assignIn(
|
||||
new gd.AbstractFileSystemJS(),
|
||||
localFileSystem
|
||||
);
|
||||
const outputDir = path.join(fileSystem.getTempDir(), 'preview');
|
||||
const exporter = new gd.Exporter(fileSystem, gdjsRoot);
|
||||
|
||||
resolve({
|
||||
outputDir,
|
||||
exporter,
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
launchLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout,
|
||||
options: PreviewOptions
|
||||
): Promise<any> => {
|
||||
if (!project || !layout) return Promise.reject();
|
||||
|
||||
return this._prepareExporter().then(({ outputDir, exporter }) => {
|
||||
timeFunction(
|
||||
() => {
|
||||
exporter.exportLayoutForPixiPreview(project, layout, outputDir);
|
||||
exporter.delete();
|
||||
this._openPreviewWindow(project, outputDir, options);
|
||||
},
|
||||
time => console.info(`Preview took ${time}ms`)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
launchExternalLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout,
|
||||
externalLayout: gdExternalLayout,
|
||||
options: PreviewOptions
|
||||
): Promise<any> => {
|
||||
if (!project || !externalLayout) return Promise.reject();
|
||||
|
||||
return this._prepareExporter().then(({ outputDir, exporter }) => {
|
||||
timeFunction(
|
||||
() => {
|
||||
exporter.exportExternalLayoutForPixiPreview(
|
||||
project,
|
||||
layout,
|
||||
externalLayout,
|
||||
outputDir
|
||||
);
|
||||
exporter.delete();
|
||||
this._openPreviewWindow(project, outputDir, options);
|
||||
},
|
||||
time => console.info(`Preview took ${time}ms`)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
_checkSubscription = (options: PreviewOptions) => {
|
||||
if (!this._subscriptionChecker) return true;
|
||||
|
||||
return this._subscriptionChecker.checkHasSubscription();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
networkPreviewDialogOpen,
|
||||
networkPreviewHost,
|
||||
networkPreviewPort,
|
||||
networkPreviewError,
|
||||
} = this.state;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SubscriptionChecker
|
||||
ref={subscriptionChecker =>
|
||||
(this._subscriptionChecker = subscriptionChecker)}
|
||||
onChangeSubscription={() => {
|
||||
this.setState({ networkPreviewDialogOpen: false });
|
||||
if (this.props.onChangeSubscription)
|
||||
this.props.onChangeSubscription();
|
||||
}}
|
||||
title="Preview over wifi"
|
||||
mode="try"
|
||||
/>
|
||||
<LocalNetworkPreviewDialog
|
||||
open={networkPreviewDialogOpen}
|
||||
url={
|
||||
networkPreviewHost && networkPreviewPort
|
||||
? `${networkPreviewHost}:${networkPreviewPort}`
|
||||
: null
|
||||
}
|
||||
error={networkPreviewError}
|
||||
onClose={() => this.setState({ networkPreviewDialogOpen: false })}
|
||||
onExport={this.props.onExport}
|
||||
onRunPreviewLocally={this._openPreviewBrowserWindow}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
20
newIDE/app/src/Export/PreviewLauncher.flow.js
Normal file
20
newIDE/app/src/Export/PreviewLauncher.flow.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export type PreviewOptions = {|
|
||||
networkPreview?: boolean,
|
||||
|};
|
||||
|
||||
export type PreviewLauncher = {
|
||||
launchLayoutPreview: (
|
||||
project: gdProject,
|
||||
layout: gdLayout,
|
||||
options: PreviewOptions
|
||||
) => Promise<any>,
|
||||
|
||||
launchExternalLayoutPreview: (
|
||||
project: gdProject,
|
||||
layout: gdLayout,
|
||||
externalLayout: gdExternalLayout,
|
||||
options: PreviewOptions
|
||||
) => Promise<any>,
|
||||
|
||||
canDoNetworkPreview: () => boolean,
|
||||
};
|
@@ -124,7 +124,7 @@ class ExternalEditor extends Component<Props, State> {
|
||||
console.log('Update send done');
|
||||
};
|
||||
|
||||
editObject = (object: any) => {
|
||||
editObject = (object: gdObject) => {
|
||||
this.sendUpdate();
|
||||
this.bridge.send('editObject', object.getName());
|
||||
};
|
||||
|
@@ -12,7 +12,7 @@ export default class InstancesResizer {
|
||||
}
|
||||
|
||||
_roundXPosition(x) {
|
||||
if (!this.options.snap || !this.options.grid) return x;
|
||||
if (!this.options.snap || !this.options.grid) return Math.round(x);
|
||||
|
||||
return (
|
||||
Math.round((x - this.options.gridOffsetX) / this.options.gridWidth) *
|
||||
@@ -22,7 +22,7 @@ export default class InstancesResizer {
|
||||
}
|
||||
|
||||
_roundYPosition(y) {
|
||||
if (!this.options.snap || !this.options.grid) return y;
|
||||
if (!this.options.snap || !this.options.grid) return Math.round(y);
|
||||
|
||||
return (
|
||||
Math.round((y - this.options.gridOffsetY) / this.options.gridHeight) *
|
||||
|
@@ -275,6 +275,14 @@ export default class InstancesEditorContainer extends Component {
|
||||
) {
|
||||
this._mountEditorComponents(nextProps);
|
||||
}
|
||||
|
||||
// For avoiding useless renderings, which is costly for CPU/GPU, when the editor
|
||||
// is not displayed, `pauseRendering` prop can be set to true.
|
||||
if (nextProps.pauseRendering && !this.props.pauseRendering)
|
||||
this.pauseSceneRendering();
|
||||
|
||||
if (!nextProps.pauseRendering && this.props.pauseRendering)
|
||||
this.restartSceneRendering();
|
||||
}
|
||||
|
||||
zoomBy(value) {
|
||||
@@ -476,6 +484,7 @@ export default class InstancesEditorContainer extends Component {
|
||||
_renderScene = () => {
|
||||
// Protect against rendering scheduled after the component is unmounted.
|
||||
if (this._unmounted) return;
|
||||
if (this._renderingPaused) return;
|
||||
|
||||
// Avoid killing the CPU by limiting the rendering calls.
|
||||
if (this.fpsLimiter.shouldUpdate()) {
|
||||
@@ -493,6 +502,16 @@ export default class InstancesEditorContainer extends Component {
|
||||
this.nextFrame = requestAnimationFrame(this._renderScene);
|
||||
};
|
||||
|
||||
pauseSceneRendering = () => {
|
||||
if (this.nextFrame) cancelAnimationFrame(this.nextFrame);
|
||||
this._renderingPaused = true;
|
||||
}
|
||||
|
||||
restartSceneRendering = () => {
|
||||
this._renderingPaused = false;
|
||||
this._renderScene();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.project) return null;
|
||||
|
||||
|
@@ -60,7 +60,7 @@ export default class AboutDialog extends Component<Props, *> {
|
||||
<FlatButton
|
||||
label="GDevelop Website"
|
||||
primary={false}
|
||||
onClick={() => Window.openExternalURL('http://compilgames.net')}
|
||||
onClick={() => Window.openExternalURL('http://gdevelop-app.com')}
|
||||
/>,
|
||||
<FlatButton label="Close" primary={false} onClick={onClose} />,
|
||||
];
|
||||
|
@@ -4,7 +4,7 @@ import FlatButton from 'material-ui/FlatButton';
|
||||
|
||||
export default class BetaIntroDialog extends Component {
|
||||
_onOpenWebsite() {
|
||||
window.open('http://compilgames.net', '_blank');
|
||||
window.open('http://gdevelop-app.com', '_blank');
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@@ -1,10 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
|
||||
export type EditorTab = {|
|
||||
render: () => React$Element<*>,
|
||||
render: (isCurrentTab: boolean) => React$Element<*>,
|
||||
editorRef: ?any,
|
||||
name: string,
|
||||
key: string,
|
||||
@@ -23,17 +21,22 @@ export const getEditorTabsInitialState = (): EditorTabsState => {
|
||||
};
|
||||
};
|
||||
|
||||
type renderEditorProps = {|
|
||||
isActive: boolean,
|
||||
editorRef: Function,
|
||||
|};
|
||||
|
||||
export const openEditorTab = (
|
||||
state: EditorTabsState,
|
||||
{
|
||||
name,
|
||||
editorCreator,
|
||||
renderEditor,
|
||||
key,
|
||||
dontFocusTab,
|
||||
closable,
|
||||
}: {
|
||||
name: string,
|
||||
editorCreator: () => React$Element<*>,
|
||||
renderEditor: (props: renderEditorProps) => React$Element<*>,
|
||||
key: string,
|
||||
dontFocusTab?: boolean,
|
||||
closable?: boolean,
|
||||
@@ -50,10 +53,11 @@ export const openEditorTab = (
|
||||
};
|
||||
}
|
||||
|
||||
const editorTab = {
|
||||
render: () =>
|
||||
React.cloneElement(editorCreator(), {
|
||||
ref: editorRef => (editorTab.editorRef = editorRef),
|
||||
const editorTab: EditorTab = {
|
||||
render: (isCurrentTab: boolean) =>
|
||||
renderEditor({
|
||||
isActive: isCurrentTab,
|
||||
editorRef: editorRef => (editorTab.editorRef = editorRef),
|
||||
}),
|
||||
editorRef: null,
|
||||
name,
|
||||
@@ -107,7 +111,10 @@ export const getCurrentTab = (state: EditorTabsState): EditorTab => {
|
||||
return state.editors[state.currentTab];
|
||||
};
|
||||
|
||||
export const closeProjectTabs = (state: EditorTabsState, project: ?gdProject) => {
|
||||
export const closeProjectTabs = (
|
||||
state: EditorTabsState,
|
||||
project: ?gdProject
|
||||
) => {
|
||||
return changeCurrentTab(
|
||||
{
|
||||
...state,
|
||||
|
@@ -1,17 +1,18 @@
|
||||
import { Component } from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { serializeToJSObject } from '../../Utils/Serializer';
|
||||
import { rgbToHexNumber } from '../../Utils/ColorTransformer';
|
||||
|
||||
export default class BaseEditor extends Component {
|
||||
export default class BaseEditor extends React.Component<*,*> {
|
||||
static defaultProps = {
|
||||
setToolbar: () => {},
|
||||
};
|
||||
|
||||
getProject() {
|
||||
getProject(): ?gdProject {
|
||||
return this.props.project;
|
||||
}
|
||||
|
||||
getLayout() {
|
||||
getLayout() :?gdLayout {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -27,7 +28,7 @@ export default class BaseEditor extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
static getLayoutSerializedElements(layout) {
|
||||
static getLayoutSerializedElements(layout: ?gdLayout) {
|
||||
if (!layout) return {};
|
||||
|
||||
return {
|
||||
|
@@ -1,15 +1,19 @@
|
||||
import React from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import EventsSheet from '../../EventsSheet';
|
||||
import { serializeToJSObject } from '../../Utils/Serializer';
|
||||
import BaseEditor from './BaseEditor';
|
||||
|
||||
export default class EventsEditor extends BaseEditor {
|
||||
editor: ?typeof EventsSheet;
|
||||
|
||||
updateToolbar() {
|
||||
if (this.editor) this.editor.updateToolbar();
|
||||
}
|
||||
|
||||
getSerializedElements() {
|
||||
const layout = this.getLayout();
|
||||
if (!layout) return {};
|
||||
|
||||
return {
|
||||
...BaseEditor.getLayoutSerializedElements(layout),
|
||||
@@ -17,7 +21,7 @@ export default class EventsEditor extends BaseEditor {
|
||||
};
|
||||
}
|
||||
|
||||
getLayout() {
|
||||
getLayout(): ?gdLayout {
|
||||
const { project, layoutName } = this.props;
|
||||
if (!project || !project.hasLayoutNamed(layoutName)) return null;
|
||||
|
||||
@@ -39,7 +43,7 @@ export default class EventsEditor extends BaseEditor {
|
||||
project={project}
|
||||
layout={layout}
|
||||
events={layout.getEvents()}
|
||||
onPreview={() => this.props.onPreview(project, layout)}
|
||||
onPreview={(options) => this.props.onPreview(project, layout, options)}
|
||||
onOpenExternalEvents={this.props.onOpenExternalEvents}
|
||||
/>
|
||||
);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user