forked from woofmeow/straywild
feat: add animated net gathering
This commit is contained in:
parent
b252b2f20c
commit
f0ddcccaad
38 changed files with 1298 additions and 76 deletions
139
tests/character_rig_validation.gd
Normal file
139
tests/character_rig_validation.gd
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
extends SceneTree
|
||||
|
||||
|
||||
const CHARACTER_SCENE: PackedScene = preload(
|
||||
"res://art/exported/characters/base/netfishing_base_character.glb"
|
||||
)
|
||||
|
||||
const EXPECTED_ANIMATIONS: Array[StringName] = [
|
||||
&"casting",
|
||||
&"casting_sit",
|
||||
&"draw",
|
||||
&"fighting",
|
||||
&"fighting_sit",
|
||||
&"fishing",
|
||||
&"fishing_sit",
|
||||
&"idle",
|
||||
&"idle_show",
|
||||
&"idle_sit",
|
||||
&"idle_sit_show",
|
||||
&"idle_sneak",
|
||||
&"pocket_idle_idle",
|
||||
&"pocket_idle_show",
|
||||
&"pocket_show_idle",
|
||||
&"pocket_show_show",
|
||||
&"pocket_sit_idle_idle",
|
||||
&"pocket_sit_idle_show",
|
||||
&"pocket_sit_show_idle",
|
||||
&"pocket_sit_show_show",
|
||||
&"pocket_walking_idle_idle",
|
||||
&"pocket_walking_idle_show",
|
||||
&"pocket_walking_show_idle",
|
||||
&"pocket_walking_show_show",
|
||||
&"release",
|
||||
&"release_sit",
|
||||
&"retract",
|
||||
&"retract_sit",
|
||||
&"running",
|
||||
&"running_show",
|
||||
&"sneaking",
|
||||
&"strike",
|
||||
&"walking",
|
||||
&"walking_show",
|
||||
]
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var character := CHARACTER_SCENE.instantiate()
|
||||
root.add_child(character)
|
||||
var skeleton := _find_first(character, "Skeleton3D") as Skeleton3D
|
||||
var animation_player := _find_first(
|
||||
character,
|
||||
"AnimationPlayer",
|
||||
) as AnimationPlayer
|
||||
assert(skeleton != null)
|
||||
assert(animation_player != null)
|
||||
|
||||
var socket_index := skeleton.find_bone("rod_socket")
|
||||
assert(socket_index >= 0)
|
||||
var socket_direction_index := skeleton.find_bone("rod_socket.001")
|
||||
assert(socket_direction_index >= 0)
|
||||
assert(skeleton.get_bone_parent(socket_direction_index) == socket_index)
|
||||
var animation_names := animation_player.get_animation_list()
|
||||
assert(animation_names.size() == EXPECTED_ANIMATIONS.size())
|
||||
for expected in EXPECTED_ANIMATIONS:
|
||||
assert(expected in animation_names, "Missing animation: %s" % expected)
|
||||
var animation := animation_player.get_animation(expected)
|
||||
if expected in [&"casting", &"draw", &"idle_sneak", &"sneaking", &"strike"]:
|
||||
var has_socket_track: bool = false
|
||||
for track_index in animation.get_track_count():
|
||||
has_socket_track = has_socket_track or String(
|
||||
animation.track_get_path(track_index)
|
||||
).contains("rod_socket")
|
||||
assert(
|
||||
has_socket_track,
|
||||
"Animation %s lost its authored rod socket pose." % expected,
|
||||
)
|
||||
|
||||
var arms := character.find_child(
|
||||
"body_arms",
|
||||
true,
|
||||
false,
|
||||
) as MeshInstance3D
|
||||
assert(arms != null)
|
||||
var blended_arm_vertices := _validate_mesh_weights(arms)
|
||||
assert(blended_arm_vertices > 0, "body_arms has no blended joint weights.")
|
||||
var body := character.find_child(
|
||||
"body_main",
|
||||
true,
|
||||
false,
|
||||
) as MeshInstance3D
|
||||
assert(body != null)
|
||||
_validate_mesh_weights(body)
|
||||
|
||||
print(
|
||||
"Character rig validation: PASS "
|
||||
+ "(animations=%d, blended arm vertices=%d)"
|
||||
% [EXPECTED_ANIMATIONS.size(), blended_arm_vertices]
|
||||
)
|
||||
character.queue_free()
|
||||
quit()
|
||||
|
||||
|
||||
func _validate_mesh_weights(mesh_instance: MeshInstance3D) -> int:
|
||||
var blended_vertices := 0
|
||||
for surface_index in mesh_instance.mesh.get_surface_count():
|
||||
var arrays := mesh_instance.mesh.surface_get_arrays(surface_index)
|
||||
var weights: PackedFloat32Array = arrays[Mesh.ARRAY_WEIGHTS]
|
||||
var vertices: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
|
||||
assert(not weights.is_empty())
|
||||
assert(not vertices.is_empty())
|
||||
var components_per_vertex := weights.size() / vertices.size()
|
||||
assert(components_per_vertex == 4 or components_per_vertex == 8)
|
||||
for offset in range(0, weights.size(), components_per_vertex):
|
||||
var total := 0.0
|
||||
var influence_count := 0
|
||||
for component in range(components_per_vertex):
|
||||
var weight := weights[offset + component]
|
||||
total += weight
|
||||
if weight > 0.000001:
|
||||
influence_count += 1
|
||||
assert(absf(total - 1.0) <= 0.0001)
|
||||
assert(influence_count <= 2)
|
||||
if influence_count == 2:
|
||||
blended_vertices += 1
|
||||
return blended_vertices
|
||||
|
||||
|
||||
func _find_first(parent: Node, class_name_to_find: String) -> Node:
|
||||
if parent.is_class(class_name_to_find):
|
||||
return parent
|
||||
for child in parent.get_children():
|
||||
var found := _find_first(child, class_name_to_find)
|
||||
if found != null:
|
||||
return found
|
||||
return null
|
||||
1
tests/character_rig_validation.gd.uid
Normal file
1
tests/character_rig_validation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://di6r1gphajq8u
|
||||
66
tests/gathering_marker_surface_validation.gd
Normal file
66
tests/gathering_marker_surface_validation.gd
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
extends SceneTree
|
||||
|
||||
const PlayerScene: PackedScene = preload("res://player/player.tscn")
|
||||
const GatheringControllerType = preload(
|
||||
"res://gathering/gathering_controller.gd"
|
||||
)
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var player := PlayerScene.instantiate() as Player
|
||||
root.add_child(player)
|
||||
player.set_process(false)
|
||||
player.set_physics_process(false)
|
||||
|
||||
var controller := GatheringControllerType.new()
|
||||
root.add_child(controller)
|
||||
controller.set("_player", player)
|
||||
|
||||
var wall := _make_surface(
|
||||
Vector3(2.0, 2.0, 0.1),
|
||||
Vector3(0.0, 0.9, -1.5),
|
||||
)
|
||||
root.add_child(wall)
|
||||
await physics_frame
|
||||
controller.call("_update_marker_target")
|
||||
var marker := controller.get("_marker") as MeshInstance3D
|
||||
assert(marker != null)
|
||||
assert(bool(controller.get("_marker_has_surface")))
|
||||
assert(marker.global_basis.y.dot(Vector3.BACK) > 0.99)
|
||||
|
||||
wall.queue_free()
|
||||
await process_frame
|
||||
var floor := _make_surface(
|
||||
Vector3(10.0, 0.2, 10.0),
|
||||
Vector3(0.0, -0.1, -1.7),
|
||||
)
|
||||
root.add_child(floor)
|
||||
await physics_frame
|
||||
controller.call("_update_marker_target")
|
||||
assert(bool(controller.get("_marker_has_surface")))
|
||||
assert(marker.global_basis.y.dot(Vector3.UP) > 0.99)
|
||||
assert(is_equal_approx(absf(marker.global_basis.determinant()), 1.0))
|
||||
|
||||
floor.queue_free()
|
||||
controller.queue_free()
|
||||
player.queue_free()
|
||||
await process_frame
|
||||
print("Gathering marker surface validation: PASS")
|
||||
quit()
|
||||
|
||||
|
||||
func _make_surface(size: Vector3, position: Vector3) -> StaticBody3D:
|
||||
var body := StaticBody3D.new()
|
||||
body.position = position
|
||||
body.collision_layer = 1
|
||||
body.collision_mask = 0
|
||||
var collision := CollisionShape3D.new()
|
||||
var shape := BoxShape3D.new()
|
||||
shape.size = size
|
||||
collision.shape = shape
|
||||
body.add_child(collision)
|
||||
return body
|
||||
1
tests/gathering_marker_surface_validation.gd.uid
Normal file
1
tests/gathering_marker_surface_validation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://ddsf88kuxgsqp
|
||||
|
|
@ -241,7 +241,25 @@ func _run_host() -> void:
|
|||
await create_timer(2.5).timeout
|
||||
player.apply_authoritative_network_input(_movement_input(2, false))
|
||||
await create_timer(2.5).timeout
|
||||
player.apply_authoritative_network_input(_movement_input(3, false, false))
|
||||
player.apply_authoritative_network_input(
|
||||
_movement_input(3, false, true, true)
|
||||
)
|
||||
await create_timer(2.0).timeout
|
||||
player.apply_authoritative_network_input(
|
||||
_movement_input(4, false, false, true)
|
||||
)
|
||||
await create_timer(2.0).timeout
|
||||
player.apply_authoritative_network_input(
|
||||
_movement_input(5, false, false, true, &"draw", 1)
|
||||
)
|
||||
await create_timer(1.0).timeout
|
||||
player.apply_authoritative_network_input(
|
||||
_movement_input(6, false, false, true, &"strike", 2)
|
||||
)
|
||||
await create_timer(0.5).timeout
|
||||
player.apply_authoritative_network_input(
|
||||
_movement_input(7, false, false, false, &"", 3)
|
||||
)
|
||||
var disconnect_deadline: int = Time.get_ticks_msec() + 8000
|
||||
while (
|
||||
Time.get_ticks_msec() < disconnect_deadline
|
||||
|
|
@ -284,16 +302,24 @@ func _run_client() -> void:
|
|||
var sprint_dust := host_avatar.get_node("%SprintDust") as SprintDustTrail
|
||||
var saw_running: bool = false
|
||||
var saw_walking: bool = false
|
||||
var saw_sneaking: bool = false
|
||||
var saw_idle_sneak: bool = false
|
||||
var saw_net_draw: bool = false
|
||||
var saw_net_strike: bool = false
|
||||
var saw_animation_advance: bool = false
|
||||
var saw_dust: bool = false
|
||||
var previous_animation: StringName = &""
|
||||
var previous_animation_position: float = -1.0
|
||||
var observation_deadline: int = Time.get_ticks_msec() + 7000
|
||||
var observation_deadline: int = Time.get_ticks_msec() + 14000
|
||||
while Time.get_ticks_msec() < observation_deadline:
|
||||
await process_frame
|
||||
var current_animation: StringName = animation_player.current_animation
|
||||
saw_running = saw_running or current_animation.begins_with("running")
|
||||
saw_walking = saw_walking or current_animation.begins_with("walking")
|
||||
saw_sneaking = saw_sneaking or current_animation == &"sneaking"
|
||||
saw_idle_sneak = saw_idle_sneak or current_animation == &"idle_sneak"
|
||||
saw_net_draw = saw_net_draw or current_animation == &"draw"
|
||||
saw_net_strike = saw_net_strike or current_animation == &"strike"
|
||||
var current_position: float = (
|
||||
animation_player.current_animation_position
|
||||
)
|
||||
|
|
@ -306,10 +332,23 @@ func _run_client() -> void:
|
|||
previous_animation = current_animation
|
||||
previous_animation_position = current_position
|
||||
saw_dust = saw_dust or sprint_dust.get_active_puff_count() > 0
|
||||
if saw_running and saw_walking and saw_animation_advance and saw_dust:
|
||||
if (
|
||||
saw_running
|
||||
and saw_walking
|
||||
and saw_sneaking
|
||||
and saw_idle_sneak
|
||||
and saw_net_draw
|
||||
and saw_net_strike
|
||||
and saw_animation_advance
|
||||
and saw_dust
|
||||
):
|
||||
break
|
||||
assert(saw_running)
|
||||
assert(saw_walking)
|
||||
assert(saw_sneaking)
|
||||
assert(saw_idle_sneak)
|
||||
assert(saw_net_draw)
|
||||
assert(saw_net_strike)
|
||||
assert(saw_animation_advance)
|
||||
assert(saw_dust)
|
||||
print("Movement multiplayer client validation: PASS")
|
||||
|
|
@ -325,6 +364,9 @@ func _movement_input(
|
|||
sequence: int,
|
||||
sprinting: bool,
|
||||
moving: bool = true,
|
||||
sneaking: bool = false,
|
||||
action_id: StringName = &"",
|
||||
action_sequence: int = 0,
|
||||
) -> Dictionary:
|
||||
return {
|
||||
"sequence": sequence,
|
||||
|
|
@ -332,12 +374,13 @@ func _movement_input(
|
|||
"camera_yaw": 0.0,
|
||||
"jump": false,
|
||||
"sprint": sprinting,
|
||||
"sneak": false,
|
||||
"sneak": sneaking,
|
||||
"slow_walk": false,
|
||||
"sitting": false,
|
||||
"casting": false,
|
||||
"animation_action": (
|
||||
NetworkPlayerAnimationProtocol.make_action_state()
|
||||
"animation_action": NetworkPlayerAnimationProtocol.make_action_state(
|
||||
action_id,
|
||||
action_sequence,
|
||||
),
|
||||
}
|
||||
|
||||
|
|
|
|||
157
tests/net_attachment_validation.gd
Normal file
157
tests/net_attachment_validation.gd
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
extends SceneTree
|
||||
|
||||
const NetAttachmentScene: PackedScene = preload(
|
||||
"res://player/net_attachment.tscn"
|
||||
)
|
||||
const FishingRodAttachmentScene: PackedScene = preload(
|
||||
"res://player/fishing_rod_attachment.tscn"
|
||||
)
|
||||
|
||||
var failures: Array[String] = []
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var host_skeleton := Skeleton3D.new()
|
||||
host_skeleton.name = "HostSkeleton"
|
||||
host_skeleton.add_bone("rod_socket")
|
||||
host_skeleton.add_bone("rod_socket.001")
|
||||
host_skeleton.set_bone_parent(1, 0)
|
||||
host_skeleton.set_bone_rest(
|
||||
1,
|
||||
Transform3D(
|
||||
Basis(Quaternion(Vector3.UP, Vector3.RIGHT)),
|
||||
Vector3(0.0, 0.25, 0.0),
|
||||
),
|
||||
)
|
||||
root.add_child(host_skeleton)
|
||||
|
||||
var attachment := NetAttachmentScene.instantiate() as BoneAttachment3D
|
||||
host_skeleton.add_child(attachment)
|
||||
await process_frame
|
||||
await process_frame
|
||||
|
||||
_check(
|
||||
attachment.bone_name == "rod_socket",
|
||||
"Attachment moved off rod_socket.",
|
||||
)
|
||||
var catching_net := attachment.get_node_or_null("CatchingNet") as Node3D
|
||||
_check(catching_net != null, "CatchingNet presentation root is missing.")
|
||||
if catching_net == null:
|
||||
_finish(host_skeleton)
|
||||
return
|
||||
_check(not catching_net.visible, "Net must start hidden.")
|
||||
_check(
|
||||
catching_net.position.is_zero_approx(),
|
||||
"Net direction alignment moved its hand anchor.",
|
||||
)
|
||||
_check(
|
||||
catching_net.basis.y.is_equal_approx(Vector3.UP),
|
||||
"Net working axis no longer follows rod_socket.",
|
||||
)
|
||||
_check(
|
||||
catching_net.basis.z.is_equal_approx(Vector3.RIGHT),
|
||||
"Net rear face did not follow the socket direction guide.",
|
||||
)
|
||||
|
||||
var model := catching_net.get_node_or_null("ModelMount/NetModel") as Node3D
|
||||
_check(model != null, "Imported net model is missing.")
|
||||
if model == null:
|
||||
_finish(host_skeleton)
|
||||
return
|
||||
var skeleton := model.find_child("Skeleton3D", true, false) as Skeleton3D
|
||||
_check(skeleton != null, "Imported net Skeleton3D is missing.")
|
||||
if skeleton == null:
|
||||
_finish(host_skeleton)
|
||||
return
|
||||
_check(
|
||||
skeleton.find_bone("net_root") >= 0,
|
||||
"Imported net_root bone is missing.",
|
||||
)
|
||||
_check(
|
||||
skeleton.find_bone("net_rim") >= 0,
|
||||
"Imported net_rim bone is missing.",
|
||||
)
|
||||
_check(
|
||||
skeleton.find_bone("net_mid") >= 0,
|
||||
"Imported net_mid bone is missing.",
|
||||
)
|
||||
_check(
|
||||
skeleton.find_bone("net_tip") >= 0,
|
||||
"Imported net_tip bone is missing.",
|
||||
)
|
||||
|
||||
var spring := skeleton.get_node_or_null("NetSpring") as SpringBoneSimulator3D
|
||||
_check(spring != null, "Net spring simulator was not created.")
|
||||
if spring != null:
|
||||
_check(spring.get_setting_count() == 1, "Net spring settings changed.")
|
||||
_check(
|
||||
spring.get_root_bone_name(0) == &"net_mid",
|
||||
"Net spring root must remain net_mid.",
|
||||
)
|
||||
_check(
|
||||
spring.get_end_bone_name(0) == &"net_tip",
|
||||
"Net spring end must remain net_tip.",
|
||||
)
|
||||
_check(not spring.active, "Hidden net spring must be suspended.")
|
||||
catching_net.visible = true
|
||||
await process_frame
|
||||
await process_frame
|
||||
_check(spring.active, "Visible net spring must be active.")
|
||||
_check(spring.get_joint_count(0) == 2, "Net spring chain is incomplete.")
|
||||
catching_net.visible = false
|
||||
await process_frame
|
||||
_check(not spring.active, "Hidden net spring did not suspend.")
|
||||
|
||||
for child: Node in model.find_children("*", "MeshInstance3D", true, false):
|
||||
var mesh_instance := child as MeshInstance3D
|
||||
if mesh_instance.mesh == null:
|
||||
continue
|
||||
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
||||
var material := mesh_instance.mesh.surface_get_material(surface_index)
|
||||
if material is BaseMaterial3D:
|
||||
_check(
|
||||
(material as BaseMaterial3D).texture_filter
|
||||
== BaseMaterial3D.TEXTURE_FILTER_NEAREST,
|
||||
"Net material does not use nearest texture sampling.",
|
||||
)
|
||||
|
||||
var rod_attachment := (
|
||||
FishingRodAttachmentScene.instantiate() as BoneAttachment3D
|
||||
)
|
||||
host_skeleton.add_child(rod_attachment)
|
||||
await process_frame
|
||||
var fishing_rod := rod_attachment.get_node("FishingRod") as Node3D
|
||||
_check(
|
||||
fishing_rod.position.is_zero_approx(),
|
||||
"Rod direction alignment moved its hand anchor.",
|
||||
)
|
||||
_check(
|
||||
fishing_rod.basis.y.is_equal_approx(Vector3.UP),
|
||||
"Rod working axis no longer follows rod_socket.",
|
||||
)
|
||||
_check(
|
||||
fishing_rod.basis.z.is_equal_approx(Vector3.RIGHT),
|
||||
"Rod rear face did not follow the socket direction guide.",
|
||||
)
|
||||
|
||||
_finish(host_skeleton)
|
||||
|
||||
|
||||
func _finish(host_skeleton: Skeleton3D) -> void:
|
||||
host_skeleton.queue_free()
|
||||
if failures.is_empty():
|
||||
print("Net attachment validation: PASS")
|
||||
quit(0)
|
||||
return
|
||||
for failure: String in failures:
|
||||
printerr("Net attachment validation: ", failure)
|
||||
quit(1)
|
||||
|
||||
|
||||
func _check(condition: bool, message: String) -> void:
|
||||
if not condition:
|
||||
failures.append(message)
|
||||
1
tests/net_attachment_validation.gd.uid
Normal file
1
tests/net_attachment_validation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://clnr8ofu1fqw5
|
||||
|
|
@ -15,11 +15,24 @@ func _initialize() -> void:
|
|||
1.25,
|
||||
)
|
||||
assert(NetworkPlayerAnimationProtocol.validate_state(emote))
|
||||
var paused_action := NetworkPlayerAnimationProtocol.make_action_state(
|
||||
&"strike",
|
||||
18,
|
||||
0.72,
|
||||
true,
|
||||
)
|
||||
assert(NetworkPlayerAnimationProtocol.validate_action_state(paused_action))
|
||||
assert(bool(paused_action["paused"]))
|
||||
var future_locomotion := NetworkPlayerAnimationProtocol.make_state(
|
||||
&"swimming_fast",
|
||||
false,
|
||||
)
|
||||
assert(NetworkPlayerAnimationProtocol.validate_state(future_locomotion))
|
||||
var sneaking := NetworkPlayerAnimationProtocol.make_state(
|
||||
NetworkPlayerAnimationProtocol.LOCOMOTION_SNEAKING,
|
||||
true,
|
||||
)
|
||||
assert(NetworkPlayerAnimationProtocol.validate_state(sneaking))
|
||||
var extra_future_field: Dictionary = emote.duplicate(true)
|
||||
extra_future_field["future_layer"] = {"id": "umbrella"}
|
||||
assert(NetworkPlayerAnimationProtocol.validate_state(extra_future_field))
|
||||
|
|
@ -45,5 +58,8 @@ func _initialize() -> void:
|
|||
"elapsed": INF,
|
||||
}
|
||||
assert(not NetworkPlayerAnimationProtocol.validate_state(invalid_elapsed))
|
||||
var invalid_paused: Dictionary = emote.duplicate(true)
|
||||
invalid_paused["action"]["paused"] = "yes"
|
||||
assert(not NetworkPlayerAnimationProtocol.validate_state(invalid_paused))
|
||||
print("Network player animation protocol validation: PASS")
|
||||
quit()
|
||||
|
|
|
|||
93
tests/player_gathering_animation_validation.gd
Normal file
93
tests/player_gathering_animation_validation.gd
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
extends SceneTree
|
||||
|
||||
|
||||
const PlayerScene: PackedScene = preload("res://player/player.tscn")
|
||||
const CrabBrown: FishData = preload(
|
||||
"res://fish/species/crab_brown/crab_brown.tres"
|
||||
)
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var player := PlayerScene.instantiate() as Player
|
||||
root.add_child(player)
|
||||
await process_frame
|
||||
player.set_process(false)
|
||||
player.set_physics_process(false)
|
||||
var animation_player := player.get_node(
|
||||
"Visuals/CharacterRig/AnimationPlayer"
|
||||
) as AnimationPlayer
|
||||
assert(animation_player != null)
|
||||
for required_animation: StringName in [
|
||||
&"draw",
|
||||
&"idle_sneak",
|
||||
&"sneaking",
|
||||
&"strike",
|
||||
]:
|
||||
assert(animation_player.has_animation(required_animation))
|
||||
|
||||
Input.action_press(&"sneak")
|
||||
player.velocity = Vector3.ZERO
|
||||
player.call("_update_character_animation")
|
||||
assert(animation_player.current_animation == &"idle_sneak")
|
||||
|
||||
player.velocity = Vector3(player.sneak_speed, 0.0, 0.0)
|
||||
player.call("_update_character_animation")
|
||||
assert(animation_player.current_animation == &"sneaking")
|
||||
|
||||
player.velocity = Vector3.ZERO
|
||||
assert(player.begin_net_draw_visual())
|
||||
assert(animation_player.current_animation == &"draw")
|
||||
player.call("_on_character_animation_finished", &"draw")
|
||||
assert(StringName(player.get("_animation_action_id")) == &"draw")
|
||||
assert(animation_player.current_animation == &"draw")
|
||||
|
||||
assert(player.play_net_strike_visual())
|
||||
assert(animation_player.current_animation == &"strike")
|
||||
player.call("_on_character_animation_finished", &"strike")
|
||||
assert(StringName(player.get("_animation_action_id")) == &"strike")
|
||||
assert(bool(player.get("_animation_action_paused")))
|
||||
assert(player.is_net_strike_held())
|
||||
assert(player.release_net_strike_hold())
|
||||
assert(StringName(player.get("_animation_action_id")).is_empty())
|
||||
assert(animation_player.current_animation == &"idle_sneak")
|
||||
|
||||
Input.action_release(&"sneak")
|
||||
assert(player.begin_net_draw_visual())
|
||||
assert(animation_player.current_animation == &"casting")
|
||||
player.cancel_net_action_visual()
|
||||
assert(StringName(player.get("_animation_action_id")).is_empty())
|
||||
assert(animation_player.current_animation == &"idle")
|
||||
Input.action_press(&"sneak")
|
||||
|
||||
assert(player.play_net_strike_visual())
|
||||
player.resolve_net_strike_visual(true)
|
||||
var crab_catch := FishCatch.new()
|
||||
crab_catch.fish = CrabBrown
|
||||
crab_catch.fish_id = CrabBrown.id
|
||||
crab_catch.weight_lb = CrabBrown.get_minimum_weight()
|
||||
crab_catch.display_scale = CrabBrown.get_display_scale_for_weight(
|
||||
crab_catch.weight_lb
|
||||
)
|
||||
crab_catch.sale_value = CrabBrown.get_sale_value_for_weight(
|
||||
crab_catch.weight_lb
|
||||
)
|
||||
crab_catch.ensure_identity()
|
||||
player.begin_catch_showcase(crab_catch)
|
||||
assert(not bool(player.get("_showcase_animation_active")))
|
||||
assert(player.get("_pending_net_showcase_catch") == crab_catch)
|
||||
player.call("_on_character_animation_finished", &"strike")
|
||||
assert(StringName(player.get("_animation_action_id")).is_empty())
|
||||
assert(bool(player.get("_showcase_animation_active")))
|
||||
var catch_display := player.get("_catch_display") as Node3D
|
||||
assert(catch_display != null and catch_display.visible)
|
||||
player.end_catch_showcase(Callable(), true)
|
||||
|
||||
Input.action_release(&"sneak")
|
||||
player.queue_free()
|
||||
await process_frame
|
||||
print("Player gathering animation validation: PASS")
|
||||
quit()
|
||||
1
tests/player_gathering_animation_validation.gd.uid
Normal file
1
tests/player_gathering_animation_validation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cba1j1dfxk5t4
|
||||
|
|
@ -4,6 +4,12 @@ const Gatherables: GatherableCatalog = preload(
|
|||
"res://gathering/catalog/gatherable_catalog.tres"
|
||||
)
|
||||
const FishCatalog: FishPool = preload("res://fish/pools/fish_catalog.tres")
|
||||
const GatheringControllerType = preload(
|
||||
"res://gathering/gathering_controller.gd"
|
||||
)
|
||||
|
||||
const REDUCED_CRAB_PIXEL_SIZE: float = 0.00025
|
||||
const REDUCED_CATCH_RING_RADIUS: float = 0.175
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
|
|
@ -27,6 +33,12 @@ func _validate_catalog_statuses() -> void:
|
|||
assert(brown.catch_data.logbook_section == FishData.LogbookSection.SHELLFISH)
|
||||
assert(brown.population == 2)
|
||||
assert(is_equal_approx(brown.charge_duration, 2.0))
|
||||
assert(
|
||||
is_equal_approx(
|
||||
brown.sprite_pixel_size,
|
||||
REDUCED_CRAB_PIXEL_SIZE,
|
||||
)
|
||||
)
|
||||
_validate_quality_behavior(brown)
|
||||
assert(is_equal_approx(brown.capture_respawn_min_seconds, 480.0))
|
||||
assert(is_equal_approx(brown.capture_respawn_max_seconds, 720.0))
|
||||
|
|
@ -47,6 +59,12 @@ func _validate_catalog_statuses() -> void:
|
|||
assert(entry.is_valid())
|
||||
assert(not entry.catch_data.active)
|
||||
assert(not entry.is_available())
|
||||
assert(
|
||||
is_equal_approx(
|
||||
entry.sprite_pixel_size,
|
||||
REDUCED_CRAB_PIXEL_SIZE,
|
||||
)
|
||||
)
|
||||
assert(entry.catch_data.collection_method == FishData.CollectionMethod.NET)
|
||||
assert(
|
||||
entry.catch_data.logbook_section
|
||||
|
|
@ -63,6 +81,15 @@ func _validate_catalog_statuses() -> void:
|
|||
assert(captured_delay >= 480.0 and captured_delay <= 720.0)
|
||||
assert(scared_delay >= 45.0 and scared_delay <= 90.0)
|
||||
|
||||
var gathering_controller := GatheringControllerType.new()
|
||||
assert(
|
||||
is_equal_approx(
|
||||
gathering_controller.marker_radius,
|
||||
REDUCED_CATCH_RING_RADIUS,
|
||||
)
|
||||
)
|
||||
gathering_controller.free()
|
||||
|
||||
|
||||
func _validate_quality_behavior(entry: GatherableData) -> void:
|
||||
var prior_speed: float = -1.0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue