99
1010namespace DialogueManagerRuntime
1111{
12+
13+ public enum MutationBehaviour
14+ {
15+ Wait ,
16+ DoNotWait ,
17+ Skip
18+ }
19+
1220 public enum TranslationSource
1321 {
1422 None ,
@@ -33,6 +41,8 @@ public partial class DialogueManager : RefCounted
3341
3442 [ Signal ] public delegate void ResolvedEventHandler ( Variant value ) ;
3543
44+ private static Random random = new Random ( ) ;
45+
3646 private static GodotObject ? instance ;
3747 public static GodotObject Instance
3848 {
@@ -41,7 +51,11 @@ public static GodotObject Instance
4151 if ( instance == null )
4252 {
4353 instance = Engine . GetSingleton ( "DialogueManager" ) ;
44- instance . Connect ( "bridge_dialogue_started" , Callable . From ( ( Resource dialogueResource ) => DialogueStarted ? . Invoke ( dialogueResource ) ) ) ;
54+ instance . Connect ( "dialogue_started" , Callable . From ( ( Resource dialogueResource ) => DialogueStarted ? . Invoke ( dialogueResource ) ) ) ;
55+ instance . Connect ( "passed_title" , Callable . From ( ( string title ) => PassedTitle ? . Invoke ( title ) ) ) ;
56+ instance . Connect ( "got_dialogue" , Callable . From ( ( RefCounted line ) => GotDialogue ? . Invoke ( new DialogueLine ( line ) ) ) ) ;
57+ instance . Connect ( "mutated" , Callable . From ( ( Dictionary mutation ) => Mutated ? . Invoke ( mutation ) ) ) ;
58+ instance . Connect ( "dialogue_ended" , Callable . From ( ( Resource dialogueResource ) => DialogueEnded ? . Invoke ( dialogueResource ) ) ) ;
4559 }
4660 return instance ;
4761 }
@@ -81,56 +95,37 @@ public static Func<Node> GetCurrentScene
8195 set => Instance . Set ( "get_current_scene" , Callable . From ( value ) ) ;
8296 }
8397
84-
85- public static void Prepare ( GodotObject instance )
98+ public static Resource CreateResourceFromText ( string text )
8699 {
87- instance . Connect ( "passed_title" , Callable . From ( ( string title ) => PassedTitle ? . Invoke ( title ) ) ) ;
88- instance . Connect ( "got_dialogue" , Callable . From ( ( RefCounted line ) => GotDialogue ? . Invoke ( new DialogueLine ( line ) ) ) ) ;
89- instance . Connect ( "mutated" , Callable . From ( ( Dictionary mutation ) => Mutated ? . Invoke ( mutation ) ) ) ;
90- instance . Connect ( "dialogue_ended" , Callable . From ( ( Resource dialogueResource ) => DialogueEnded ? . Invoke ( dialogueResource ) ) ) ;
100+ return ( Resource ) Instance . Call ( "create_resource_from_text" , text ) ;
91101 }
92102
93-
94- public static async Task < GodotObject > GetSingleton ( )
103+ public static async Task < DialogueLine ? > GetNextDialogueLine ( Resource dialogueResource , string key = "" , Array < Variant > ? extraGameStates = null , MutationBehaviour mutation_behaviour = MutationBehaviour . Wait )
95104 {
96- if ( instance != null ) return instance ;
97-
98- var tree = Engine . GetMainLoop ( ) ;
99- int x = 0 ;
100-
101- // Try and find the singleton for a few seconds
102- while ( ! Engine . HasSingleton ( "DialogueManager" ) && x < 300 )
105+ int id = random . Next ( ) ;
106+ Instance . Call ( "_bridge_get_next_dialogue_line" , id , dialogueResource , key , extraGameStates ?? new Array < Variant > ( ) , ( int ) mutation_behaviour ) ;
107+ while ( true )
103108 {
104- await tree . ToSignal ( tree , SceneTree . SignalName . ProcessFrame ) ;
105- x ++ ;
106- }
107-
108- // If it times out something is wrong
109- if ( x >= 300 )
110- {
111- throw new Exception ( "The DialogueManager singleton is missing." ) ;
109+ var result = await Instance . ToSignal ( Instance , "bridge_get_next_dialogue_line_completed" ) ;
110+ if ( ( int ) result [ 0 ] == id )
111+ {
112+ return ( ( RefCounted ) result [ 1 ] == null ) ? null : new DialogueLine ( ( RefCounted ) result [ 1 ] ) ;
113+ }
112114 }
113-
114- instance = Engine . GetSingleton ( "DialogueManager" ) ;
115- return instance ;
116- }
117-
118- public static Resource CreateResourceFromText ( string text )
119- {
120- return ( Resource ) Instance . Call ( "create_resource_from_text" , text ) ;
121115 }
122116
123- public static async Task < DialogueLine ? > GetNextDialogueLine ( Resource dialogueResource , string key = "" , Array < Variant > ? extraGameStates = null )
117+ public static async Task < DialogueLine ? > GetLine ( Resource dialogueResource , string key = "" , Array < Variant > ? extraGameStates = null )
124118 {
125- var instance = ( Node ) Instance . Call ( "_bridge_get_new_instance" ) ;
126- Prepare ( instance ) ;
127- instance . Call ( "_bridge_get_next_dialogue_line" , dialogueResource , key , extraGameStates ?? new Array < Variant > ( ) ) ;
128- var result = await instance . ToSignal ( instance , "bridge_get_next_dialogue_line_completed" ) ;
129- instance . QueueFree ( ) ;
130-
131- if ( ( RefCounted ) result [ 0 ] == null ) return null ;
132-
133- return new DialogueLine ( ( RefCounted ) result [ 0 ] ) ;
119+ int id = random . Next ( ) ;
120+ Instance . Call ( "_bridge_get_line" , id , dialogueResource , key , extraGameStates ?? new Array < Variant > ( ) ) ;
121+ while ( true )
122+ {
123+ var result = await Instance . ToSignal ( Instance , "bridge_get_line_completed" ) ;
124+ if ( ( int ) result [ 0 ] == id )
125+ {
126+ return ( ( RefCounted ) result [ 0 ] == null ) ? null : new DialogueLine ( ( RefCounted ) result [ 0 ] ) ;
127+ }
128+ }
134129 }
135130
136131
@@ -176,12 +171,20 @@ public static string StaticIdToLineId(Resource dialogueResource, string staticId
176171
177172 public static async void Mutate ( Dictionary mutation , Array < Variant > ? extraGameStates = null , bool isInlineMutation = false )
178173 {
179- Instance . Call ( "_bridge_mutate" , mutation , extraGameStates ?? new Array < Variant > ( ) , isInlineMutation ) ;
180- await Instance . ToSignal ( Instance , "bridge_mutated" ) ;
174+ int id = random . Next ( ) ;
175+ Instance . Call ( "_bridge_mutate" , id , mutation , extraGameStates ?? new Array < Variant > ( ) , isInlineMutation ) ;
176+ while ( true )
177+ {
178+ var result = await Instance . ToSignal ( Instance , "bridge_mutated" ) ;
179+ if ( ( int ) result [ 0 ] == id )
180+ {
181+ return ;
182+ }
183+ }
181184 }
182185
183186
184- public static Array < Dictionary > GetMembersForAutoload ( Script script )
187+ public static Array < Dictionary > GetMembersForScript ( Script script )
185188 {
186189 Array < Dictionary > members = new Array < Dictionary > ( ) ;
187190
@@ -215,6 +218,10 @@ public static Array<Dictionary> GetMembersForAutoload(Script script)
215218 type = "method" ;
216219 break ;
217220
221+ case MemberTypes . NestedType :
222+ type = "constant" ;
223+ break ;
224+
218225 default :
219226 continue ;
220227 }
@@ -232,40 +239,43 @@ public static Array<Dictionary> GetMembersForAutoload(Script script)
232239
233240 public bool ThingHasConstant ( GodotObject thing , string property )
234241 {
235- var fieldInfos = thing . GetType ( ) . GetFields ( BindingFlags . Instance | BindingFlags . Static | BindingFlags . Public | BindingFlags . DeclaredOnly ) ;
236- foreach ( var fieldInfo in fieldInfos )
237- {
238- if ( fieldInfo . Name == property && fieldInfo . IsLiteral )
239- {
240- return true ;
241- }
242- }
243-
244- return false ;
242+ var memberInfos = thing . GetType ( ) . GetMember ( property , BindingFlags . Instance | BindingFlags . Static | BindingFlags . Public | BindingFlags . DeclaredOnly ) ;
243+ return memberInfos . Length > 0 ;
245244 }
246245
247246
248247 public Variant ResolveThingConstant ( GodotObject thing , string property )
249248 {
250- var fieldInfos = thing . GetType ( ) . GetFields ( BindingFlags . Instance | BindingFlags . Static | BindingFlags . Public | BindingFlags . DeclaredOnly ) ;
251- foreach ( var fieldInfo in fieldInfos )
249+ var memberInfos = thing . GetType ( ) . GetMember ( property , BindingFlags . Instance | BindingFlags . Static | BindingFlags . Public | BindingFlags . DeclaredOnly ) ;
250+ foreach ( var memberInfo in memberInfos )
252251 {
253- if ( fieldInfo . Name == property && fieldInfo . IsLiteral )
252+ if ( memberInfo != null )
254253 {
255254 try
256255 {
257- Variant value = fieldInfo . GetValue ( thing ) switch
256+ switch ( memberInfo . MemberType )
258257 {
259- int v => Variant . From ( ( long ) v ) ,
260- float v => Variant . From ( ( double ) v ) ,
261- System . String v => Variant . From ( ( string ) v ) ,
262- _ => Variant . From ( fieldInfo . GetValue ( thing ) )
263- } ;
264- return value ;
258+ case MemberTypes . Field :
259+ return ConvertValueToVariant ( ( memberInfo as FieldInfo ) . GetValue ( thing ) ) ;
260+
261+ case MemberTypes . Property :
262+ return ConvertValueToVariant ( ( memberInfo as PropertyInfo ) . GetValue ( thing ) ) ;
263+
264+ case MemberTypes . NestedType :
265+ var type = thing . GetType ( ) . GetNestedType ( property ) ;
266+ if ( type . IsEnum )
267+ {
268+ return GetEnumAsDictionary ( type ) ;
269+ }
270+ break ;
271+
272+ default :
273+ continue ;
274+ }
265275 }
266- catch ( Exception )
276+ catch ( Exception e )
267277 {
268- throw new Exception ( $ "Constant { property } of type $ { fieldInfo . GetValue ( thing ) . GetType ( ) } is not supported by Variant.") ;
278+ throw new Exception ( $ "{ property } is not supported by Variant.") ;
269279 }
270280 }
271281 }
@@ -274,6 +284,53 @@ public Variant ResolveThingConstant(GodotObject thing, string property)
274284 }
275285
276286
287+ Dictionary GetEnumAsDictionary ( Type enumType )
288+ {
289+ Dictionary dictionary = new Dictionary ( ) ;
290+ foreach ( var value in enumType . GetEnumValuesAsUnderlyingType ( ) )
291+ {
292+ var key = enumType . GetEnumName ( value ) ;
293+ if ( key != null )
294+ {
295+ dictionary . Add ( key , ConvertValueToVariant ( value ) ) ;
296+ }
297+ }
298+ return dictionary ;
299+ }
300+
301+
302+ Variant ConvertValueToVariant ( object value )
303+ {
304+ if ( value == null ) return default ;
305+
306+ Type rawType = value . GetType ( ) ;
307+ if ( rawType . IsEnum )
308+ {
309+ var values = GetEnumAsDictionary ( rawType ) ;
310+ value = values [ value . ToString ( ) ] ;
311+ }
312+
313+ return value switch
314+ {
315+ Variant v => v ,
316+ bool v => Variant . From ( v ) ,
317+ byte v => Variant . From ( ( long ) v ) ,
318+ sbyte v => Variant . From ( ( long ) v ) ,
319+ short v => Variant . From ( ( long ) v ) ,
320+ ushort v => Variant . From ( ( long ) v ) ,
321+ int v => Variant . From ( ( long ) v ) ,
322+ uint v => Variant . From ( ( long ) v ) ,
323+ long v => Variant . From ( v ) ,
324+ ulong v => Variant . From ( ( long ) v ) ,
325+ float v => Variant . From ( ( double ) v ) ,
326+ double v => Variant . From ( v ) ,
327+ string v => Variant . From ( v ) ,
328+ GodotObject godotObj => Variant . From ( godotObj ) ,
329+ _ => default
330+ } ;
331+ }
332+
333+
277334 public bool ThingHasMethod ( GodotObject thing , string method , Array < Variant > args )
278335 {
279336 var methodInfos = thing . GetType ( ) . GetMethods ( BindingFlags . Instance | BindingFlags . Static | BindingFlags . Public | BindingFlags . DeclaredOnly ) ;
@@ -289,19 +346,22 @@ public bool ThingHasMethod(GodotObject thing, string method, Array<Variant> args
289346 }
290347
291348
292- public async void ResolveThingMethod ( GodotObject thing , string method , Array < Variant > args )
349+ public async void ResolveThingMethod ( float id , GodotObject thing , string method , Array < Variant > args )
293350 {
294351 MethodInfo ? info = null ;
295352 var methodInfos = thing . GetType ( ) . GetMethods ( BindingFlags . Instance | BindingFlags . Static | BindingFlags . Public | BindingFlags . DeclaredOnly ) ;
296353 foreach ( var methodInfo in methodInfos )
297354 {
298- if ( methodInfo . Name == method && args . Count >= methodInfo . GetParameters ( ) . Where ( p => ! p . HasDefaultValue ) . Count ( ) )
355+ if ( methodInfo . Name == method && args . Count >= methodInfo . GetParameters ( ) . Count ( p => ! p . HasDefaultValue ) )
299356 {
300357 info = methodInfo ;
301358 }
302359 }
303360
304- if ( info == null ) return ;
361+ if ( info == null ) {
362+ EmitSignal ( SignalName . Resolved , id ) ;
363+ return ;
364+ }
305365
306366#nullable disable
307367 // Convert the method args to something reflection can handle
@@ -339,20 +399,26 @@ public async void ResolveThingMethod(GodotObject thing, string method, Array<Var
339399 await taskResult ;
340400 try
341401 {
342- Variant value = ( Variant ) taskResult . GetType ( ) . GetProperty ( "Result" ) . GetValue ( taskResult ) ;
343- EmitSignal ( SignalName . Resolved , value ) ;
402+ object value = taskResult . GetType ( ) . GetProperty ( "Result" ) . GetValue ( taskResult ) ;
403+ EmitSignal ( SignalName . Resolved , id , ConvertValueToVariant ( value ) ) ;
344404 }
345405 catch ( Exception )
346406 {
347- EmitSignal ( SignalName . Resolved ) ;
407+ EmitSignal ( SignalName . Resolved , id ) ;
348408 }
349409 }
350410 else
351411 {
352- EmitSignal ( SignalName . Resolved , ( Variant ) result ) ;
412+ EmitSignal ( SignalName . Resolved , id , ConvertValueToVariant ( result ) ) ;
353413 }
354414 }
355415#nullable enable
416+
417+
418+ public static string GetErrorMessage ( int error )
419+ {
420+ return ( string ) Instance . Call ( "_bridge_get_error_message" , error ) ;
421+ }
356422 }
357423
358424
@@ -412,12 +478,6 @@ public string? Time
412478 get => time ;
413479 }
414480
415- private Dictionary pauses = new Dictionary ( ) ;
416- public Dictionary Pauses
417- {
418- get => pauses ;
419- }
420-
421481 private Dictionary speeds = new Dictionary ( ) ;
422482 public Dictionary Speeds
423483 {
@@ -456,7 +516,6 @@ public DialogueLine(RefCounted data)
456516 character = ( string ) data . Get ( "character" ) ;
457517 text = ( string ) data . Get ( "text" ) ;
458518 translation_key = ( string ) data . Get ( "translation_key" ) ;
459- pauses = ( Dictionary ) data . Get ( "pauses" ) ;
460519 speeds = ( Dictionary ) data . Get ( "speeds" ) ;
461520 inline_mutations = ( Array < Godot . Collections . Array > ) data . Get ( "inline_mutations" ) ;
462521 time = ( string ) data . Get ( "time" ) ;
@@ -474,6 +533,20 @@ public DialogueLine(RefCounted data)
474533 }
475534
476535
536+ public bool HasTag ( string tagName )
537+ {
538+ string wrapped = $ "{ tagName } =";
539+ foreach ( var tag in tags )
540+ {
541+ if ( tag . StartsWith ( wrapped ) )
542+ {
543+ return true ;
544+ }
545+ }
546+ return false ;
547+ }
548+
549+
477550 public string GetTagValue ( string tagName )
478551 {
479552 string wrapped = $ "{ tagName } =";
0 commit comments