diff --git a/.vscode/settings.json b/.vscode/settings.json index af5ff1d6fc9d..6bce19147ce8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ // Place your settings in this file to overwrite default and user settings. { "files.associations": { + "*.ldtk": "json", "*.idl": "java", "Fastfile": "ruby", "iosfwd": "cpp", diff --git a/Core/GDCore/Extensions/Builtin/CommonInstructionsExtension.cpp b/Core/GDCore/Extensions/Builtin/CommonInstructionsExtension.cpp index 696dff8d81fe..2beb07b94842 100644 --- a/Core/GDCore/Extensions/Builtin/CommonInstructionsExtension.cpp +++ b/Core/GDCore/Extensions/Builtin/CommonInstructionsExtension.cpp @@ -98,6 +98,17 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension( "res/conditions/once24.png", "res/conditions/once.png"); + extension + .AddCondition("OncePerInstance", + _("Trigger once while true for an object"), + _("Run actions only once per instance of an object every " + "time the previous conditions start matching."), + _("Trigger once per instance of _PARAM0_"), + "", + "res/conditions/once24.png", + "res/conditions/once.png") + .AddParameter("object", _("The objects that should trigger once")); + extension .AddCondition("CompareNumbers", _("Compare two numbers"), diff --git a/GDJS/GDJS/Extensions/Builtin/CommonInstructionsExtension.cpp b/GDJS/GDJS/Extensions/Builtin/CommonInstructionsExtension.cpp index bf9b9fa9949a..fefc905bc0f4 100644 --- a/GDJS/GDJS/Extensions/Builtin/CommonInstructionsExtension.cpp +++ b/GDJS/GDJS/Extensions/Builtin/CommonInstructionsExtension.cpp @@ -68,7 +68,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() { instruction.GetParameter(2).GetPlainString()); gd::String resultingBoolean = - codeGenerator.GenerateUpperScopeBooleanFullName("isConditionTrue", context); + codeGenerator.GenerateUpperScopeBooleanFullName("isConditionTrue", + context); return resultingBoolean + " = (" + value1Code + " " + operatorCode + " " + value2Code + ");\n"; @@ -98,7 +99,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() { instruction.GetParameter(2).GetPlainString()); gd::String resultingBoolean = - codeGenerator.GenerateUpperScopeBooleanFullName("isConditionTrue", context); + codeGenerator.GenerateUpperScopeBooleanFullName("isConditionTrue", + context); return resultingBoolean + " = (" + value1Code + " " + operatorCode + " " + value2Code + ");\n"; @@ -133,12 +135,10 @@ CommonInstructionsExtension::CommonInstructionsExtension() { gd::String conditionsCode = codeGenerator.GenerateConditionsListCode( event.GetConditions(), context); - gd::String ifPredicate = - event.GetConditions().empty() - ? "" - : codeGenerator.GenerateBooleanFullName( - "isConditionTrue", - context); + gd::String ifPredicate = event.GetConditions().empty() + ? "" + : codeGenerator.GenerateBooleanFullName( + "isConditionTrue", context); gd::EventsCodeGenerationContext actionsContext; actionsContext.Reuse(context); @@ -187,8 +187,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() { // The Or "return" true by setting the upper boolean to true. // So, it needs to be initialized to false. conditionsCode += codeGenerator.GenerateUpperScopeBooleanFullName( - "isConditionTrue", parentContext) + - " = false;\n"; + "isConditionTrue", parentContext) + + " = false;\n"; //"OR" condition must declare objects list, but without picking the // objects from the scene. Lists are either empty or come from a // parent event. @@ -199,12 +199,11 @@ CommonInstructionsExtension::CommonInstructionsExtension() { // "MyObject" will both have to declare a "MyObject" object list. gd::EventsCodeGenerationContext context; context.InheritsFrom(parentContext); - context.ForbidReuse(); // TODO: This may not be necessary (to be investigated/heavily tested). + context.ForbidReuse(); // TODO: This may not be necessary (to be + // investigated/heavily tested). gd::String conditionCode = codeGenerator.GenerateConditionCode( - conditions[cId], - "isConditionTrue", - context); + conditions[cId], "isConditionTrue", context); conditionsCode += "{\n"; @@ -216,11 +215,10 @@ CommonInstructionsExtension::CommonInstructionsExtension() { // If the condition is true : merge all objects picked in the // final object lists. - conditionsCode += - "if(" + - codeGenerator.GenerateBooleanFullName( - "isConditionTrue", context) + - ") {\n"; + conditionsCode += "if(" + + codeGenerator.GenerateBooleanFullName( + "isConditionTrue", context) + + ") {\n"; conditionsCode += " " + codeGenerator.GenerateUpperScopeBooleanFullName( "isConditionTrue", context) + @@ -354,9 +352,10 @@ CommonInstructionsExtension::CommonInstructionsExtension() { gd::EventsCodeGenerationContext& context) { size_t uniqueId = codeGenerator.GenerateSingleUsageUniqueIdFor( instruction.GetOriginalInstruction().lock().get()); - gd::String outputCode = codeGenerator.GenerateUpperScopeBooleanFullName( - "isConditionTrue", context) + - " = "; + gd::String outputCode = + codeGenerator.GenerateUpperScopeBooleanFullName( + "isConditionTrue", context) + + " = "; gd::String contextObjectName = codeGenerator.HasProjectAndLayout() ? "runtimeScene" : "eventsFunctionContext"; @@ -366,6 +365,72 @@ CommonInstructionsExtension::CommonInstructionsExtension() { return outputCode; }); + GetAllConditions()["BuiltinCommonInstructions::OncePerInstance"] + .SetCustomCodeGenerator([](gd::Instruction& instruction, + gd::EventsCodeGenerator& codeGenerator, + gd::EventsCodeGenerationContext& context) + -> gd::String { + gd::String objectName = instruction.GetParameter(0).GetPlainString(); + + if (!codeGenerator.GetObjectsAndGroups().HasObjectNamed(objectName) && + !codeGenerator.GetGlobalObjectsAndGroups().HasObjectNamed( + objectName) && + !codeGenerator.GetObjectsAndGroups().GetObjectGroups().Has( + objectName) && + !codeGenerator.GetGlobalObjectsAndGroups().GetObjectGroups().Has( + objectName)) { + return "/* Unknown object - skipped. */"; + } + + size_t uniqueId = codeGenerator.GenerateSingleUsageUniqueIdFor( + instruction.GetOriginalInstruction().lock().get()); + + gd::String conditionCode = ""; + std::vector realObjects = + codeGenerator.ExpandObjectsName(objectName, context); + for (std::size_t i = 0; i < realObjects.size(); ++i) { + // Set up the context + gd::String objectType = + gd::GetTypeOfObject(codeGenerator.GetGlobalObjectsAndGroups(), + codeGenerator.GetObjectsAndGroups(), + realObjects[i]); + + context.SetCurrentObject(realObjects[i]); + context.ObjectsListNeeded(realObjects[i]); + + conditionCode += + "for (var i = 0, k = 0, l = " + + codeGenerator.GetObjectListName(objectName, context) + + ".length;i