@@ -6,23 +6,26 @@ enum State {
66 SHOOTING = 2 ,
77}
88
9- const PLAYER_AIM_TOLERANCE_DEGREES = 15
9+ const PLAYER_AIM_TOLERANCE_DEGREES = deg_to_rad ( 15 )
1010
1111const SHOOT_WAIT = 6.0
1212const AIM_TIME = 1
1313
1414const AIM_PREPARE_TIME = 0.5
1515const BLEND_AIM_SPEED = 0.05
1616
17- @export var health : int = 5
17+ signal exploded ()
18+
1819@export var test_shoot : bool = false
1920
20- var state = State .APPROACH
21+ @export var target_position := Vector3 ()
22+ @export var health : int = 5
23+ @export var state : State = State .APPROACH
24+ @export var dead = false
25+ @export var aim_preparing = AIM_PREPARE_TIME
2126
2227var shoot_countdown = SHOOT_WAIT
2328var aim_countdown = AIM_TIME
24- var aim_preparing = AIM_PREPARE_TIME
25- var dead = false
2629
2730var player = null
2831var orientation = Transform3D ()
@@ -56,15 +59,21 @@ func _ready():
5659 $ AnimationTree .active = true
5760 if test_shoot :
5861 shoot_countdown = 0.0
59- animation_tree ["parameters/state/transition_request" ] = "idle" # Go idle.
6062
63+ if dead :
64+ model .visible = false
65+ collision_shape .disabled = true
66+ animation_tree .active = false
67+
68+ animate ()
6169
6270func resume_approach ():
6371 state = State .APPROACH
6472 aim_preparing = AIM_PREPARE_TIME
6573 shoot_countdown = SHOOT_WAIT
6674
6775
76+ @rpc ("call_local" )
6877func hit ():
6978 if dead :
7079 return
@@ -74,39 +83,24 @@ func hit():
7483 health -= 1
7584 if health == 0 :
7685 dead = true
77- var base_xf = global_transform .basis
7886 animation_tree .active = false
7987 model .visible = false
8088 death .visible = true
8189 collision_shape .disabled = true
8290
83- death_shield1 .freeze = false
84- death_shield1 .get_node ("Col1" ).disabled = false
85- death_shield1 .get_node ("Col2" ).disabled = false
86-
87- death_shield2 .freeze = false
88- death_shield2 .get_node ("Col1" ).disabled = false
89- death_shield2 .get_node ("Col2" ).disabled = false
90-
91- death_head .freeze = false
92- death_head .get_node ("Col1" ).disabled = false
93- death_head .get_node ("Col2" ).disabled = false
94-
9591 death_detach_spark1 .emitting = true
9692 death_detach_spark2 .emitting = true
9793
98- death_shield1 .linear_velocity = 3 * (Vector3 .UP - base_xf .x ).normalized ()
99- death_shield2 .linear_velocity = 3 * (Vector3 .UP + base_xf .x ).normalized ()
100- death_head .linear_velocity = 3 * (Vector3 .UP ).normalized ()
101- death_shield1 .angular_velocity = (Vector3 (randf (), randf (), randf ()).normalized () * 2 - Vector3 .ONE ) * 10
102- death_shield2 .angular_velocity = (Vector3 (randf (), randf (), randf ()).normalized () * 2 - Vector3 .ONE ) * 10
103- death_head .angular_velocity = (Vector3 (randf (), randf (), randf ()).normalized () * 2 - Vector3 .ONE ) * 10
104-
105- death_shield1 .start_disappear_countdown ()
106- death_shield2 .start_disappear_countdown ()
107- death_head .start_disappear_countdown ()
94+ death_shield1 .explode ()
95+ death_shield2 .explode ()
96+ death_head .explode ()
10897
10998 explosion_sound .play ()
99+ exploded .emit ()
100+
101+ if multiplayer .is_server ():
102+ await get_tree ().create_timer (10.0 ).timeout
103+ queue_free ()
110104
111105
112106func shoot ():
@@ -136,38 +130,73 @@ func shoot():
136130 player .add_camera_shake_trauma (13 )
137131
138132
133+ func animate (delta := 0.0 ):
134+ if state == State .APPROACH :
135+ var to_player_local = target_position * global_transform
136+ # The front of the robot is +Z, and atan2 is zero at +X, so we need to use the Z for the X parameter (second one).
137+ var angle_to_player = atan2 (to_player_local .x , to_player_local .z )
138+ if angle_to_player > PLAYER_AIM_TOLERANCE_DEGREES :
139+ animation_tree ["parameters/state/transition_request" ] = "turn_left"
140+ elif angle_to_player < - PLAYER_AIM_TOLERANCE_DEGREES :
141+ animation_tree ["parameters/state/transition_request" ] = "turn_right"
142+ elif target_position == Vector3 .ZERO :
143+ animation_tree ["parameters/state/transition_request" ] = "idle"
144+ else :
145+ animation_tree ["parameters/state/transition_request" ] = "walk"
146+ else :
147+ animation_tree ["parameters/state/transition_request" ] = "idle"
148+
149+ # Aiming or shooting
150+ if target_position != Vector3 .ZERO :
151+ animation_tree ["parameters/aiming/blend_amount" ] = clamp (aim_preparing / AIM_PREPARE_TIME , 0 , 1 )
152+
153+ var to_cannon_local = target_position + Vector3 .UP * ray_mesh .global_transform
154+ var h_angle = rad_to_deg (atan2 ( to_cannon_local .x , - to_cannon_local .z ))
155+ var v_angle = rad_to_deg (atan2 ( to_cannon_local .y , - to_cannon_local .z ))
156+ var blend_pos = animation_tree .get ("parameters/aim/blend_position" )
157+ var h_motion = BLEND_AIM_SPEED * delta * - h_angle
158+ blend_pos .x += h_motion
159+ blend_pos .x = clamp (blend_pos .x , - 1 , 1 )
160+
161+ var v_motion = BLEND_AIM_SPEED * delta * v_angle
162+ blend_pos .y += v_motion
163+ blend_pos .y = clamp (blend_pos .y , - 1 , 1 )
164+
165+ animation_tree ["parameters/aim/blend_position" ] = blend_pos
166+
167+
139168func _physics_process (delta ):
169+ if dead :
170+ return
171+
172+ if not multiplayer .is_server ():
173+ animate (delta )
174+ return
175+
140176 if test_shoot :
141177 shoot ()
142178 test_shoot = false
143179
144- if dead :
145- return
146-
147180 if not player :
148- animation_tree ["parameters/state/transition_request" ] = "idle" # Go idle.
181+ target_position = Vector3 ()
182+ animate (delta )
149183 set_velocity (gravity * delta )
150184 set_up_direction (Vector3 .UP )
151185 move_and_slide ()
152186 return
153187
188+ target_position = player .global_transform .origin
189+
154190 if state == State .APPROACH :
155191 if aim_preparing > 0 :
156192 aim_preparing -= delta
157193 if aim_preparing < 0 :
158194 aim_preparing = 0
159- animation_tree ["parameters/aiming/blend_amount" ] = aim_preparing / AIM_PREPARE_TIME
160195
161- var to_player_local = player . global_transform . origin * global_transform
196+ var to_player_local = target_position * global_transform
162197 # The front of the robot is +Z, and atan2 is zero at +X, so we need to use the Z for the X parameter (second one).
163198 var angle_to_player = atan2 (to_player_local .x , to_player_local .z )
164- var tolerance = deg_to_rad (PLAYER_AIM_TOLERANCE_DEGREES )
165- if angle_to_player > tolerance :
166- animation_tree ["parameters/state/transition_request" ] = "turn_left"
167- elif angle_to_player < - tolerance :
168- animation_tree ["parameters/state/transition_request" ] = "turn_right"
169- else :
170- animation_tree ["parameters/state/transition_request" ] = "walk"
199+ if angle_to_player > - PLAYER_AIM_TOLERANCE_DEGREES and angle_to_player < PLAYER_AIM_TOLERANCE_DEGREES :
171200 # Facing player, try to shoot.
172201 shoot_countdown -= delta
173202 if shoot_countdown < 0 :
@@ -180,7 +209,6 @@ func _physics_process(delta):
180209 state = State .AIM
181210 aim_countdown = AIM_TIME
182211 aim_preparing = 0
183- animation_tree ["parameters/state/transition_request" ] = "idle"
184212 else :
185213 # Player not in sight, do nothing.
186214 shoot_countdown = SHOOT_WAIT
@@ -195,34 +223,19 @@ func _physics_process(delta):
195223 if aim_preparing > AIM_PREPARE_TIME :
196224 aim_preparing = AIM_PREPARE_TIME
197225
198- animation_tree ["parameters/aiming/blend_amount" ] = clamp (aim_preparing / AIM_PREPARE_TIME , 0 , 1 )
199226 aim_countdown -= delta
200227 if aim_countdown < 0 and state == State .AIM :
201228 var ray_origin = ray_from .global_transform .origin
202- var ray_to = player . global_transform . origin + Vector3 .UP # Above middle of player.
229+ var ray_to = target_position + Vector3 .UP
203230 var col = get_world_3d ().direct_space_state .intersect_ray (PhysicsRayQueryParameters3D .create (ray_origin , ray_to , 0xFFFFFFFF , [self ]))
204231 if not col .is_empty () and col .collider == player :
205232 state = State .SHOOTING
206- shoot_animation .play ("shoot" )
207233 shoot_countdown = SHOOT_WAIT
234+ play_shoot .rpc ()
208235 else :
209236 resume_approach ()
210237
211- if animation_tree .active :
212- var to_cannon_local = player .global_transform .origin + Vector3 .UP * ray_mesh .global_transform
213- var h_angle = rad_to_deg (atan2 ( to_cannon_local .x , - to_cannon_local .z ))
214- var v_angle = rad_to_deg (atan2 ( to_cannon_local .y , - to_cannon_local .z ))
215- var blend_pos = animation_tree .get ("parameters/aim/blend_position" )
216- var h_motion = BLEND_AIM_SPEED * delta * - h_angle
217- blend_pos .x += h_motion
218- blend_pos .x = clamp (blend_pos .x , - 1 , 1 )
219-
220- var v_motion = BLEND_AIM_SPEED * delta * v_angle
221- blend_pos .y += v_motion
222- blend_pos .y = clamp (blend_pos .y , - 1 , 1 )
223-
224- animation_tree ["parameters/aim/blend_position" ] = blend_pos
225-
238+ animate (delta )
226239 # Apply root motion to orientation.
227240 orientation *= Transform3D (animation_tree .get_root_motion_rotation (), animation_tree .get_root_motion_position ())
228241
@@ -240,13 +253,19 @@ func _physics_process(delta):
240253 global_transform .basis = orientation .basis
241254
242255
256+ @rpc ("call_local" )
257+ func play_shoot ():
258+ shoot_animation .play ("shoot" )
259+
260+
243261func shoot_check ():
244262 test_shoot = true
245263
246264
247265func _clip_ray (length ):
248266 var mesh_offset = ray_mesh .position .z
249- ray_mesh .get_surface_override_material (0 ).set_shader_parameter ("clip" , length + mesh_offset )
267+ if not OS .has_feature ("dedicated_server" ):
268+ ray_mesh .get_surface_override_material (0 ).set_shader_parameter ("clip" , length + mesh_offset )
250269
251270
252271func _on_area_body_entered (body ):
0 commit comments