@@ -89,7 +89,7 @@ public void Apply_effect_node_defaults_level_and_ownership_from_ability_context(
8989
9090 graph . VariableDefinitions . DefineObjectProperty (
9191 "effect" ,
92- new EffectDataResolver ( CreateTrackingEffectData ( "Tracked" , DurationType . Instant , capture ) ) ) ;
92+ new EffectFromDataResolver ( CreateTrackingEffectData ( "Tracked" , DurationType . Instant , capture ) ) ) ;
9393 graph . VariableDefinitions . DefineObjectVariable < IForgeEntity > ( "entity" , target ) ;
9494
9595 ApplyEffectNode node = CreateApplyEffectNode ( "effect" , "entity" ) ;
@@ -107,7 +107,7 @@ public void Apply_effect_node_defaults_level_and_ownership_from_ability_context(
107107
108108 [ Fact ]
109109 [ Trait ( "Graph" , "ApplyEffect" ) ]
110- public void Apply_effect_node_uses_bound_level_and_ownership_when_present ( )
110+ public void Apply_effect_node_uses_level_and_ownership_configured_on_the_effect_resolver ( )
111111 {
112112 TestEntity abilityOwner = CreateTestEntity ( ) ;
113113 TestEntity abilitySource = CreateTestEntity ( ) ;
@@ -117,20 +117,20 @@ public void Apply_effect_node_uses_bound_level_and_ownership_when_present()
117117 var capture = new AppliedEffectCaptureComponent ( ) ;
118118 var graph = new Graph ( ) ;
119119
120- graph . VariableDefinitions . DefineObjectProperty (
121- "effect" ,
122- new EffectDataResolver ( CreateTrackingEffectData ( "Tracked" , DurationType . Instant , capture ) ) ) ;
123- graph . VariableDefinitions . DefineObjectVariable < IForgeEntity > ( "entity" , target ) ;
124120 graph . VariableDefinitions . DefineVariable ( "level" , 7 ) ;
125121 graph . VariableDefinitions . DefineObjectVariable < IForgeEntity > ( "ownershipOwner" , explicitOwner ) ;
126122 graph . VariableDefinitions . DefineObjectVariable < IForgeEntity > ( "ownershipSource" , explicitSource ) ;
127123 graph . VariableDefinitions . DefineObjectProperty (
128- "ownership" ,
129- new OwnershipResolver (
130- new EntityVariableResolver ( "ownershipOwner" ) ,
131- new EntityVariableResolver ( "ownershipSource" ) ) ) ;
124+ "effect" ,
125+ new EffectFromDataResolver (
126+ CreateTrackingEffectData ( "Tracked" , DurationType . Instant , capture ) ,
127+ new VariableResolver ( "level" , typeof ( int ) ) ,
128+ new OwnershipResolver (
129+ new EntityVariableResolver ( "ownershipOwner" ) ,
130+ new EntityVariableResolver ( "ownershipSource" ) ) ) ) ;
131+ graph . VariableDefinitions . DefineObjectVariable < IForgeEntity > ( "entity" , target ) ;
132132
133- ApplyEffectNode node = CreateApplyEffectNode ( "effect" , "entity" , "level" , "ownership" ) ;
133+ ApplyEffectNode node = CreateApplyEffectNode ( "effect" , "entity" ) ;
134134 graph . AddNode ( node ) ;
135135 graph . AddConnection ( new Connection (
136136 graph . EntryNode . OutputPorts [ EntryNode . OutputPort ] ,
@@ -143,6 +143,159 @@ public void Apply_effect_node_uses_bound_level_and_ownership_when_present()
143143 capture . LastSource . Should ( ) . BeSameAs ( explicitSource ) ;
144144 }
145145
146+ [ Fact ]
147+ [ Trait ( "Graph" , "ApplyEffect" ) ]
148+ public void Apply_effect_node_writes_single_active_effect_output_for_non_instant_effect ( )
149+ {
150+ TestEntity target = CreateTestEntity ( ) ;
151+ EffectData effectData = CreateFlatEffectData (
152+ "Buff" ,
153+ "TestAttributeSet.Attribute1" ,
154+ 10 ,
155+ DurationType . Infinite ) ;
156+ var graph = new Graph ( ) ;
157+
158+ graph . VariableDefinitions . DefineObjectProperty ( "effect" , new EffectFromDataResolver ( effectData ) ) ;
159+ graph . VariableDefinitions . DefineObjectVariable < IForgeEntity > ( "entity" , target ) ;
160+ graph . VariableDefinitions . DefineObjectVariable < ActiveEffectHandle > ( "activeEffect" ) ;
161+
162+ ApplyEffectNode node = CreateApplyEffectNode ( "effect" , "entity" ) ;
163+ node . BindOutput ( ApplyEffectNode . ActiveEffectOutput , "activeEffect" ) ;
164+ graph . AddNode ( node ) ;
165+ graph . AddConnection ( new Connection (
166+ graph . EntryNode . OutputPorts [ EntryNode . OutputPort ] ,
167+ node . InputPorts [ ActionNode . InputPort ] ) ) ;
168+
169+ var processor = new GraphProcessor ( graph ) ;
170+ processor . StartGraph ( ) ;
171+
172+ processor . GraphContext . GraphVariables . TryGetObject ( "activeEffect" , out ActiveEffectHandle ? handle )
173+ . Should ( ) . BeTrue ( ) ;
174+ handle . Should ( ) . NotBeNull ( ) ;
175+ handle ! . IsValid . Should ( ) . BeTrue ( ) ;
176+ }
177+
178+ [ Fact ]
179+ [ Trait ( "Graph" , "ApplyEffect" ) ]
180+ public void Apply_effect_node_writes_active_effect_array_output_following_input_shape ( )
181+ {
182+ TestEntity firstTarget = CreateTestEntity ( ) ;
183+ TestEntity secondTarget = CreateTestEntity ( ) ;
184+ EffectData effectData = CreateFlatEffectData (
185+ "Buff" ,
186+ "TestAttributeSet.Attribute1" ,
187+ 10 ,
188+ DurationType . Infinite ) ;
189+ var graph = new Graph ( ) ;
190+
191+ graph . VariableDefinitions . DefineObjectProperty ( "effect" , new EffectFromDataResolver ( effectData ) ) ;
192+ graph . VariableDefinitions . DefineObjectArrayVariable < IForgeEntity > ( "entities" , firstTarget , secondTarget ) ;
193+ graph . VariableDefinitions . DefineObjectArrayVariable < ActiveEffectHandle > ( "activeEffects" ) ;
194+
195+ ApplyEffectNode node = CreateApplyEffectNode ( "effect" , "entities" ) ;
196+ node . BindOutput ( ApplyEffectNode . ActiveEffectOutput , "activeEffects" ) ;
197+ graph . AddNode ( node ) ;
198+ graph . AddConnection ( new Connection (
199+ graph . EntryNode . OutputPorts [ EntryNode . OutputPort ] ,
200+ node . InputPorts [ ActionNode . InputPort ] ) ) ;
201+
202+ var processor = new GraphProcessor ( graph ) ;
203+ processor . StartGraph ( ) ;
204+
205+ processor . GraphContext . GraphVariables . TryGetObjectArray ( "activeEffects" , out ActiveEffectHandle [ ] ? handles )
206+ . Should ( ) . BeTrue ( ) ;
207+ handles . Should ( ) . HaveCount ( 2 ) ;
208+ handles . Should ( ) . OnlyContain ( handle => handle . IsValid ) ;
209+ }
210+
211+ [ Fact ]
212+ [ Trait ( "Graph" , "ApplyEffect" ) ]
213+ public void Apply_effect_node_writes_null_active_effect_output_for_instant_effect ( )
214+ {
215+ TestEntity target = CreateTestEntity ( ) ;
216+ EffectData effectData = CreateFlatEffectData (
217+ "Zap" ,
218+ "TestAttributeSet.Attribute1" ,
219+ 10 ,
220+ DurationType . Instant ) ;
221+ var graph = new Graph ( ) ;
222+
223+ graph . VariableDefinitions . DefineObjectProperty ( "effect" , new EffectFromDataResolver ( effectData ) ) ;
224+ graph . VariableDefinitions . DefineObjectVariable < IForgeEntity > ( "entity" , target ) ;
225+ graph . VariableDefinitions . DefineObjectVariable < ActiveEffectHandle > ( "activeEffect" ) ;
226+
227+ ApplyEffectNode node = CreateApplyEffectNode ( "effect" , "entity" ) ;
228+ node . BindOutput ( ApplyEffectNode . ActiveEffectOutput , "activeEffect" ) ;
229+ graph . AddNode ( node ) ;
230+ graph . AddConnection ( new Connection (
231+ graph . EntryNode . OutputPorts [ EntryNode . OutputPort ] ,
232+ node . InputPorts [ ActionNode . InputPort ] ) ) ;
233+
234+ var processor = new GraphProcessor ( graph ) ;
235+ processor . StartGraph ( ) ;
236+
237+ processor . GraphContext . GraphVariables . TryGetObject ( "activeEffect" , out ActiveEffectHandle ? handle ) ;
238+ handle . Should ( ) . BeNull ( ) ;
239+ }
240+
241+ [ Fact ]
242+ [ Trait ( "Graph" , "ApplyEffect" ) ]
243+ public void Apply_effect_node_passes_provider_built_context_data_through_the_pipeline ( )
244+ {
245+ TestEntity target = CreateTestEntity ( ) ;
246+ var capture = new ContextCaptureComponent ( ) ;
247+ var graph = new Graph ( ) ;
248+
249+ graph . VariableDefinitions . DefineVariable ( "damage" , 42 ) ;
250+ graph . VariableDefinitions . DefineObjectProperty (
251+ "effect" ,
252+ new EffectFromDataResolver ( CreateTrackingEffectData ( "Tracked" , DurationType . Instant , capture ) ) ) ;
253+ graph . VariableDefinitions . DefineObjectVariable < IForgeEntity > ( "entity" , target ) ;
254+ graph . VariableDefinitions . DefineObjectProperty (
255+ "context" ,
256+ new EffectContextDataResolver ( new DamageContextProvider ( ) ) ) ;
257+
258+ ApplyEffectNode node = CreateApplyEffectNode ( "effect" , "entity" ) ;
259+ node . BindInput ( ApplyEffectNode . ContextDataInput , "context" ) ;
260+ graph . AddNode ( node ) ;
261+ graph . AddConnection ( new Connection (
262+ graph . EntryNode . OutputPorts [ EntryNode . OutputPort ] ,
263+ node . InputPorts [ ActionNode . InputPort ] ) ) ;
264+
265+ var processor = new GraphProcessor ( graph ) ;
266+ processor . StartGraph ( ) ;
267+
268+ capture . WasApplied . Should ( ) . BeTrue ( ) ;
269+ capture . ReceivedContext . Should ( ) . BeTrue ( ) ;
270+ capture . ReceivedDamage . Should ( ) . Be ( 42 ) ;
271+ }
272+
273+ [ Fact ]
274+ [ Trait ( "Graph" , "ApplyEffect" ) ]
275+ public void Apply_effect_node_applies_without_context_data_when_input_is_unbound ( )
276+ {
277+ TestEntity target = CreateTestEntity ( ) ;
278+ var capture = new ContextCaptureComponent ( ) ;
279+ var graph = new Graph ( ) ;
280+
281+ graph . VariableDefinitions . DefineObjectProperty (
282+ "effect" ,
283+ new EffectFromDataResolver ( CreateTrackingEffectData ( "Tracked" , DurationType . Instant , capture ) ) ) ;
284+ graph . VariableDefinitions . DefineObjectVariable < IForgeEntity > ( "entity" , target ) ;
285+
286+ ApplyEffectNode node = CreateApplyEffectNode ( "effect" , "entity" ) ;
287+ graph . AddNode ( node ) ;
288+ graph . AddConnection ( new Connection (
289+ graph . EntryNode . OutputPorts [ EntryNode . OutputPort ] ,
290+ node . InputPorts [ ActionNode . InputPort ] ) ) ;
291+
292+ var processor = new GraphProcessor ( graph ) ;
293+ processor . StartGraph ( ) ;
294+
295+ capture . WasApplied . Should ( ) . BeTrue ( ) ;
296+ capture . ReceivedContext . Should ( ) . BeFalse ( ) ;
297+ }
298+
146299 private static void ConfigureEffectInput (
147300 Graph graph ,
148301 bool useEffectArray ,
@@ -153,11 +306,13 @@ private static void ConfigureEffectInput(
153306 {
154307 graph . VariableDefinitions . DefineObjectArrayProperty (
155308 "effect" ,
156- new EffectDataArrayResolver ( firstEffect , secondEffect ) ) ;
309+ new EffectArrayFromDataResolver ( [ firstEffect , secondEffect ] ) ) ;
157310 return ;
158311 }
159312
160- graph . VariableDefinitions . DefineObjectProperty ( "effect" , new EffectDataResolver ( firstEffect ) ) ;
313+ graph . VariableDefinitions . DefineObjectProperty (
314+ "effect" ,
315+ new EffectFromDataResolver ( firstEffect ) ) ;
161316 }
162317
163318 private static void ConfigureEntityInput (
@@ -245,4 +400,35 @@ public void OnEffectApplied(IForgeEntity target, in EffectEvaluatedData effectEv
245400 LastSource = effectEvaluatedData . Effect . Ownership . Source ;
246401 }
247402 }
403+
404+ private sealed record DamageContext ( int Damage ) ;
405+
406+ private sealed class DamageContextProvider : EffectContextDataProvider < DamageContext >
407+ {
408+ public override DamageContext CreateData ( GraphContext graphContext , EffectContextDataInputs inputs )
409+ {
410+ graphContext . TryResolve ( "damage" , out int damage ) ;
411+ return new DamageContext ( damage ) ;
412+ }
413+ }
414+
415+ private sealed class ContextCaptureComponent : IEffectComponent
416+ {
417+ public bool WasApplied { get ; private set ; }
418+
419+ public bool ReceivedContext { get ; private set ; }
420+
421+ public int ReceivedDamage { get ; private set ; }
422+
423+ public void OnEffectApplied ( IForgeEntity target , in EffectEvaluatedData effectEvaluatedData )
424+ {
425+ WasApplied = true ;
426+
427+ if ( effectEvaluatedData . TryGetContextData ( out DamageContext ? context ) )
428+ {
429+ ReceivedContext = true ;
430+ ReceivedDamage = context . Damage ;
431+ }
432+ }
433+ }
248434}
0 commit comments