A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
DlgNode.cpp
Go to the documentation of this file.
1// Copyright Csaba Molnar, Daniel Butum. All Rights Reserved.
2#include "Nodes/DlgNode.h"
3
4#include "Kismet/GameplayStatics.h"
5#include "EngineUtils.h"
6
7#include "DlgContext.h"
8#include "Logging/DlgLogger.h"
11#include "Sound/SoundWave.h"
12
14// Begin UObject interface
15void UDlgNode::Serialize(FArchive& Ar)
16{
17 Super::Serialize(Ar);
18 if (Ar.UE4Ver() >= VER_UE4_COOKED_ASSETS_IN_EDITOR_SUPPORT)
19 {
20 // NOTE: This modifies the Archive
21 // DO NOT REMOVE THIS
22 const FStripDataFlags StripFlags(Ar);
23
24 // Only in editor, add the graph node
25#if WITH_EDITOR
26 if (!StripFlags.IsEditorDataStripped())
27 {
28 Ar << GraphNode;
29 }
30#endif // WITH_EDITOR
31 }
32 else
33 {
34 // Super old version, is this possible?
35#if WITH_EDITOR
36 Ar << GraphNode;
37#endif // WITH_EDITOR
38 }
39}
40
41void UDlgNode::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
42{
43 UDlgNode* This = CastChecked<UDlgNode>(InThis);
44
45 // Add the GraphNode to the referenced objects
46#if WITH_EDITOR
47 Collector.AddReferencedObject(This->GraphNode, This);
48#endif
49
50 Super::AddReferencedObjects(InThis, Collector);
51}
52
54{
55 Super::PostLoad();
56
57 // NOTE: We don't this here but instead we do it in the compile phase
58 // Create thew new GUID
59 // if (!HasGUID())
60 // {
61 // RegenerateGUID();
62 // }
63}
64
66{
67 Super::PostInitProperties();
68
69 // Ignore these cases
70 if (HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad))
71 {
72 return;
73 }
74
75 // GUID is set in the dialogue compile phase
76}
77
78void UDlgNode::PostDuplicate(bool bDuplicateForPIE)
79{
80 Super::PostDuplicate(bDuplicateForPIE);
81
82 // Used when duplicating Nodes.
83 // We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
84 if (HasGUID())
85 {
87 }
88}
89
91{
92 Super::PostEditImport();
93
94 // Used when duplicating Nodes.
95 // We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
96 if (HasGUID())
97 {
99 }
100}
101
102#if WITH_EDITOR
103void UDlgNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
104{
105 Super::PostEditChangeProperty(PropertyChangedEvent);
106
107 // Signal to the listeners
108 OnDialogueNodePropertyChanged.Broadcast(PropertyChangedEvent, BroadcastPropertyEdgeIndexChanged);
109 BroadcastPropertyEdgeIndexChanged = INDEX_NONE;
110}
111
112void UDlgNode::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
113{
114 // The Super::PostEditChangeChainProperty will construct a new FPropertyChangedEvent that will only have the Property and the
115 // MemberProperty name and it will call the PostEditChangeProperty, so we must get the array index of the Nodes modified from here.
116 // If you want to preserve all the change history of the tree you must broadcast the event from here to the children, but be warned
117 // that Property and MemberProperty are not set properly.
118 BroadcastPropertyEdgeIndexChanged = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_STRING_CHECKED(UDlgNode, Children));
119 Super::PostEditChangeChainProperty(PropertyChangedEvent);
120}
121
122
123#endif //WITH_EDITOR
124// End UObject interface
126
128// Begin own function
129bool UDlgNode::HandleNodeEnter(UDlgContext& Context, TSet<const UDlgNode*> NodesEnteredWithThisStep)
130{
131 // Fire all the node enter events
132 FireNodeEnterEvents(Context);
133
134 for (FDlgEdge& Edge : Children)
135 {
136 Edge.RebuildConstructedText(Context, OwnerName);
137 }
138
139 return ReevaluateChildren(Context, {});
140}
141
143{
144 for (const FDlgEvent& Event : EnterEvents)
145 {
146 // Get Participant from either event or parent
147 UObject* Participant = Context.GetMutableParticipant(Event.ParticipantName);
148 if (!IsValid(Participant))
149 {
151 }
152
153 Event.Call(Context, TEXT("FireNodeEnterEvents"), Participant);
154 }
155}
156
157bool UDlgNode::ReevaluateChildren(UDlgContext& Context, TSet<const UDlgNode*> AlreadyEvaluated)
158{
159 TArray<FDlgEdge>& AvailableOptions = Context.GetMutableOptionsArray();
160 TArray<FDlgEdgeData>& AllOptions = Context.GetAllMutableOptionsArray();
161 AvailableOptions.Empty();
162 AllOptions.Empty();
163
164 for (const FDlgEdge& Edge : Children)
165 {
166 const bool bSatisfied = Edge.Evaluate(Context, { this });
167
168 if (bSatisfied || Edge.bIncludeInAllOptionListIfUnsatisfied)
169 {
170 AllOptions.Add(FDlgEdgeData{ bSatisfied, Edge });
171 }
172 if (bSatisfied)
173 {
174 AvailableOptions.Add(Edge);
175 }
176 }
177
178 // no child, but no end node?
179 if (AvailableOptions.Num() == 0)
180 {
182 TEXT("ReevaluateChildren (ReevaluateOptions) - no valid child option for a NODE.\nContext:\n\t%s"),
183 *Context.GetContextString()
184 );
185 return false;
186 }
187
188 return true;
189}
190
191bool UDlgNode::CheckNodeEnterConditions(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
192{
193 if (AlreadyVisitedNodes.Contains(this))
194 {
195 return true;
196 }
197
198 AlreadyVisitedNodes.Add(this);
200 {
201 return false;
202 }
204 {
205 return true;
206 }
207
208 // Has a valid child?
209 return HasAnySatisfiedChild(Context, AlreadyVisitedNodes);
210}
211
212bool UDlgNode::HasAnySatisfiedChild(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
213{
214 for (const FDlgEdge& Edge : Children)
215 {
216 // Found at least one valid child
217 if (Edge.Evaluate(Context, AlreadyVisitedNodes))
218 {
219 return true;
220 }
221 }
222
223 return false;
224}
225
226bool UDlgNode::OptionSelected(int32 OptionIndex, UDlgContext& Context)
227{
228 const TArray<FDlgEdge>& AvailableOptions = Context.GetOptionsArray();
229 if (AvailableOptions.IsValidIndex(OptionIndex))
230 {
231 check(AvailableOptions[OptionIndex].IsValid());
232 return Context.EnterNode(AvailableOptions[OptionIndex].TargetIndex, {});
233 }
234
236 TEXT("OptionSelected - Failed to choose OptionIndex = %d - it only has %d valid options.\nContext:\n\t%s"),
237 OptionIndex, AvailableOptions.Num(), *Context.GetContextString()
238 );
239 return false;
240}
241
243{
244 TArray<int32> OutArray;
245 const int32 EdgesNum = Children.Num();
246 for (int32 EdgeIndex = 0; EdgeIndex < EdgesNum; EdgeIndex++)
247 {
248 if (!Children[EdgeIndex].IsValid())
249 {
250 OutArray.Add(EdgeIndex);
251 }
252 }
253
254 return OutArray;
255}
256
258{
259 for (FDlgEdge& Edge : Children)
260 {
261 if (Edge.TargetIndex == TargetIndex)
262 {
263 return &Edge;
264 }
265 }
266
267 return nullptr;
268}
269
270
272 const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode
273)
274{
275 // We only care about edges here
276 if (bEdges)
277 {
278 const bool bSkipAfterFirstChild = Settings.bSetDefaultEdgeTextOnFirstChildOnly;
279 if (Settings.bSetDefaultEdgeTexts)
280 {
282 for (FDlgEdge& Edge : Children)
283 {
284 Edge.UpdateTextValueFromDefaultAndRemapping(*Dialogue, *this, Settings, false);
285
286 // Set only one, kill the rest
287 if (bSkipAfterFirstChild)
288 {
289 break;
290 }
291 }
292 }
293
294 // Update the rest of the texts remapping
295 for (FDlgEdge& Edge : Children)
296 {
297 FDlgLocalizationHelper::UpdateTextFromRemapping(Settings, Edge.GetMutableUnformattedText());
298 }
299 }
300
301 if (bUpdateGraphNode)
302 {
304 }
305}
306
307void UDlgNode::UpdateTextsNamespacesAndKeys(const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode)
308{
309 if (bEdges)
310 {
311 UObject* Outer = GetOuter();
312 for (FDlgEdge& Edge : Children)
313 {
314 Edge.UpdateTextsNamespacesAndKeys(Outer, Settings);
315 }
316 }
317
318 if (bUpdateGraphNode)
319 {
321 }
322}
323
324void UDlgNode::RebuildTextArguments(bool bEdges, bool bUpdateGraphNode)
325{
326 if (bEdges)
327 {
328 for (FDlgEdge& Edge : Children)
329 {
330 Edge.RebuildTextArguments();
331 }
332 }
333
334 if (bUpdateGraphNode)
335 {
337 }
338}
339
341{
342#if WITH_EDITOR
343 UDlgDialogue::GetDialogueEditorAccess()->UpdateGraphNodeEdges(GraphNode);
344#endif // WITH_EDITOR
345}
346
347void UDlgNode::GetAssociatedParticipants(TArray<FName>& OutArray) const
348{
349 if (OwnerName != NAME_None)
350 {
351 OutArray.AddUnique(OwnerName);
352 }
353}
354
356{
357 return CastChecked<UDlgDialogue>(GetOuter());
358}
359
361{
362 return Cast<USoundWave>(GetNodeVoiceSoundBase());
363}
364
365// End own functions
static void UpdateTextFromRemapping(const UDlgSystemSettings &Settings, FText &OutText)
static FDlgLogger & Get()
Definition DlgLogger.h:24
void Errorf(const FmtType &Fmt, Types... Args)
Definition INYLogger.h:305
UCLASS(BlueprintType)
Definition DlgContext.h:96
const TArray< FDlgEdge > & GetOptionsArray() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Options|Satisfied")
Definition DlgContext.h:248
bool EnterNode(int32 NodeIndex, TSet< const UDlgNode * > NodesEnteredWithThisStep)
TArray< FDlgEdge > & GetMutableOptionsArray()
Definition DlgContext.h:249
FString GetContextString() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Context")
UObject * GetMutableParticipant(FName ParticipantName) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data", DisplayName = "Get Participant")
TArray< FDlgEdgeData > & GetAllMutableOptionsArray()
Definition DlgContext.h:304
UCLASS(BlueprintType, Meta = (DisplayThumbnail = "true"))
Definition DlgDialogue.h:85
UCLASS(BlueprintType, Abstract, EditInlineNew, ClassGroup = "Dialogue")
Definition DlgNode.h:40
virtual bool HandleNodeEnter(UDlgContext &Context, TSet< const UDlgNode * > NodesEnteredWithThisStep)
Definition DlgNode.cpp:129
void UpdateGraphNode()
Definition DlgNode.cpp:340
virtual void GetAssociatedParticipants(TArray< FName > &OutArray) const
Definition DlgNode.cpp:347
USoundWave * GetNodeVoiceSoundWave() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.cpp:360
virtual FDlgEdge * GetMutableNodeChildForTargetIndex(int32 TargetIndex)
Definition DlgNode.cpp:257
virtual USoundBase * GetNodeVoiceSoundBase() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:295
FName OwnerName
UPROPERTY(EditAnywhere, Category = "Dialogue|Node", Meta = (DisplayName = "Participant Name"))
Definition DlgNode.h:359
const TArray< int32 > GetNodeOpenChildren_DEPRECATED() const
Definition DlgNode.cpp:242
FDialogueNodePropertyChanged OnDialogueNodePropertyChanged
Definition DlgNode.h:87
void PostLoad() override
Definition DlgNode.cpp:53
UDlgDialogue * GetDialogue() const
Definition DlgNode.cpp:355
virtual void RebuildTextArguments(bool bEdges, bool bUpdateGraphNode=true)
Definition DlgNode.cpp:324
void RegenerateGUID()
Definition DlgNode.h:113
virtual bool CheckNodeEnterConditions(const UDlgContext &Context, TSet< const UDlgNode * > AlreadyVisitedNodes) const
Definition DlgNode.cpp:191
virtual bool OptionSelected(int32 OptionIndex, UDlgContext &Context)
Definition DlgNode.cpp:226
bool bCheckChildrenOnEvaluation
UPROPERTY(EditAnywhere, Category = "Dialogue|Node")
Definition DlgNode.h:369
TArray< FDlgCondition > EnterConditions
UPROPERTY(EditAnywhere, Category = "Dialogue|Node")
Definition DlgNode.h:376
void PostEditImport() override
Definition DlgNode.cpp:90
static void AddReferencedObjects(UObject *InThis, FReferenceCollector &Collector)
Definition DlgNode.cpp:41
virtual void UpdateTextsNamespacesAndKeys(const UDlgSystemSettings &Settings, bool bEdges, bool bUpdateGraphNode=true)
Definition DlgNode.cpp:307
void PostDuplicate(bool bDuplicateForPIE) override
Definition DlgNode.cpp:78
bool HasGUID() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:111
void Serialize(FArchive &Ar) override
Definition DlgNode.cpp:15
void PostInitProperties() override
Definition DlgNode.cpp:65
TArray< FDlgEvent > EnterEvents
UPROPERTY(EditAnywhere, Category = "Dialogue|Node")
Definition DlgNode.h:383
TArray< FDlgEdge > Children
UPROPERTY(VisibleAnywhere, EditFixedSize, AdvancedDisplay, Category = "Dialogue|Node")
Definition DlgNode.h:402
virtual void UpdateTextsValuesFromDefaultsAndRemappings(const UDlgSystemSettings &Settings, bool bEdges, bool bUpdateGraphNode=true)
Definition DlgNode.cpp:271
void FireNodeEnterEvents(UDlgContext &Context)
Definition DlgNode.cpp:142
bool HasAnySatisfiedChild(const UDlgContext &Context, TSet< const UDlgNode * > AlreadyVisitedNodes) const
Definition DlgNode.cpp:212
virtual bool ReevaluateChildren(UDlgContext &Context, TSet< const UDlgNode * > AlreadyEvaluated)
Definition DlgNode.cpp:157
UCLASS(Config = Engine, DefaultConfig, meta = (DisplayName = "Dialogue System Settings"))
bool bSetDefaultEdgeTextOnFirstChildOnly
UPROPERTY(Category = "Default Texts", Config, EditAnywhere, DisplayName = "Set Default Edge Texts on ...
bool bSetDefaultEdgeTexts
UPROPERTY(Category = "Default Texts", Config, EditAnywhere, DisplayName = "Set Default Edge Texts")
static bool EvaluateArray(const UDlgContext &Context, const TArray< FDlgCondition > &ConditionsArray, FName DefaultParticipantName=NAME_None)
USTRUCT(BlueprintType)
Definition DlgContext.h:28
USTRUCT(BlueprintType)
Definition DlgEdge.h:25
USTRUCT(BlueprintType)
Definition DlgEvent.h:59