Compare commits

...

9 Commits

Author SHA1 Message Date
Davy Hélard
66b0cb6349 Fix the mock. 2023-11-13 21:35:16 +01:00
Davy Hélard
e533637c1e Fix tests. 2023-11-13 20:29:05 +01:00
Davy Hélard
4546ad1a8a Fix types. 2023-11-13 19:07:37 +01:00
Davy Hélard
873502eaf6 Fix after a rebase. 2023-11-13 13:59:29 +01:00
Davy Hélard
733935ecd1 Unoptimized isPicked flag on ObjectsLists. 2023-11-13 13:49:18 +01:00
Davy Hélard
336a40fe1c Fix the mock. 2023-11-13 13:40:52 +01:00
Davy Hélard
5dee9da344 Format 2023-11-13 13:40:51 +01:00
Davy Hélard
6522ac74c3 Update tests. 2023-11-13 13:40:51 +01:00
Davy Hélard
ad3fda1a3f Fix the "create" action to picking in functions 2023-11-13 13:38:23 +01:00
17 changed files with 357 additions and 98 deletions

View File

@@ -299,6 +299,22 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
AddIncludeFiles(instrInfos.GetIncludeFiles());
maxConditionsListsSize =
std::max(maxConditionsListsSize, condition.GetSubInstructions().size());
// Flag the ObjectsLists as modified.
gd::ParameterMetadataTools::IterateOverParameters(
condition.GetParameters(), instrInfos.parameters,
[this, &context,
&conditionCode](const gd::ParameterMetadata &parameterMetadata,
const gd::Expression &parameterValue,
const gd::String &lastObjectName) {
if (parameterMetadata.GetType() == "objectList" ||
parameterMetadata.GetType() == "objectListOrEmptyIfJustDeclared" ||
parameterMetadata.GetType() == "objectListOrEmptyWithoutPicking") {
conditionCode +=
GetObjectMapName(parameterValue.GetPlainString(), context) +
".isPicked = true;\n";
}
});
if (instrInfos.HasCustomCodeGenerator()) {
context.EnterCustomCondition();

View File

@@ -585,6 +585,12 @@ class GD_CORE_API EventsCodeGenerator {
return "fakeObjectListOf_" + objectName;
}
// TODO Documentation
virtual gd::String GetObjectMapName(const gd::String &objectName,
gd::EventsCodeGenerationContext &context) {
return "fakeObjectListOf_" + objectName;
}
virtual gd::String GeneratePropertyGetter(
const gd::PropertiesContainer& propertiesContainer,
const gd::NamedPropertyDescriptor& property,

View File

@@ -1375,7 +1375,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/create24.png",
"res/actions/create24.png")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter("objectListOrEmptyIfJustDeclared", _("Object to create"))
.AddParameter("objectList", _("Object to create"))
.AddParameter("expression", _("X position"))
.AddParameter("expression", _("Y position"))
.AddParameter("layer", _("Layer"), "", true)
@@ -1393,7 +1393,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/create24.png",
"res/actions/create24.png")
.AddCodeOnlyParameter("objectsContext", "")
.AddParameter("objectListOrEmptyIfJustDeclared",
.AddParameter("objectList",
_("Group of potential objects"))
.SetParameterLongDescription(
_("Group containing objects that can be created by the action."))

View File

@@ -1,9 +1,9 @@
namespace gdjs {
export namespace physics2 {
export const objectsCollide = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
objectsLists1: ObjectsLists,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,
objectsLists2: ObjectsLists,
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(
@@ -16,9 +16,9 @@ namespace gdjs {
};
export const haveObjectsStartedColliding = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
objectsLists1: ObjectsLists,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,
objectsLists2: ObjectsLists,
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(
@@ -31,9 +31,9 @@ namespace gdjs {
};
export const haveObjectsStoppedColliding = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
objectsLists1: ObjectsLists,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,
objectsLists2: ObjectsLists,
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(
@@ -45,7 +45,11 @@ namespace gdjs {
);
};
export const setTimeScale = function (objectsLists, behavior, timeScale) {
export const setTimeScale = function (
objectsLists: ObjectsLists,
behavior: gdjs.Physics2RuntimeBehavior,
timeScale: float
) {
const lists = gdjs.staticArray(gdjs.physics2.setTimeScale);
objectsLists.values(lists);
for (let i = 0, len = lists.length; i < len; i++) {

View File

@@ -2,9 +2,9 @@ namespace gdjs {
export namespace evtTools {
export namespace platform {
export const isOnPlatform = function (
objectsLists1: Hashtable<Array<gdjs.RuntimeObject>>,
objectsLists1: ObjectsLists,
behaviorName: string,
objectsLists2: Hashtable<Array<gdjs.RuntimeObject>>,
objectsLists2: ObjectsLists,
inverted: boolean
) {
return gdjs.evtTools.object.twoListsTest(

View File

@@ -568,22 +568,21 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
// to create the new object as the object names used in the function
// are not the same as the objects available in the scene.
" createObject: function(objectName) {\n"
" const objectsList = "
" const objectsLists = "
"eventsFunctionContext._objectsMap[objectName];\n" +
// TODO: we could speed this up by storing a map of object names, but
// the cost of creating/storing it for each events function might not
// be worth it.
" if (objectsList) {\n" +
" if (objectsLists) {\n" +
" const object = parentEventsFunctionContext ?\n" +
" "
"parentEventsFunctionContext.createObject(objectsList.firstKey()) "
"parentEventsFunctionContext.createObject(objectsLists.firstKey()) "
":\n" +
" runtimeScene.createObject(objectsList.firstKey());\n" +
" runtimeScene.createObject(objectsLists.firstKey());\n" +
// Add the new instance to object lists
" if (object) {\n" +
" objectsList.get(objectsList.firstKey()).push(object);\n" +
" "
"eventsFunctionContext._objectArraysMap[objectName].push(object);\n" +
" if (object) {\n"
" gdjs.evtTools.objectsLists.addObject(objectsLists, "
"objectsLists.firstKey(), object);\n" +
" }\n" + " return object;" + " }\n" +
// Unknown object, don't create anything:
" return null;\n" +
@@ -767,6 +766,9 @@ gd::String EventsCodeGenerator::GenerateObjectCondition(
}
if (conditionInverted) predicate = GenerateNegatedPredicate(predicate);
// Flag the picking list as modified.
conditionCode += GetObjectMapName(objectName, context) + ".isPicked = true;\n";
// Generate whole condition code
conditionCode +=
"for (var i = 0, k = 0, l = " + GetObjectListName(objectName, context) +
@@ -823,6 +825,9 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
<< "\" requested for object \'" << objectName
<< "\" (condition: " << instrInfos.GetFullName() << ")." << endl;
} else {
// Flag the picking list as modified.
conditionCode += GetObjectMapName(objectName, context) + ".isPicked = true;\n";
conditionCode +=
"for (var i = 0, k = 0, l = " + GetObjectListName(objectName, context) +
".length;i<l;++i) {\n";
@@ -1021,7 +1026,8 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
gd::String copiedListName = "asyncObjectsList.getObjects(" +
ConvertToStringExplicit(object) + ")";
return "gdjs.copyArray(" + copiedListName + ", " + objectListName +
");\n";
");\n" +
GenerateObject(object, "objectList", context) + ".isPicked = false;\n";
}
//*Optimization*: Avoid expensive copy of the object list if we're using
@@ -1032,7 +1038,8 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
gd::String copiedListName =
GetObjectListName(object, *context.GetParentContext());
return "gdjs.copyArray(" + copiedListName + ", " + objectListName +
");\n";
");\n" +
GenerateObject(object, "objectList", context) + ".isPicked = false;\n";
};
gd::String declarationsCode;
@@ -1041,7 +1048,8 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
if (!context.ObjectAlreadyDeclaredByParents(object)) {
objectListDeclaration += "gdjs.copyArray(" +
GenerateAllInstancesGetterCode(object, context) +
", " + GetObjectListName(object, context) + ");";
", " + GetObjectListName(object, context) + ");\n" +
GenerateObject(object, "objectList", context) + ".isPicked = false;\n";
} else
objectListDeclaration = declareObjectListFromParent(object, context);
@@ -1051,7 +1059,8 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
gd::String objectListDeclaration = "";
if (!context.ObjectAlreadyDeclaredByParents(object)) {
objectListDeclaration =
GetObjectListName(object, context) + ".length = 0;\n";
GetObjectListName(object, context) + ".length = 0;\n" +
GenerateObject(object, "objectList", context) + ".isPicked = false;\n";
} else
objectListDeclaration = declareObjectListFromParent(object, context);
@@ -1061,10 +1070,12 @@ gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(
gd::String objectListDeclaration = "";
if (!context.ObjectAlreadyDeclaredByParents(object)) {
objectListDeclaration =
GetObjectListName(object, context) + ".length = 0;\n";
GetObjectListName(object, context) + ".length = 0;\n" +
GenerateObject(object, "objectList", context) + ".isPicked = false;\n";
} else
objectListDeclaration =
GetObjectListName(object, context) + ".length = 0;\n";
GetObjectListName(object, context) + ".length = 0;\n" +
GenerateObject(object, "objectList", context) + ".isPicked = false;\n";
declarationsCode += objectListDeclaration + "\n";
}
@@ -1183,6 +1194,28 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
return argOutput;
}
gd::String EventsCodeGenerator::GetObjectMapName(
const gd::String& objectName,
gd::EventsCodeGenerationContext& context) {
// TODO Why can't it just use objectName directly?
std::vector<gd::String> realObjects =
GetObjectsContainersList().ExpandObjectName(objectName,
context.GetCurrentObject());
// The map name must be unique for each set of objects lists.
// We generate it from the objects lists names.
gd::String objectsMapName = GetCodeNamespaceAccessor() + "mapOf";
// Map each declared object to its list.
for (auto &objectName : realObjects) {
objectsMapName += ManObjListName(GetObjectListName(objectName, context));
}
// TODO Handle objectListOrEmptyWithoutPicking?
return objectsMapName;
}
gd::String EventsCodeGenerator::GenerateObject(
const gd::String& objectName,
const gd::String& type,

View File

@@ -335,6 +335,9 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
const gd::String& type,
gd::EventsCodeGenerationContext& context) override;
virtual gd::String GetObjectMapName(const gd::String &objectName,
gd::EventsCodeGenerationContext &context) override;
virtual gd::String GenerateNegatedPredicate(const gd::String& predicate) const override {
return "!(" + predicate + ")";
};

View File

@@ -614,6 +614,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
// First, do not forget common includes (they must be included before events
// generated code files).
InsertUnique(includesFiles, "libs/jshashtable.js");
InsertUnique(includesFiles, "ObjectsLists.js");
InsertUnique(includesFiles, "logger.js");
InsertUnique(includesFiles, "gd.js");
InsertUnique(includesFiles, "libs/rbush.js");

View File

@@ -0,0 +1,58 @@
/*
* GDevelop JS Platform
* Copyright 2013-2023 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
* This project is released under the MIT License.
*/
/**
* Picked objects lists.
*/
class ObjectsLists extends Hashtable<Array<gdjs.RuntimeObject>> {
/** Is true as soon as an instruction has pick some or every instances */
isPicked: boolean = false;
}
namespace gdjs {
export namespace evtTools {
export namespace objectsLists {
/**
* Construct a ObjectsLists from a JS object.
*
* @param items The content of the Hashtable.
* @returns The new picked objects lists.
*/
export const newFrom = (
items: {
[key: string]: Array<gdjs.RuntimeObject>;
},
isPicked: boolean
): ObjectsLists => {
const hashtable = new ObjectsLists();
hashtable.items = items;
hashtable.isPicked = isPicked;
return hashtable;
};
export const addObject = (
objectsLists: ObjectsLists,
objectName: string,
object: gdjs.RuntimeObject
) => {
if (!objectsLists.isPicked) {
// A picking starts from empty lists.
clearObjectsLists(objectsLists);
objectsLists.isPicked = true;
}
objectsLists.get(objectName).push(object);
};
const clearObjectsLists = (objectsLists: ObjectsLists) => {
for (const k in objectsLists.items) {
if (objectsLists.items.hasOwnProperty(k)) {
objectsLists.items[k].length = 0;
}
}
};
}
}
}

View File

@@ -388,7 +388,7 @@ namespace gdjs {
};
export const cursorOnObject = function (
objectsLists: Hashtable<gdjs.RuntimeObject[]>,
objectsLists: ObjectsLists,
instanceContainer: gdjs.RuntimeInstanceContainer,
accurate: boolean,
inverted: boolean

View File

@@ -496,14 +496,14 @@ namespace gdjs {
const obj = objectsContext.createObject(objectName);
const layer = objectsContext.getLayer(layerName);
if (obj !== null) {
//Do some extra setup
// Do some extra setup
obj.setPosition(x, y);
obj.setLayer(layerName);
obj.setZOrder(layer.getDefaultZOrder());
//Let the new object be picked by next actions/conditions.
// Let the new object be picked by next actions/conditions.
if (objectsLists.containsKey(objectName)) {
objectsLists.get(objectName).push(obj);
gdjs.evtTools.objectsLists.addObject(objectsLists, objectName, obj);
}
}
return obj;

View File

@@ -13,9 +13,6 @@ declare type float = number;
/** A point in cartesian space. */
declare type FloatPoint = [number, number];
/** A Hastable with the picked objects lists. */
declare type ObjectsLists = Hashtable<gdjs.RuntimeObject[]>;
/**
* Represents the context of the events function (or the behavior method),
* if any. If the JavaScript code is running in a scene, this will be undefined (so you can't use this in a scene).

View File

@@ -36,6 +36,7 @@ module.exports = function (config) {
//GDJS game engine files: (Order is important)
'./newIDE/app/resources/GDJS/Runtime/libs/jshashtable.js',
'./newIDE/app/resources/GDJS/Runtime/ObjectsLists.js',
'./newIDE/app/resources/GDJS/Runtime/logger.js',
'./newIDE/app/resources/GDJS/Runtime/gd.js',
'./newIDE/app/resources/GDJS/Runtime/AsyncTasksManager.js',

View File

@@ -13,28 +13,37 @@ describe('gdjs.evtTools.object', function () {
expect(
gdjs.evtTools.object.getPickedInstancesCount(
Hashtable.newFrom({
MyObjectA: [objectA1, objectA2],
MyObjectB: [objectB1],
})
gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [objectA1, objectA2],
MyObjectB: [objectB1],
},
true
)
)
).to.be(3);
expect(
gdjs.evtTools.object.getPickedInstancesCount(
Hashtable.newFrom({
MyObjectA: [],
MyObjectB: [],
})
gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [],
MyObjectB: [],
},
true
)
)
).to.be(0);
// Also test the deprecated name for this function:
expect(
gdjs.evtTools.object.pickedObjectsCount(
Hashtable.newFrom({
MyObjectA: [objectA1, objectA2],
MyObjectB: [objectB1],
})
gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [objectA1, objectA2],
MyObjectB: [objectB1],
},
true
)
)
).to.be(3);
});
@@ -52,43 +61,58 @@ describe('gdjs.evtTools.object', function () {
expect(
gdjs.evtTools.object.getSceneInstancesCount(
runtimeScene,
Hashtable.newFrom({
MyObjectA: [objectA1],
MyObjectB: [objectB1],
})
gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [objectA1],
MyObjectB: [objectB1],
},
true
)
)
).to.be(2 + 1);
expect(
gdjs.evtTools.object.getSceneInstancesCount(
runtimeScene,
Hashtable.newFrom({
MyObjectA: [objectA1],
MyObjectB: [],
})
gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [objectA1],
MyObjectB: [],
},
true
)
)
).to.be(2 + 1);
expect(
gdjs.evtTools.object.getSceneInstancesCount(
runtimeScene,
Hashtable.newFrom({
MyObjectA: [objectA1],
})
gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [objectA1],
},
true
)
)
).to.be(2);
expect(
gdjs.evtTools.object.getSceneInstancesCount(
runtimeScene,
Hashtable.newFrom({
MyObjectA: [],
})
gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [],
},
false
)
)
).to.be(2);
expect(
gdjs.evtTools.object.getSceneInstancesCount(
runtimeScene,
Hashtable.newFrom({
MyObjectC: [],
})
gdjs.evtTools.objectsLists.newFrom(
{
MyObjectC: [],
},
false
)
)
).to.be(0);
});
@@ -106,9 +130,12 @@ describe('gdjs.evtTools.object', function () {
runtimeScene.createObject('MyObjectA');
// 1 of 2 instances are picked.
const pickedObjectList = Hashtable.newFrom({
MyObjectA: [objectA1],
});
const pickedObjectList = gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [objectA1],
},
true
);
const newObjectA = gdjs.evtTools.object.createObjectOnScene(
runtimeScene,
@@ -134,9 +161,12 @@ describe('gdjs.evtTools.object', function () {
runtimeScene.createObject('MyObjectA');
// 0 of 2 instances are picked.
const pickedObjectList = Hashtable.newFrom({
MyObjectA: [],
});
const pickedObjectList = gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [],
},
true
);
const newObjectA = gdjs.evtTools.object.createObjectOnScene(
runtimeScene,
@@ -152,7 +182,7 @@ describe('gdjs.evtTools.object', function () {
);
});
it('can create an instance and keep all instances picked', function () {
it('can create an instance and only pick this one when all instances were picked', function () {
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.TestRuntimeScene(runtimeGame);
@@ -161,9 +191,12 @@ describe('gdjs.evtTools.object', function () {
const objectA2 = runtimeScene.createObject('MyObjectA');
// All instances are picked.
const pickedObjectList = Hashtable.newFrom({
MyObjectA: [objectA1, objectA2],
});
const pickedObjectList = gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [objectA1, objectA2],
},
false
);
const newObjectA = gdjs.evtTools.object.createObjectOnScene(
runtimeScene,
@@ -173,9 +206,9 @@ describe('gdjs.evtTools.object', function () {
''
);
// All instances are still picked.
// Only the created instance is picked.
expect(getInstancesIds(pickedObjectList.get('MyObjectA'))).to.eql(
getInstancesIds([objectA1, objectA2, newObjectA])
getInstancesIds([newObjectA])
);
});
@@ -191,10 +224,13 @@ describe('gdjs.evtTools.object', function () {
runtimeScene.createObject('MyObjectB');
// 2 of 3 instances are picked.
const pickedObjectList = Hashtable.newFrom({
MyObjectA: [objectA1],
MyObjectB: [objectB1],
});
const pickedObjectList = gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [objectA1],
MyObjectB: [objectB1],
},
true
);
const newObjectA = gdjs.evtTools.object.createObjectOnScene(
runtimeScene,
@@ -213,7 +249,7 @@ describe('gdjs.evtTools.object', function () {
);
});
it('can create an instance and keep all instances picked for a group', function () {
it('can create an instance and only pick this one when all instances were picked for a group', function () {
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.TestRuntimeScene(runtimeGame);
@@ -224,10 +260,14 @@ describe('gdjs.evtTools.object', function () {
const objectB1 = runtimeScene.createObject('MyObjectB');
// All instances are picked.
const pickedObjectList = Hashtable.newFrom({
MyObjectA: [objectA1, objectA2],
MyObjectB: [objectB1],
});
/** @type {ObjectsLists} */
const pickedObjectList = gdjs.evtTools.objectsLists.newFrom(
{
MyObjectA: [objectA1, objectA2],
MyObjectB: [objectB1],
},
false
);
const newObjectA = gdjs.evtTools.object.createObjectOnScene(
runtimeScene,
@@ -237,12 +277,12 @@ describe('gdjs.evtTools.object', function () {
''
);
// All instances are still picked.
// Only the created instance is picked.
expect(getInstancesIds(pickedObjectList.get('MyObjectA'))).to.eql(
getInstancesIds([objectA1, objectA2, newObjectA])
getInstancesIds([newObjectA])
);
expect(getInstancesIds(pickedObjectList.get('MyObjectB'))).to.eql(
getInstancesIds([objectB1])
getInstancesIds([])
);
});
});

View File

@@ -522,17 +522,59 @@ const copyArray = function (src, dst) {
dst.length = len;
};
const createObjectOnScene = (objectsContext, objectsLists, x, y, layer) => {
const objectName = objectsLists.firstKey();
/**
* @param {any} objectsContext
* @param {string} objectName
* @param {Hashtable<RuntimeObject[]>} objectsLists
* @param {number} x
* @param {number} y
* @param {string} layer
*/
const doCreateObjectOnScene = function (
objectsContext,
objectName,
objectsLists,
x,
y,
layerName
) {
// 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).
const obj = objectsContext.createObject(objectName);
if (obj !== null) {
// Ignore position and layer set up of the object as we're in a minimal mock of GDJS.
// Let the new object be picked by next actions/conditions.
//Let the new object be picked by next actions/conditions.
if (objectsLists.containsKey(objectName)) {
// TODO Encapsulate this in ObjectsLists
if (
getPickedInstancesCount(objectsLists) + 1 ===
getVisibleInstancesCount(objectsContext, objectsLists)
) {
clearObjectLists(objectsLists);
}
objectsLists.get(objectName).push(obj);
}
}
return obj;
};
/**
* @param {any} objectsContext
* @param {Hashtable<RuntimeObject[]>} objectsLists
* @param {number} x
* @param {number} y
* @param {string} layerName
*/
const createObjectOnScene = (objectsContext, objectsLists, x, y, layerName) => {
return doCreateObjectOnScene(
objectsContext,
objectsLists.firstKey(),
objectsLists,
x,
y,
layerName
);
};
/**
@@ -565,6 +607,37 @@ const getPickedInstancesCount = (objectsLists) => {
return count;
};
/**
* @param {any} objectsContext
* @param {Hashtable<RuntimeObject[]>} objectsLists
*/
const getVisibleInstancesCount = (objectsContext, objectsLists) => {
let count = 0;
const objectNames = [];
objectsLists.keys(objectNames);
const uniqueObjectNames = new Set(objectNames);
for (const objectName of uniqueObjectNames) {
const visibleObjects = objectsContext.getObjects(objectName);
if (visibleObjects) {
count += visibleObjects.length;
}
}
return count;
};
/**
* @param {Hashtable<RuntimeObject[]>} objectsLists
*/
const clearObjectLists = (objectsLists) => {
const lists = [];
objectsLists.values(lists);
for (let i = 0, len = lists.length; i < len; ++i) {
lists[i].length = 0;
}
};
/** A minimal implementation of gdjs.RuntimeScene for testing. */
class RuntimeScene {
constructor(sceneData) {
@@ -706,6 +779,27 @@ class LongLivedObjectsList {
}
}
const clearObjectsLists = (objectsLists) => {
for (const k in objectsLists.items) {
if (objectsLists.items.hasOwnProperty(k)) {
objectsLists.items[k].length = 0;
}
}
};
const addObject = (
objectsLists,
objectName,
object
) => {
if (!objectsLists.isPicked) {
// A picking starts from empty lists.
clearObjectsLists(objectsLists);
objectsLists.isPicked = true;
}
objectsLists.get(objectName).push(object);
};
/**
* Create a minimal mock of GDJS with a RuntimeScene (`gdjs.RuntimeScene`),
* supporting setting a variable, using "Trigger Once" conditions
@@ -729,6 +823,8 @@ function makeMinimalGDJSMock(options) {
createObjectOnScene,
getSceneInstancesCount,
getPickedInstancesCount,
getVisibleInstancesCount,
clearObjectLists,
},
runtimeScene: {
wait: () => new FakeAsyncTask(),
@@ -737,6 +833,9 @@ function makeMinimalGDJSMock(options) {
common: {
resolveAsyncEventsFunction: ({ task }) => task.resolve(),
},
objectsLists: {
addObject,
}
},
registerBehavior: (behaviorTypeName, Ctor) => {
behaviorCtors[behaviorTypeName] = Ctor;

View File

@@ -36,7 +36,7 @@ describe('libGD.js - GDJS Code Generation integration tests', () => {
});
};
it('can create an instance and keep all instances picked', function () {
it('can create an instance and only pick this one when all instances were picked', function () {
const runCompiledEvents = generateFunctionWithCreateAction(gd);
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
@@ -51,8 +51,8 @@ describe('libGD.js - GDJS Code Generation integration tests', () => {
runCompiledEvents(gdjs, runtimeScene, [myObjectLists]);
// All instances are still picked.
expect(myObjectLists.get('MyObject').length).toBe(3);
// Only the created instance is picked.
expect(myObjectLists.get('MyObject').length).toBe(1);
});
it('can create and pick an instance when some instances were not picked', function () {
@@ -101,7 +101,7 @@ describe('libGD.js - GDJS Code Generation integration tests', () => {
expect(myObjectLists.get('MyObject').length).toBe(1);
});
it('can create an instance and keep all instances picked of a group', function () {
it('can create an instance and only pick this one when all instances were picked for a group', function () {
const runCompiledEvents = generateFunctionWithCreateAction(gd);
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
@@ -118,9 +118,9 @@ describe('libGD.js - GDJS Code Generation integration tests', () => {
runCompiledEvents(gdjs, runtimeScene, [myObjectLists]);
// All instances are still picked.
expect(myObjectLists.get('MyObjectA').length).toBe(3);
expect(myObjectLists.get('MyObjectB').length).toBe(1);
// Only the created instance is picked.
expect(myObjectLists.get('MyObjectA').length).toBe(1);
expect(myObjectLists.get('MyObjectB').length).toBe(0);
});
it('can create and pick an instance when some instances of a group were not picked', function () {

View File

@@ -538,7 +538,8 @@ describe('libGD.js - GDJS Code Generation integration tests', function () {
const runCompiledEvents = generateCompiledEventsForEventsFunction(
gd,
project,
eventsFunction
eventsFunction,
false
);
const { gdjs, runtimeScene } = makeMinimalGDJSMock();