Skip to content

Commit

Permalink
Fix object creation in functions and add space-shooter-with-functions…
Browse files Browse the repository at this point in the history
… example
  • Loading branch information
4ian committed Oct 20, 2018
1 parent 35aa78e commit 9f5b63b
Show file tree
Hide file tree
Showing 37 changed files with 7,806 additions and 254 deletions.
6 changes: 3 additions & 3 deletions Core/GDCore/Extensions/Builtin/BaseObjectExtension.cpp
Expand Up @@ -878,7 +878,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Objects"),
"res/actions/create24.png",
"res/actions/create.png")
.AddCodeOnlyParameter("currentScene", "")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter("objectListWithoutPicking", _("Object to create"))
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
Expand All @@ -896,7 +896,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Objects"),
"res/actions/create24.png",
"res/actions/create.png")
.AddCodeOnlyParameter("currentScene", "")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter(
"objectListWithoutPicking",
_("Groups containing objects that can be created by the action"))
Expand All @@ -916,7 +916,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Objects"),
"res/actions/add24.png",
"res/actions/add.png")
.AddCodeOnlyParameter("currentScene", "")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter("objectList", _("Object"))
.MarkAsAdvanced();

Expand Down
10 changes: 2 additions & 8 deletions Core/GDCore/Project/EventsFunction.cpp
Expand Up @@ -4,19 +4,13 @@
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include <vector>
#include "EventsFunction.h"
#include <vector>
#include "GDCore/Serialization/SerializerElement.h"

