Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add trigger once per instance #5435

Closed
wants to merge 5 commits into from
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
arthuro555 marked this conversation as resolved.
Show resolved Hide resolved
.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