Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
11 changes: 11 additions & 0 deletions Core/GDCore/Extensions/Builtin/CommonInstructionsExtension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
126 changes: 97 additions & 29 deletions GDJS/GDJS/Extensions/Builtin/CommonInstructionsExtension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand All @@ -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";

Expand All @@ -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) +
Expand Down Expand Up @@ -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";
Expand All @@ -366,6 +365,73 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
return outputCode;
});

GetAllConditions()["BuiltinCommonInstructions::OncePerInstance"]
.codeExtraInformation
.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<gd::String> 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<l;++i) {\n";
conditionCode +=
" if(" + gd::String(instruction.IsInverted() ? "!" : "") +
codeGenerator.GetObjectListName(objectName, context) +
"[i].getOnceTriggers().triggerOnce(" +
gd::String::From(uniqueId) + ")) {\n";
// Custom code generators "return" true by setting the upper boolean
// to true.
conditionCode += " " +
codeGenerator.GenerateUpperScopeBooleanFullName(
"isConditionTrue", context) +
" = true;\n";
conditionCode +=
" " +
codeGenerator.GetObjectListName(objectName, context) +
"[k] = " + codeGenerator.GetObjectListName(objectName, context) +
"[i];\n";
conditionCode += " ++k;\n";
conditionCode += " }\n";
conditionCode += "}\n";
conditionCode +=
codeGenerator.GetObjectListName(objectName, context) +
".length = k;\n";

context.SetNoCurrentObject();
}

return conditionCode;
});

GetAllEvents()["BuiltinCommonInstructions::While"].SetCodeGenerator(
[](gd::BaseEvent& event_,
gd::EventsCodeGenerator& codeGenerator,
Expand Down Expand Up @@ -417,8 +483,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
outputCode += "if (" + ifPredicate + ") {\n";
outputCode += actionsCode;
outputCode += "\n{ //Subevents: \n";
// TODO: check (and heavily test) if sub events should be generated before
// the call to GenerateObjectsDeclarationCode.
// TODO: check (and heavily test) if sub events should be generated
// before the call to GenerateObjectsDeclarationCode.
outputCode +=
codeGenerator.GenerateEventsListCode(event.GetSubEvents(), context);
outputCode += "} //Subevents end.\n";
Expand Down Expand Up @@ -449,9 +515,9 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
gd::String actionsCode =
codeGenerator.GenerateActionsListCode(event.GetActions(), context);
gd::String ifPredicate = event.GetConditions().empty()
? "true"
: codeGenerator.GenerateBooleanFullName(
"isConditionTrue", context);
? "true"
: codeGenerator.GenerateBooleanFullName(
"isConditionTrue", context);

// Prepare object declaration and sub events
gd::String subevents =
Expand Down Expand Up @@ -629,7 +695,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
"repeatCount" + gd::String::From(context.GetContextDepth());
gd::String repeatIndexVar =
"repeatIndex" + gd::String::From(context.GetContextDepth());
outputCode += "const " + repeatCountVar + " = " + repeatCountCode + ";\n";
outputCode +=
"const " + repeatCountVar + " = " + repeatCountCode + ";\n";
outputCode += "for (let " + repeatIndexVar + " = 0;" + repeatIndexVar +
" < " + repeatCountVar + ";++" + repeatIndexVar + ") {\n";
outputCode += objectDeclaration;
Expand Down Expand Up @@ -667,7 +734,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
// picked again)
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).

for (unsigned int i = 0; i < realObjects.size(); ++i)
context.EmptyObjectsListNeeded(realObjects[i]);
Expand Down
1 change: 1 addition & 0 deletions GDJS/Runtime/CustomRuntimeObjectInstanceContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ namespace gdjs {
} else {
obj.update(this);
}
obj.getOnceTriggers().startNewFrame();
obj.updateTimers(elapsedTime);
obj.stepBehaviorsPreEvents(this);
}
Expand Down
1 change: 1 addition & 0 deletions GDJS/Runtime/RuntimeInstanceContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ namespace gdjs {
} else {
obj.update(this);
}
obj.getOnceTriggers().startNewFrame();
obj.updateTimers(elapsedTime);
allInstancesList[i].stepBehaviorsPreEvents(this);
}
Expand Down
9 changes: 9 additions & 0 deletions GDJS/Runtime/runtimeobject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ namespace gdjs {
*/
pick: boolean = false;

private readonly onceTriggers = new gdjs.OnceTriggers();

//Hit boxes:
protected _defaultHitBoxes: gdjs.Polygon[] = [];
protected hitBoxes: gdjs.Polygon[];
Expand Down Expand Up @@ -378,6 +380,13 @@ namespace gdjs {
return this._runtimeScene;
}

/**
* The object's trigger onces states.
*/
getOnceTriggers() {
return this.onceTriggers;
}

/**
* Called once during the game loop, before events and rendering.
* @param instanceContainer The container the object belongs to.
Expand Down