namespace gd {

EventsFunction::EventsFunction() : functionType(Action) {
ParameterMetadata runtimeSceneParameter;
runtimeSceneParameter.SetCodeOnly()
.SetType("currentScene")
.SetName("runtimeScene");
parameters.push_back(runtimeSceneParameter);
}
EventsFunction::EventsFunction() : functionType(Action) {}

void EventsFunction::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
Expand Down
6 changes: 5 additions & 1 deletion Core/GDCore/Project/EventsFunction.h
Expand Up @@ -126,7 +126,11 @@ class GD_CORE_API EventsFunction {
gd::EventsList& GetEvents() { return events; };

/**
* \brief Return the parameters.
* \brief Return the parameters of the function.
*
* \note During code/extension generation, new parameters are added
* to the generated function, like "runtimeScene" and "eventsFunctionContext".
* This should be transparent to the user.
*/
const std::vector<gd::ParameterMetadata>& GetParameters() const {
return parameters;
Expand Down
2 changes: 1 addition & 1 deletion GDCpp/GDCpp/Events/CodeGeneration/EventsCodeGenerator.cpp
Expand Up @@ -351,7 +351,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
gd::String argOutput;

// Code only parameter type
if (metadata.type == "currentScene") {
if (metadata.type == "currentScene" || metadata.type == "objectsContext") {
argOutput += "*runtimeContext->scene";
}
// Code only parameter type
Expand Down
47 changes: 40 additions & 7 deletions GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.cpp
Expand Up @@ -14,8 +14,8 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/SceneNameMangler.h"
#include "GDCore/Project/EventsFunction.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EventsFunction.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
Expand Down Expand Up @@ -154,26 +154,44 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(

gd::String EventsCodeGenerator::GenerateEventsFunctionParameterDeclarationsList(
const vector<gd::ParameterMetadata>& parameters) {
gd::String declaration = "";
gd::String declaration = "runtimeScene";
for (const auto& parameter : parameters) {
if (!declaration.empty()) declaration += ", ";
declaration += parameter.GetName().empty() ? "_" : parameter.GetName();
declaration += ", " + (parameter.GetName().empty() ? "_" : parameter.GetName());
}
declaration += ", parentEventsFunctionContext";

return declaration;
}

gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
const vector<gd::ParameterMetadata>& parameters) {
gd::String objectsGetters;
gd::String objectsCreators;
gd::String argumentsGetters;
for (const auto& parameter : parameters) {
if (parameter.GetName().empty()) continue;

if (gd::ParameterMetadata::IsObject(parameter.GetType())) {
// Generate getter that will be used to get the lists of objects passed
// as parameters
objectsGetters +=
"if (objectName === " + ConvertToStringExplicit(parameter.GetName()) +
") return gdjs.objectsListsToArray(" + parameter.GetName() + ");\n";
"&& !!" + parameter.GetName() + ") return gdjs.objectsListsToArray(" +
parameter.GetName() + ");\n";

// Generate creator functions that will be used to create new objects. We
// need to check if the function was given the context of the calling
// function (parentEventsFunctionContext). If this is the case, use it to
// create the new object as the object names used in the function are not
// the same as the objects available in the scene.
gd::String objectNameCode = parameter.GetName() + ".firstKey()";
objectsCreators +=
"if (objectName === " + ConvertToStringExplicit(parameter.GetName()) +
"&& !!" + parameter.GetName() +
") return parentEventsFunctionContext ? "
"parentEventsFunctionContext.createObject(" +
objectNameCode + ") : runtimeScene.createObject(" + objectNameCode +
");\n";
} else {
argumentsGetters +=
"if (argName === " + ConvertToStringExplicit(parameter.GetName()) +
Expand All @@ -185,8 +203,11 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
" getObjects: function(objectName) {\n" + objectsGetters +
" return [];"
" },\n" +
" getArgument: function(argName) {\n" + argumentsGetters +
" return \"\";" + " }\n" + "};\n";
" createObject: function(objectName) {\n" + objectsCreators +
" return null;\n" +
" },\n"
" getArgument: function(argName) {\n" +
argumentsGetters + " return \"\";\n" + " }\n" + "};\n";
}

gd::String EventsCodeGenerator::GenerateEventsFunctionReturn(
Expand Down Expand Up @@ -746,6 +767,18 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
argOutput = "runtimeScene";
}
// Code only parameter type
else if (metadata.type == "objectsContext") {
argOutput =
"(typeof eventsFunctionContext !== 'undefined' ? eventsFunctionContext "
": runtimeScene)";
}
// Code only parameter type
else if (metadata.type == "eventsFunctionContext") {
argOutput =
"(typeof eventsFunctionContext !== 'undefined' ? eventsFunctionContext "
": undefined)";
}
// Code only parameter type
else if (metadata.type == "objectList") {
std::vector<gd::String> realObjects = ExpandObjectsName(parameter, context);
for (auto& objectName : realObjects) context.ObjectsListNeeded(objectName);
Expand Down
13 changes: 13 additions & 0 deletions GDJS/GDJS/Events/CodeGeneration/EventsCodeGenerator.h
Expand Up @@ -254,10 +254,23 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
*/
void AddAllObjectsIncludeFiles();

/**
* \brief Generate the list of parameters of a function.
*
* \note runtimeScene is always added as the first parameter, and
* parentEventsFunctionContext as the last parameter.
*/
gd::String GenerateEventsFunctionParameterDeclarationsList(
const std::vector<gd::ParameterMetadata>& parameters);

/**
* \brief Generate the "eventsFunctionContext" object that allow a function
* to provides access objects, object creation and access to arguments from
* the rest of the events.
*/
gd::String GenerateEventsFunctionContext(
const std::vector<gd::ParameterMetadata>& parameters);

gd::String GenerateEventsFunctionReturn(
const gd::EventsFunction & eventFunction);

Expand Down
22 changes: 15 additions & 7 deletions GDJS/GDJS/Extensions/Builtin/AdvancedExtension.cpp
Expand Up @@ -39,7 +39,9 @@ AdvancedExtension::AdvancedExtension() {
expressionCode = "0";
}

return "eventsFunctionContext.returnValue = " + expressionCode + ";";
return "if (typeof eventsFunctionContext !== 'undefined') { "
"eventsFunctionContext.returnValue = " +
expressionCode + "; }";
});

GetAllActions()["SetReturnString"]
Expand All @@ -62,7 +64,9 @@ AdvancedExtension::AdvancedExtension() {
expressionCode = "\"\"";
}

return "eventsFunctionContext.returnValue = " + expressionCode + ";";
return "if (typeof eventsFunctionContext !== 'undefined') { "
"eventsFunctionContext.returnValue = " +
expressionCode + "; }";
});

GetAllActions()["SetReturnBoolean"]
Expand All @@ -75,7 +79,9 @@ AdvancedExtension::AdvancedExtension() {
gd::String booleanCode =
(parameter == "True" || parameter == "Vrai") ? "true" : "false";

return "eventsFunctionContext.returnValue = " + booleanCode + ";";
return "if (typeof eventsFunctionContext !== 'undefined') { "
"eventsFunctionContext.returnValue = " +
booleanCode + "; }";
});

auto generateParameterNameCode =
Expand Down Expand Up @@ -109,8 +115,9 @@ AdvancedExtension::AdvancedExtension() {
gd::String parameterNameCode =
generateParameterNameCode(parameters, codeGenerator, context);

return "(Number(eventsFunctionContext.getArgument(" +
parameterNameCode + ")) || 0)";
return "(typeof eventsFunctionContext !== 'undefined' ? "
"Number(eventsFunctionContext.getArgument(" +
parameterNameCode + ")) || 0 : 0)";
});

GetAllExpressions()["GetArgumentAsString"]
Expand All @@ -122,8 +129,9 @@ AdvancedExtension::AdvancedExtension() {
gd::String parameterNameCode =
generateParameterNameCode(parameters, codeGenerator, context);

return "(\"\" + eventsFunctionContext.getArgument(" +
parameterNameCode + "))";
return "(typeof eventsFunctionContext !== 'undefined' ? \"\" + "
"eventsFunctionContext.getArgument(" +
parameterNameCode + ") : \"\")";
});
}

