Skip to content

Commit ca0db73

Browse files
committed
Comply variants when the EBO is edited or its extension is updated.
1 parent 38dbf32 commit ca0db73

17 files changed

+268
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* GDevelop Core
3+
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
4+
* reserved. This project is released under the MIT License.
5+
*/
6+
#include "EventsBasedObjectVariantHelper.h"
7+
8+
#include "GDCore/Project/EventsBasedObject.h"
9+
#include "GDCore/Project/InitialInstancesContainer.h"
10+
#include "GDCore/Project/Object.h"
11+
#include "GDCore/Project/ObjectGroup.h"
12+
#include "GDCore/Project/ObjectsContainer.h"
13+
#include "GDCore/Project/ObjectsContainersList.h"
14+
#include "GDCore/Project/Project.h"
15+
#include "GDCore/Project/Variable.h"
16+
#include "GDCore/Project/VariablesContainer.h"
17+
#include "GDCore/String.h"
18+
19+
namespace gd {
20+
21+
void EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
22+
const gd::Project &project, gd::EventsBasedObject &eventsBasedObject) {
23+
auto &defaultObjects = eventsBasedObject.GetDefaultVariant().GetObjects();
24+
25+
for (const auto &variant :
26+
eventsBasedObject.GetVariants().GetInternalVector()) {
27+
auto &objects = variant->GetObjects();
28+
29+
// Delete extra objects
30+
for (auto it = objects.GetObjects().begin();
31+
it != objects.GetObjects().end(); ++it) {
32+
const auto &objectName = it->get()->GetName();
33+
if (!defaultObjects.HasObjectNamed(objectName)) {
34+
objects.RemoveObject(objectName);
35+
--it;
36+
}
37+
}
38+
for (const auto &defaultObject : defaultObjects.GetObjects()) {
39+
const auto &objectName = defaultObject->GetName();
40+
const auto &defaultVariables = defaultObject->GetVariables();
41+
const auto &defaultBehaviors = defaultObject->GetAllBehaviorContents();
42+
43+
// Copy missing objects
44+
if (!objects.HasObjectNamed(objectName)) {
45+
objects.InsertObject(*defaultObject,
46+
defaultObjects.GetObjectPosition(objectName));
47+
objects.AddMissingObjectsInRootFolder();
48+
continue;
49+
}
50+
auto &object = objects.GetObject(objectName);
51+
52+
// Copy missing behaviors
53+
auto &behaviors = object.GetAllBehaviorContents();
54+
for (const auto &pair : defaultBehaviors) {
55+
const auto &behaviorName = pair.first;
56+
const auto &defaultBehavior = pair.second;
57+
58+
if (object.HasBehaviorNamed(behaviorName) &&
59+
object.GetBehavior(behaviorName).GetTypeName() !=
60+
defaultBehavior->GetTypeName()) {
61+
object.RemoveBehavior(behaviorName);
62+
}
63+
if (!object.HasBehaviorNamed(behaviorName)) {
64+
auto *behavior = object.AddNewBehavior(
65+
project, defaultBehavior->GetTypeName(), behaviorName);
66+
gd::SerializerElement element;
67+
defaultBehavior->SerializeTo(element);
68+
behavior->UnserializeFrom(element);
69+
}
70+
}
71+
// Delete extra behaviors
72+
for (auto it = behaviors.begin(); it != behaviors.end(); ++it) {
73+
const auto &behaviorName = it->first;
74+
if (!defaultObject->HasBehaviorNamed(behaviorName)) {
75+
object.RemoveBehavior(behaviorName);
76+
--it;
77+
}
78+
}
79+
80+
// Sort and copy missing variables
81+
auto &variables = object.GetVariables();
82+
for (size_t defaultVariableIndex = 0;
83+
defaultVariableIndex < defaultVariables.Count();
84+
defaultVariableIndex++) {
85+
const auto &variableName =
86+
defaultVariables.GetNameAt(defaultVariableIndex);
87+
const auto &defaultVariable =
88+
defaultVariables.Get(defaultVariableIndex);
89+
90+
auto variableIndex = variables.GetPosition(variableName);
91+
if (variableIndex == gd::String::npos) {
92+
variables.Insert(variableName, defaultVariable, defaultVariableIndex);
93+
} else {
94+
variables.Move(variableIndex, defaultVariableIndex);
95+
}
96+
}
97+
// Remove extra variables
98+
auto variableToRemoveCount = variables.Count() - defaultVariables.Count();
99+
for (size_t iteration = 0; iteration < variableToRemoveCount;
100+
iteration++) {
101+
variables.Remove(variables.GetNameAt(variables.Count() - 1));
102+
}
103+
104+
// Remove extra instance variables
105+
variant->GetInitialInstances().IterateOverInstances(
106+
[&objectName,
107+
&defaultVariables](gd::InitialInstance &initialInstance) {
108+
if (initialInstance.GetObjectName() != objectName) {
109+
return;
110+
}
111+
auto &instanceVariables = initialInstance.GetVariables();
112+
for (size_t instanceVariableIndex = 0;
113+
instanceVariableIndex < instanceVariables.Count();
114+
instanceVariableIndex++) {
115+
const auto &variableName =
116+
defaultVariables.GetNameAt(instanceVariableIndex);
117+
118+
if (!defaultVariables.Has(variableName)) {
119+
instanceVariables.Remove(variableName);
120+
instanceVariableIndex--;
121+
}
122+
}
123+
});
124+
}
125+
auto &defaultObjectGroups =
126+
eventsBasedObject.GetDefaultVariant().GetObjects().GetObjectGroups();
127+
auto &objectGroups = variant->GetObjects().GetObjectGroups();
128+
auto objectGroupsCount = objectGroups.Count();
129+
// Clear groups
130+
for (size_t index = 0; index < objectGroupsCount; index++) {
131+
objectGroups.Remove(objectGroups.Get(0).GetName());
132+
}
133+
// Copy groups
134+
for (size_t index = 0; index < defaultObjectGroups.Count(); index++) {
135+
objectGroups.Insert(defaultObjectGroups.Get(index), index);
136+
}
137+
}
138+
}
139+
140+
} // namespace gd
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* GDevelop Core
3+
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
4+
* reserved. This project is released under the MIT License.
5+
*/
6+
#pragma once
7+
8+
namespace gd {
9+
class EventsBasedObject;
10+
class Project;
11+
} // namespace gd
12+
13+
namespace gd {
14+
15+
class GD_CORE_API EventsBasedObjectVariantHelper {
16+
public:
17+
/**
18+
* @brief Apply the changes done on events-based object children to all its
19+
* variants.
20+
*/
21+
static void
22+
ComplyVariantsToEventsBasedObject(const gd::Project &project,
23+
gd::EventsBasedObject &eventsBasedObject);
24+
};
25+
26+
} // namespace gd

