Skip to content

Commit d75656a

Browse files
authored
Ensure all decorators call after_run() correctly (#391)
1 parent b7e334a commit d75656a

File tree

7 files changed

+134
-19
lines changed

7 files changed

+134
-19
lines changed

addons/beehave/nodes/decorators/cooldown.gd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ func tick(actor: Node, blackboard: Blackboard) -> int:
4343
running_child = c
4444
if c is ActionLeaf:
4545
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
46-
47-
if response != RUNNING:
46+
else:
47+
c.after_run(actor, blackboard)
4848
blackboard.set_value(cache_key, wait_time, str(actor.get_instance_id()))
4949

5050
return response

addons/beehave/nodes/decorators/delayer.gd

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ func tick(actor: Node, blackboard: Blackboard) -> int:
3939
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
4040
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
4141

42-
if response == RUNNING and c is ActionLeaf:
42+
if response == RUNNING:
4343
running_child = c
44-
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
45-
46-
if response != RUNNING:
44+
if c is ActionLeaf:
45+
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
46+
else:
47+
c.after_run(actor, blackboard)
4748
blackboard.set_value(cache_key, 0.0, str(actor.get_instance_id()))
4849

4950
return response

addons/beehave/nodes/decorators/until_fail.gd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ func tick(actor: Node, blackboard: Blackboard) -> int:
2828
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
2929
return RUNNING
3030
if response == SUCCESS:
31+
c.after_run(actor, blackboard)
3132
return RUNNING
3233

34+
c.after_run(actor, blackboard)
3335
return SUCCESS

test/actions/mock_action.gd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ signal stopped_running(actor, blackboard)
99
signal interrupted(actor, blackboard)
1010

1111
var tick_count: int = 0
12+
var after_run_called: bool = false
1213

1314

1415
func before_run(actor: Node, blackboard: Blackboard) -> void:
1516
tick_count = 0
17+
after_run_called = false
1618
started_running.emit(actor, blackboard)
1719

1820

@@ -30,4 +32,5 @@ func interrupt(actor: Node, blackboard: Blackboard) -> void:
3032

3133
func after_run(actor: Node, blackboard: Blackboard) -> void:
3234
tick_count = 0
35+
after_run_called = true
3336
stopped_running.emit(actor, blackboard)

test/nodes/decorators/cooldown_test.gd

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,50 @@ func test_interrupt_propagates_when_composite() -> void:
8383

8484
func _on_interrupted(_actor, blackboard):
8585
var started = blackboard.get_value("interrupted", 0)
86-
blackboard.set_value("interrupted", started + 1)
86+
blackboard.set_value("interrupted", started + 1)
87+
88+
func test_after_run_called_on_success() -> void:
89+
cooldown.wait_time = 1.0
90+
action.final_result = BeehaveNode.SUCCESS
91+
92+
# First tick should execute child and call after_run
93+
assert_that(tree.tick()).is_equal(BeehaveNode.SUCCESS)
94+
assert_bool(action.after_run_called).is_true()
95+
96+
# Reset after_run_called flag since before_run will be called on next tick
97+
action.after_run_called = false
98+
99+
# Second tick should be in cooldown and not call after_run
100+
assert_that(tree.tick()).is_equal(BeehaveNode.FAILURE)
101+
assert_bool(action.after_run_called).is_false()
102+
103+
func test_after_run_called_on_failure() -> void:
104+
cooldown.wait_time = 1.0
105+
action.final_result = BeehaveNode.FAILURE
106+
107+
# First tick should execute child and call after_run
108+
assert_that(tree.tick()).is_equal(BeehaveNode.FAILURE)
109+
assert_bool(action.after_run_called).is_true()
110+
111+
# Reset after_run_called flag since before_run will be called on next tick
112+
action.after_run_called = false
113+
114+
# Second tick should be in cooldown and not call after_run
115+
assert_that(tree.tick()).is_equal(BeehaveNode.FAILURE)
116+
assert_bool(action.after_run_called).is_false()
117+
118+
func test_after_run_not_called_during_cooldown() -> void:
119+
cooldown.wait_time = 1.0
120+
action.final_result = BeehaveNode.SUCCESS
121+
122+
# First tick should execute child and call after_run
123+
assert_that(tree.tick()).is_equal(BeehaveNode.SUCCESS)
124+
assert_bool(action.after_run_called).is_true()
125+
126+
# Reset after_run_called flag since before_run will be called on next tick
127+
action.after_run_called = false
128+
129+
# Wait a bit but not enough to complete cooldown
130+
await runner.simulate_frames(1, 500)
131+
assert_that(tree.tick()).is_equal(BeehaveNode.FAILURE)
132+
assert_bool(action.after_run_called).is_false() # Should not call after_run during cooldown

test/nodes/decorators/delayer_test.gd

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ extends GdUnitTestSuite
66

77
# TestSuite generated from
88
const __source = "res://addons/beehave/nodes/decorators/delayer.gd"
9-
const __action = "res://test/actions/count_up_action.gd"
9+
const __action = "res://test/actions/mock_action.gd"
1010
const __tree = "res://addons/beehave/nodes/beehave_tree.gd"
1111
const __blackboard = "res://addons/beehave/blackboard.gd"
1212

1313
var tree: BeehaveTree
14-
var action: ActionLeaf
14+
var action: MockAction
1515
var delayer: DelayDecorator
1616
var runner: GdUnitSceneRunner
1717

@@ -34,7 +34,7 @@ func before_test() -> void:
3434

3535
func test_return_success_after_delay() -> void:
3636
delayer.wait_time = get_physics_process_delta_time()
37-
action.status = BeehaveNode.SUCCESS
37+
action.final_result = BeehaveNode.SUCCESS
3838
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
3939
assert_that(tree.tick()).is_equal(BeehaveNode.SUCCESS)
4040
# Assure that the delayer properly resets
@@ -44,16 +44,53 @@ func test_return_success_after_delay() -> void:
4444

4545
func test_return_running_after_delay() -> void:
4646
delayer.wait_time = 1.0
47-
action.status = BeehaveNode.RUNNING
47+
action.final_result = BeehaveNode.RUNNING
4848
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
4949
await runner.simulate_frames(1, 1000)
5050
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
51-
action.status = BeehaveNode.SUCCESS
51+
action.final_result = BeehaveNode.SUCCESS
5252
assert_that(tree.tick()).is_equal(BeehaveNode.SUCCESS)
5353
# Assure that the delayer properly resets
54-
action.status = BeehaveNode.RUNNING
54+
action.final_result = BeehaveNode.RUNNING
5555
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
5656
await runner.simulate_frames(1, 1000)
5757
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
58-
action.status = BeehaveNode.SUCCESS
58+
action.final_result = BeehaveNode.SUCCESS
5959
assert_that(tree.tick()).is_equal(BeehaveNode.SUCCESS)
60+
61+
func test_after_run_called_on_success() -> void:
62+
delayer.wait_time = get_physics_process_delta_time()
63+
action.final_result = BeehaveNode.SUCCESS
64+
65+
# First tick should be in delay
66+
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
67+
assert_bool(action.after_run_called).is_false()
68+
69+
# Second tick should execute child and call after_run
70+
assert_that(tree.tick()).is_equal(BeehaveNode.SUCCESS)
71+
assert_bool(action.after_run_called).is_true()
72+
73+
func test_after_run_called_on_failure() -> void:
74+
delayer.wait_time = get_physics_process_delta_time()
75+
action.final_result = BeehaveNode.FAILURE
76+
77+
# First tick should be in delay
78+
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
79+
assert_bool(action.after_run_called).is_false()
80+
81+
# Second tick should execute child and call after_run
82+
assert_that(tree.tick()).is_equal(BeehaveNode.FAILURE)
83+
assert_bool(action.after_run_called).is_true()
84+
85+
func test_after_run_not_called_during_delay() -> void:
86+
delayer.wait_time = 1.0
87+
action.final_result = BeehaveNode.SUCCESS
88+
89+
# First tick should be in delay
90+
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
91+
assert_bool(action.after_run_called).is_false()
92+
93+
# Wait a bit but not enough to complete delay
94+
await runner.simulate_frames(1, 500)
95+
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
96+
assert_bool(action.after_run_called).is_false()

test/nodes/decorators/until_fail_test.gd

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ extends GdUnitTestSuite
66

77
# TestSuite generated from
88
const __source = "res://addons/beehave/nodes/decorators/until_fail.gd"
9-
const __action = "res://test/actions/count_up_action.gd"
9+
const __action = "res://test/actions/mock_action.gd"
1010
const __tree = "res://addons/beehave/nodes/beehave_tree.gd"
1111
const __blackboard = "res://addons/beehave/blackboard.gd"
1212

1313
var tree: BeehaveTree
14-
var action: ActionLeaf
14+
var action: MockAction
1515
var until_fail: UntilFailDecorator
1616

1717

@@ -31,15 +31,41 @@ func before_test() -> void:
3131

3232

3333
func test_failure() -> void:
34-
action.status = BeehaveNode.RUNNING
34+
action.final_result = BeehaveNode.RUNNING
3535

3636
for i in range(100):
3737
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
3838

39-
action.status = BeehaveNode.SUCCESS
39+
action.final_result = BeehaveNode.SUCCESS
4040

4141
for i in range(100):
4242
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
4343

44-
action.status = BeehaveNode.FAILURE
44+
action.final_result = BeehaveNode.FAILURE
4545
assert_that(tree.tick()).is_equal(BeehaveNode.SUCCESS)
46+
47+
func test_after_run_called_on_success() -> void:
48+
action.final_result = BeehaveNode.SUCCESS
49+
50+
# Child succeeds, decorator returns RUNNING
51+
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
52+
assert_bool(action.after_run_called).is_true()
53+
54+
func test_after_run_called_on_failure() -> void:
55+
action.final_result = BeehaveNode.FAILURE
56+
57+
# Child fails, decorator returns SUCCESS
58+
assert_that(tree.tick()).is_equal(BeehaveNode.SUCCESS)
59+
assert_bool(action.after_run_called).is_true()
60+
61+
func test_after_run_not_called_during_running() -> void:
62+
action.final_result = BeehaveNode.RUNNING
63+
64+
# Child is running, decorator returns RUNNING
65+
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
66+
assert_bool(action.after_run_called).is_false()
67+
68+
# Change to success, should call after_run
69+
action.final_result = BeehaveNode.SUCCESS
70+
assert_that(tree.tick()).is_equal(BeehaveNode.RUNNING)
71+
assert_bool(action.after_run_called).is_true()

0 commit comments

Comments
 (0)