Expand Down
91 changes: 54 additions & 37 deletions GDJS/Runtime/events-tools/objecttools.js
Expand Up @@ -19,13 +19,18 @@ gdjs.evtTools.object = gdjs.evtTools.object || {};
* @param runtimeObject {gdjs.RuntimeObject} The object to keep in the lists
*/
gdjs.evtTools.object.pickOnly = function(objectsLists, runtimeObject) {
var lists = gdjs.staticArray(gdjs.evtTools.object.pickOnly);
objectsLists.values(lists);

for(var i = 0, len = lists.length;i<len;++i)
lists[i].length = 0; //Be sure not to lose the reference to the original array

objectsLists.get(runtimeObject.getName()).push(runtimeObject);
for (var listName in objectsLists.items) {
if (objectsLists.items.hasOwnProperty(listName)) {
var list = objectsLists.items[listName];

if (list.indexOf(runtimeObject) === -1) {
list.length = 0; //Be sure not to lose the reference to the original array
} else {
list.length = 0; //Be sure not to lose the reference to the original array
list.push(runtimeObject);
}
}
}
};

/**
Expand Down Expand Up @@ -237,11 +242,11 @@ gdjs.evtTools.object.turnedTowardTest = function(objectsLists1, objectsLists2, t
objectsLists1, objectsLists2, inverted, tolerance);
};

gdjs.evtTools.object.pickAllObjects = function(runtimeScene, objectsLists) {
gdjs.evtTools.object.pickAllObjects = function(objectsContext, objectsLists) {

for (var name in objectsLists.items) {
if (objectsLists.items.hasOwnProperty(name)) {
var allObjects = runtimeScene.getObjects(name);
var allObjects = objectsContext.getObjects(name);
var objectsList = objectsLists.items[name];
objectsList.length = 0;
objectsList.push.apply(objectsList, allObjects);
Expand All @@ -252,29 +257,39 @@ gdjs.evtTools.object.pickAllObjects = function(runtimeScene, objectsLists) {
};

gdjs.evtTools.object.pickRandomObject = function(runtimeScene, objectsLists) {

//Create a list with all the objects
//and clear the lists of picked objects.
var objects = gdjs.staticArray(gdjs.evtTools.object.pickRandomObject);
objects.length = 0;

var lists = gdjs.staticArray2(gdjs.evtTools.object.pickRandomObject);
objectsLists.values(lists);
for(var i = 0, len = lists.length;i<len;++i) {
objects.push.apply(objects, lists[i]);
lists[i].length = 0; //Be sure not to lose the reference to the original array
// Compute one many objects we have
var objectsCount = 0;
for (var listName in objectsLists.items) {
if (objectsLists.items.hasOwnProperty(listName)) {
var list = objectsLists.items[listName];
objectsCount += list.length;
}
}

//Pick only one object
if ( objects.length === 0 )

if (objectsCount === 0)
return false;

// Pick one random object
var index = Math.floor(Math.random()*objectsCount);
if (index >= objectsCount) index = objectsCount-1; //Should never happen.

// Find the object
var startIndex = 0;
var theChosenOne = null;
for (var listName in objectsLists.items) {
if (objectsLists.items.hasOwnProperty(listName)) {
var list = objectsLists.items[listName];

if (index - startIndex < list.length) {
theChosenOne = list[index - startIndex];
break;
}

var id = Math.floor(Math.random()*objects.length);
if (id >= objects.length) id = objects.length-1; //Should never happen.
var theChosenOne = objects[id];

objectsLists.get(theChosenOne.getName()).push(theChosenOne);

startIndex += list.length;
}
}

gdjs.evtTools.object.pickOnly(objectsLists, theChosenOne);
return true;
};

Expand Down Expand Up @@ -360,10 +375,12 @@ gdjs.evtTools.object.raycastObjectToPosition = function(objectsLists, x, y, endX
* Do the work of creating a new object
* @private
*/
gdjs.evtTools.object.doCreateObjectOnScene = function(runtimeScene, objectName, objectsLists, x, y, layer) {

//Let's ask the RuntimeScene to create the object
var obj = runtimeScene.createObject(objectName);
gdjs.evtTools.object.doCreateObjectOnScene = function(objectsContext, objectName, objectsLists, x, y, layer) {
// objectsContext will either be the gdjs.RuntimeScene or, in an events function, the
// eventsFunctionContext. We can't directly use runtimeScene because the object name could
// be different than the real object name (this is the case in a function. The eventsFunctionContext
// will take care of this in createObject).
var obj = objectsContext.createObject(objectName);

if ( obj !== null ) {
//Do some extra setup
Expand All @@ -381,16 +398,16 @@ gdjs.evtTools.object.doCreateObjectOnScene = function(runtimeScene, objectName,
* Allows events to create a new object on a scene.
* @private
*/
gdjs.evtTools.object.createObjectOnScene = function(runtimeScene, objectsLists, x, y, layer) {
gdjs.evtTools.object.doCreateObjectOnScene(runtimeScene, objectsLists.firstKey(), objectsLists, x, y, layer);
gdjs.evtTools.object.createObjectOnScene = function(objectsContext, objectsLists, x, y, layer) {
gdjs.evtTools.object.doCreateObjectOnScene(objectsContext, objectsLists.firstKey(), objectsLists, x, y, layer);
};

/**
* Allows events to create a new object on a scene.
* @private
*/
gdjs.evtTools.object.createObjectFromGroupOnScene = function(runtimeScene, objectsLists, objectName, x, y, layer) {
gdjs.evtTools.object.doCreateObjectOnScene(runtimeScene, objectName, objectsLists, x, y, layer);
gdjs.evtTools.object.createObjectFromGroupOnScene = function(objectsContext, objectsLists, objectName, x, y, layer) {
gdjs.evtTools.object.doCreateObjectOnScene(objectsContext, objectName, objectsLists, x, y, layer);
};

/**
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9f5b63b

Please sign in to comment.