GDevelop.js/Bindings/Bindings.idl

+6
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,12 @@ interface ObjectVariableHelper {
378378
[Const, Ref] VariablesChangeset changeset);
379379
};
380380

381+
interface EventsBasedObjectVariantHelper {
382+
void STATIC_ComplyVariantsToEventsBasedObject(
383+
[Ref, Const] Project project,
384+
[Ref] EventsBasedObject eventsBasedObject);
385+
};
386+
381387
interface ObjectGroupsContainer {
382388
void ObjectGroupsContainer();
383389

GDevelop.js/Bindings/Wrapper.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <GDCore/IDE/Events/ExampleExtensionUsagesFinder.h>
4848
#include <GDCore/IDE/EventsFunctionTools.h>
4949
#include <GDCore/IDE/ObjectVariableHelper.h>
50+
#include <GDCore/IDE/EventsBasedObjectVariantHelper.h>
5051
#include <GDCore/IDE/Project/ArbitraryResourceWorker.h>
5152
#include <GDCore/IDE/Project/ArbitraryObjectsWorker.h>
5253
#include <GDCore/IDE/Project/ObjectsUsingResourceCollector.h>
@@ -726,6 +727,7 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
726727
#define STATIC_MergeVariableContainers MergeVariableContainers
727728
#define STATIC_FillAnyVariableBetweenObjects FillAnyVariableBetweenObjects
728729
#define STATIC_ApplyChangesToVariants ApplyChangesToVariants
730+
#define STATIC_ComplyVariantsToEventsBasedObject ComplyVariantsToEventsBasedObject
729731
#define STATIC_RenameEventsFunctionsExtension RenameEventsFunctionsExtension
730732
#define STATIC_UpdateExtensionNameInEventsBasedBehavior \
731733
UpdateExtensionNameInEventsBasedBehavior

GDevelop.js/types.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,10 @@ export class ObjectVariableHelper extends EmscriptenObject {
387387
static applyChangesToVariants(eventsBasedObject: EventsBasedObject, objectName: string, changeset: VariablesChangeset): void;
388388
}
389389

390+
export class EventsBasedObjectVariantHelper extends EmscriptenObject {
391+
static complyVariantsToEventsBasedObject(project: Project, eventsBasedObject: EventsBasedObject): void;
392+
}
393+
390394
export class ObjectGroupsContainer extends EmscriptenObject {
391395
constructor();
392396
has(name: string): boolean;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Automatically generated by GDevelop.js/scripts/generate-types.js
2+
declare class gdEventsBasedObjectVariantHelper {
3+
static complyVariantsToEventsBasedObject(project: gdProject, eventsBasedObject: gdEventsBasedObject): void;
4+
delete(): void;
5+
ptr: number;
6+
};

GDevelop.js/types/libgdevelop.js

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ declare class libGDevelop {
7474
VariablesContainersList: Class<gdVariablesContainersList>;
7575
ObjectGroup: Class<gdObjectGroup>;
7676
ObjectVariableHelper: Class<gdObjectVariableHelper>;
77+
EventsBasedObjectVariantHelper: Class<gdEventsBasedObjectVariantHelper>;
7778
ObjectGroupsContainer: Class<gdObjectGroupsContainer>;
7879
PlatformSpecificAssets: Class<gdPlatformSpecificAssets>;
7980
LoadingScreen: Class<gdLoadingScreen>;

newIDE/app/src/EventsBasedObjectEditor/EventsBasedObjectEditorPanel.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ type Props = {|
2121
onEventsFunctionsAdded: () => void,
2222
onOpenCustomObjectEditor: () => void,
2323
unsavedChanges?: ?UnsavedChanges,
24-
onEventsBasedObjectChildrenEdited: () => void,
24+
onEventsBasedObjectChildrenEdited: (
25+
eventsBasedObject: gdEventsBasedObject
26+
) => void,
2527
|};
2628

2729
export default function EventsBasedObjectEditorPanel({

newIDE/app/src/EventsBasedObjectEditor/index.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ type Props = {|
2323
eventsBasedObject: gdEventsBasedObject,
2424
onOpenCustomObjectEditor: () => void,
2525
unsavedChanges?: ?UnsavedChanges,
26-
onEventsBasedObjectChildrenEdited: () => void,
26+
onEventsBasedObjectChildrenEdited: (
27+
eventsBasedObject: gdEventsBasedObject
28+
) => void,
2729
|};
2830

2931
export default function EventsBasedObjectEditor({
@@ -127,7 +129,7 @@ export default function EventsBasedObjectEditor({
127129
onCheck={(e, checked) => {
128130
eventsBasedObject.markAsInnerAreaFollowingParentSize(checked);
129131
onChange();
130-
onEventsBasedObjectChildrenEdited();
132+
onEventsBasedObjectChildrenEdited(eventsBasedObject);
131133
}}
132134
/>
133135
{isDev && (
@@ -137,7 +139,7 @@ export default function EventsBasedObjectEditor({
137139
onCheck={(e, checked) => {
138140
eventsBasedObject.makAsUsingLegacyInstancesRenderer(checked);
139141
onChange();
140-
onEventsBasedObjectChildrenEdited();
142+
onEventsBasedObjectChildrenEdited(eventsBasedObject);
141143
}}
142144
/>
143145
)}
@@ -147,7 +149,7 @@ export default function EventsBasedObjectEditor({
147149
onCheck={(e, checked) => {
148150
eventsBasedObject.setPrivate(checked);
149151
onChange();
150-
onEventsBasedObjectChildrenEdited();
152+
onEventsBasedObjectChildrenEdited(eventsBasedObject);
151153
}}
152154
tooltipOrHelperText={
153155
eventsBasedObject.isPrivate() ? (

newIDE/app/src/EventsFunctionsExtensionEditor/index.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ type Props = {|
7777
unsavedChanges?: ?UnsavedChanges,
7878
onOpenCustomObjectEditor: gdEventsBasedObject => void,
7979
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
80-
onEventsBasedObjectChildrenEdited: () => void,
80+
onEventsBasedObjectChildrenEdited: (
81+
eventsBasedObject: gdEventsBasedObject
82+
) => void,
8183
onRenamedEventsBasedObject: (
8284
eventsFunctionsExtension: gdEventsFunctionsExtension,
8385
oldName: string,
@@ -778,7 +780,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
778780
// Some custom object instances may target the pasted event-based object name.
779781
// It can happen when an event-based object is deleted and another one is
780782
// pasted to replace it.
781-
this.props.onEventsBasedObjectChildrenEdited();
783+
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject);
782784
};
783785

784786
_onEventsBasedBehaviorRenamed = () => {
@@ -797,7 +799,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
797799
}
798800
};
799801

800-
_onEventsBasedObjectRenamed = () => {
802+
_onEventsBasedObjectRenamed = (eventsBasedObject: gdEventsBasedObject) => {
801803
// Name of an object changed, so notify parent
802804
// that an object was edited (to trigger reload of extensions)
803805
if (this.props.onObjectEdited) {
@@ -814,7 +816,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
814816
// Some custom object instances may target the new event-based object name.
815817
// It can happen when an event-based object is deleted and another one is
816818
// renamed to replace it.
817-
this.props.onEventsBasedObjectChildrenEdited();
819+
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject);
818820
};
819821

820822
_onDeleteEventsBasedBehavior = (
@@ -853,7 +855,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
853855
eventsFunctionsExtension,
854856
eventsBasedObject.getName()
855857
);
856-
onEventsBasedObjectChildrenEdited();
858+
onEventsBasedObjectChildrenEdited(eventsBasedObject);
857859
};
858860

859861
_onCloseExtensionFunctionSelectorDialog = (

newIDE/app/src/MainFrame/EditorContainers/BaseEditor.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ export type RenderEditorContainerProps = {|
137137

138138
// Object editing
139139
openBehaviorEvents: (extensionName: string, behaviorName: string) => void,
140-
onEventsBasedObjectChildrenEdited: () => void,
140+
onEventsBasedObjectChildrenEdited: (
141+
eventsBasedObject: gdEventsBasedObject
142+
) => void,
141143
onSceneObjectEdited: (
142144
scene: gdLayout,
143145
objectWithContext: ObjectWithContext

newIDE/app/src/MainFrame/EditorContainers/CustomObjectEditorContainer.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,12 @@ export class CustomObjectEditorContainer extends React.Component<RenderEditorCon
234234
isActive={isActive}
235235
hotReloadPreviewButtonProps={this.props.hotReloadPreviewButtonProps}
236236
openBehaviorEvents={this.props.openBehaviorEvents}
237-
onObjectEdited={this.props.onEventsBasedObjectChildrenEdited}
237+
onObjectEdited={() =>
238+
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject)
239+
}
240+
onObjectGroupEdited={() =>
241+
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject)
242+
}
238243
onEventsBasedObjectChildrenEdited={
239244
this.props.onEventsBasedObjectChildrenEdited
240245
}

newIDE/app/src/MainFrame/EditorContainers/ExternalLayoutEditorContainer.js

+2
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ export class ExternalLayoutEditorContainer extends React.Component<
263263
onObjectEdited={objectWithContext =>
264264
this.props.onSceneObjectEdited(layout, objectWithContext)
265265
}
266+
// It's only used to refresh events-based object variants.
267+
onObjectGroupEdited={() => {}}
266268
// Nothing to do as events-based objects can't have external layout.
267269
onEventsBasedObjectChildrenEdited={() => {}}
268270
onExtensionInstalled={this.props.onExtensionInstalled}

newIDE/app/src/MainFrame/EditorContainers/SceneEditorContainer.js

+2
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ export class SceneEditorContainer extends React.Component<RenderEditorContainerP
157157
onObjectEdited={objectWithContext =>
158158
this.props.onSceneObjectEdited(layout, objectWithContext)
159159
}
160+
// It's only used to refresh events-based object variants.
161+
onObjectGroupEdited={() => {}}
160162
// Nothing to do as scenes are not events-based objects.
161163
onEventsBasedObjectChildrenEdited={() => {}}
162164
/>

0 commit comments

Comments
 (0)