|  |  |  | @tool | 
					
						
							|  |  |  | extends Node3D | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func get_resource_name(resource: Resource) -> String: return resource.resource_path.get_file().trim_suffix('.tres') #todo 性能 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func snap_vector3(value: Vector3) -> Vector3: return Vector3(snap_float(value.x), snap_float(value.y), snap_float(value.z)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func snap_float(value: float) -> float: return floor(value/Setting.pixel_size) * Setting.pixel_size | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func dir_angle(dir: Vector2) -> float: | 
					
						
							|  |  |  |     dir.x = abs(dir.x) | 
					
						
							|  |  |  |     return dir.angle_to(Vector2.RIGHT) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func vector_reduce(vector: Vector2, reduce: float) -> Vector2: | 
					
						
							|  |  |  |     var length: float = vector.length() | 
					
						
							|  |  |  |     if length == 0: | 
					
						
							|  |  |  |         return vector | 
					
						
							|  |  |  |     var new_len    = max(length - reduce, 0) | 
					
						
							|  |  |  |     var scale_rate = new_len / length | 
					
						
							|  |  |  |     return vector * scale_rate | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func raycast_wall(from: Vector3, to: Vector3) -> bool: | 
					
						
							|  |  |  |     var space_state: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state | 
					
						
							|  |  |  |     var query: PhysicsRayQueryParameters3D     = PhysicsRayQueryParameters3D.create(from, to, 1) | 
					
						
							|  |  |  |     var result: Dictionary                     = space_state.intersect_ray(query) | 
					
						
							|  |  |  |     return result.size()>0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func raycast_character(shape: Shape3D, origin: Vector3, dir: Vector2) -> Array[Character]: | 
					
						
							|  |  |  |     var space_state: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state | 
					
						
							|  |  |  |     var query: PhysicsShapeQueryParameters3D   = PhysicsShapeQueryParameters3D.new() | 
					
						
							|  |  |  |     var angle: float                           = dir_angle(dir) | 
					
						
							|  |  |  |     query.shape = shape | 
					
						
							|  |  |  |     query.transform.origin = origin | 
					
						
							|  |  |  |     query.transform.basis = query.transform.basis.rotated(Vector3.UP, angle) | 
					
						
							|  |  |  |     var result: Array[Dictionary]              = space_state.intersect_shape(query) | 
					
						
							|  |  |  |     var result2: Array                         = result.map(func(v): return v["collider"]).filter(func(v): return v is Character) | 
					
						
							|  |  |  |     var result3: Array[Character] | 
					
						
							|  |  |  |     for node in result2: | 
					
						
							|  |  |  |         result3.append(node as Character) | 
					
						
							|  |  |  |     if result.size() > result3.size(): | 
					
						
							|  |  |  |         result3.append(null) #特殊处理 表示命中非Character物体 例如墙体或地面 | 
					
						
							|  |  |  |     return result3 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func clean_animation_lib(): | 
					
						
							|  |  |  |     #删除技能配置中没有的animation文件 | 
					
						
							|  |  |  |     #todo 非阻断 有空再说 | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func refresh_animation_lib(): | 
					
						
							|  |  |  |     #刷新animation库 使其与animation文件同步 | 
					
						
							|  |  |  |     var animation_library_path: String = "res://resource/skill_animation_library/animation_library.tres" | 
					
						
							|  |  |  |     var animation_library              = load(animation_library_path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var dir_path: String = "res://resource/skill_animation" | 
					
						
							|  |  |  |     var dir: DirAccess   = DirAccess.open(dir_path) | 
					
						
							|  |  |  |     for file in dir.get_files(): | 
					
						
							|  |  |  |         var path: String = dir_path + "/" + file | 
					
						
							|  |  |  |         var res          = load(path) | 
					
						
							|  |  |  |         if res is Animation: | 
					
						
							|  |  |  |             var animation: Animation   = res as Animation | 
					
						
							|  |  |  |             var animation_name: String = Util.get_resource_name(animation) | 
					
						
							|  |  |  |             print("refresh_animation_lib: ", animation_name) | 
					
						
							|  |  |  |             animation_library.add_animation(animation_name, animation) | 
					
						
							|  |  |  |             animation_library.animation_added.emit(animation_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ResourceSaver.save(animation_library, animation_library_path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func refresh_mesh_library(path_list: Array): | 
					
						
							|  |  |  |     print("refresh_mesh_library") | 
					
						
							|  |  |  |     var default_shape_full: Array[Variant] = [load("res://resource/mesh_library/default_shape_full.tres") as Shape3D, Transform3D.IDENTITY] | 
					
						
							|  |  |  |     var default_shape_half: Array[Variant] = [load("res://resource/mesh_library/default_shape_half.tres") as Shape3D, Transform3D.IDENTITY] | 
					
						
							|  |  |  |     var mesh_library: MeshLibrary          = load("res://resource/mesh_library/mesh_library.tres") as MeshLibrary | 
					
						
							|  |  |  |     for file_name_full in path_list: | 
					
						
							|  |  |  |         var mesh_name = file_name_full.get_file().split('-')[0].trim_suffix('.vox') | 
					
						
							|  |  |  |         print(mesh_name) | 
					
						
							|  |  |  |         var mesh: Mesh   = load(file_name_full) as Mesh | 
					
						
							|  |  |  |         var mesh_id: int = mesh_library.find_item_by_name(mesh_name) | 
					
						
							|  |  |  |         if mesh_id == -1: | 
					
						
							|  |  |  |             mesh_id = mesh_library.get_last_unused_item_id() | 
					
						
							|  |  |  |             mesh_library.create_item(mesh_id) | 
					
						
							|  |  |  |         mesh_library.set_item_name(mesh_id, mesh_name) | 
					
						
							|  |  |  |         mesh_library.set_item_mesh(mesh_id, mesh) | 
					
						
							|  |  |  |         if mesh_name.begins_with("f_"): | 
					
						
							|  |  |  |             mesh_library.set_item_shapes(mesh_id, default_shape_full) | 
					
						
							|  |  |  |         elif mesh_name.begins_with("h_"): | 
					
						
							|  |  |  |             mesh_library.set_item_shapes(mesh_id, default_shape_half) | 
					
						
							|  |  |  |         elif mesh_name.begins_with("s_"): | 
					
						
							|  |  |  |             var new_shape: Array[Variant] = [mesh.create_convex_shape(), Transform3D.IDENTITY] | 
					
						
							|  |  |  |             mesh_library.set_item_shapes(mesh_id, new_shape) | 
					
						
							|  |  |  |     var ids: PackedInt32Array  = mesh_library.get_item_list() | 
					
						
							|  |  |  |     var meshes: Array[Variant] = [] | 
					
						
							|  |  |  |     for id in ids: | 
					
						
							|  |  |  |         meshes.append(mesh_library.get_item_mesh(id)) | 
					
						
							|  |  |  |     var previews: Array[Texture2D] = EditorInterface.make_mesh_previews(meshes, 64) | 
					
						
							|  |  |  |     for i in range(len(ids)): | 
					
						
							|  |  |  |         mesh_library.set_item_preview(ids[i], previews[i]) | 
					
						
							|  |  |  |     ResourceSaver.save(mesh_library) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func refresh_all_animation_by_sprite_frames(sprite_frames: SpriteFrames): | 
					
						
							|  |  |  |     var dir_path: String           = "res://resource/skill_animation" | 
					
						
							|  |  |  |     var dir: DirAccess             = DirAccess.open(dir_path) | 
					
						
							|  |  |  |     var sprite_frames_name: String = sprite_frames.resource_path.get_file().split('-')[0] | 
					
						
							|  |  |  |     for file in dir.get_files(): | 
					
						
							|  |  |  |         var path: String = dir_path + "/" + file | 
					
						
							|  |  |  |         var res          = load(path) | 
					
						
							|  |  |  |         if not res is Animation: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         var animation: Animation     = res as Animation | 
					
						
							|  |  |  |         var sprite_frames_track: int = animation.find_track(NodePath("View:sprite_frames"), Animation.TYPE_VALUE) | 
					
						
							|  |  |  |         if sprite_frames_track < 0: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         var sprite_frames_track_key_count: int = animation.track_get_key_count(sprite_frames_track) | 
					
						
							|  |  |  |         if sprite_frames_track_key_count == 0: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         var sprite_frames_value: SpriteFrames = animation.track_get_key_value(sprite_frames_track, 0) as SpriteFrames | 
					
						
							|  |  |  |         var target_sprite_frames_name: String = sprite_frames_value.resource_path.get_file().split('-')[0] | 
					
						
							|  |  |  |         if sprite_frames_name != target_sprite_frames_name: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         var animation_track: int = animation.find_track(NodePath("View:animation"), Animation.TYPE_VALUE) | 
					
						
							|  |  |  |         if animation_track < 0: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         var animation_track_key_count: int = animation.track_get_key_count(animation_track) | 
					
						
							|  |  |  |         if animation_track_key_count == 0: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         var animation_value: String = animation.track_get_key_value(animation_track, 0) as String | 
					
						
							|  |  |  |         refresh_animation_by_sprite_frames(path, sprite_frames, animation_value, animation) | 
					
						
							|  |  |  |     Util.refresh_animation_lib() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func refresh_animation_by_sprite_frames(animation_path: String, sprite_frames: SpriteFrames, animation_name: String, animation: Animation) -> void: | 
					
						
							|  |  |  |     if not sprite_frames.has_animation(animation_name): | 
					
						
							|  |  |  |         print("动画不存在: ", animation_name) | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     var track_sprite_frames: int = animation.find_track(NodePath("View:sprite_frames"), Animation.TYPE_VALUE) | 
					
						
							|  |  |  |     if track_sprite_frames < 0: | 
					
						
							|  |  |  |         track_sprite_frames = animation.add_track(Animation.TYPE_VALUE) | 
					
						
							|  |  |  |         animation.track_set_path(track_sprite_frames, "View:sprite_frames") | 
					
						
							|  |  |  |         animation.track_insert_key(track_sprite_frames, 0, sprite_frames) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var track_animation: int = animation.find_track(NodePath("View:animation"), Animation.TYPE_VALUE) | 
					
						
							|  |  |  |     if track_animation < 0: | 
					
						
							|  |  |  |         track_animation = animation.add_track(Animation.TYPE_VALUE) | 
					
						
							|  |  |  |         animation.track_set_path(track_animation, "View:animation") | 
					
						
							|  |  |  |         animation.track_insert_key(track_animation, 0, animation_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var track_frame: int = animation.find_track(NodePath("View:frame"), Animation.TYPE_VALUE) | 
					
						
							|  |  |  |     if track_frame >= 0: | 
					
						
							|  |  |  |         animation.remove_track(track_frame) | 
					
						
							|  |  |  |     track_frame = animation.add_track(Animation.TYPE_VALUE) | 
					
						
							|  |  |  |     animation.track_set_path(track_frame, "View:frame") | 
					
						
							|  |  |  |     var animation_speed: float     = Setting.animation_frame_rate | 
					
						
							|  |  |  |     var animation_frame_count: int = sprite_frames.get_frame_count(animation_name) | 
					
						
							|  |  |  |     animation.length = animation_speed*animation_frame_count | 
					
						
							|  |  |  |     for i in range(0, animation_frame_count): | 
					
						
							|  |  |  |         var time = i * animation_speed | 
					
						
							|  |  |  |         animation.track_insert_key(track_frame, time, i) | 
					
						
							|  |  |  |     animation.value_track_set_update_mode(track_sprite_frames, Animation.UPDATE_DISCRETE) | 
					
						
							|  |  |  |     animation.value_track_set_update_mode(track_animation, Animation.UPDATE_DISCRETE) | 
					
						
							|  |  |  |     animation.value_track_set_update_mode(track_frame, Animation.UPDATE_DISCRETE) | 
					
						
							|  |  |  |     print("refresh_animation: ", animation_name, "(", animation_frame_count, " frames)") | 
					
						
							|  |  |  |     ResourceSaver.save(animation, animation_path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func get_all_skill_player_weapon() -> Array[SkillWeaponCfg]: | 
					
						
							|  |  |  |     var ret: Array[SkillWeaponCfg] = [] | 
					
						
							|  |  |  |     var dir_path: String           = "res://config/skill_player_weapon" | 
					
						
							|  |  |  |     var dir: DirAccess             = DirAccess.open(dir_path) | 
					
						
							|  |  |  |     for file in dir.get_files(): | 
					
						
							|  |  |  |         var path: String = dir_path + "/" + file | 
					
						
							|  |  |  |         var res          = load(path) | 
					
						
							|  |  |  |         if res is SkillWeaponCfg: | 
					
						
							|  |  |  |             ret.append(res) | 
					
						
							|  |  |  |     return ret | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func get_all_skill_player_basic() -> Array[SkillWeaponCfg]: | 
					
						
							|  |  |  |     var ret: Array[SkillWeaponCfg] = [] | 
					
						
							|  |  |  |     var dir_path: String           = "res://config/skill_player_basic" | 
					
						
							|  |  |  |     var dir: DirAccess             = DirAccess.open(dir_path) | 
					
						
							|  |  |  |     for file in dir.get_files(): | 
					
						
							|  |  |  |         var path: String = dir_path + "/" + file | 
					
						
							|  |  |  |         var res          = load(path) | 
					
						
							|  |  |  |         if res is SkillWeaponCfg: | 
					
						
							|  |  |  |             ret.append(res) | 
					
						
							|  |  |  |     return ret | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func get_skill_player_weapon_by_weapon(weapon: WeaponCfg) -> Array[SkillWeaponCfg]: | 
					
						
							|  |  |  |     var ret: Array[SkillWeaponCfg] = [] | 
					
						
							|  |  |  |     var dir_path: String           = "res://config/skill_player_weapon" | 
					
						
							|  |  |  |     var dir: DirAccess             = DirAccess.open(dir_path) | 
					
						
							|  |  |  |     for file in dir.get_files(): | 
					
						
							|  |  |  |         var path: String = dir_path + "/" + file | 
					
						
							|  |  |  |         var res          = load(path) | 
					
						
							|  |  |  |         if res is SkillWeaponCfg: | 
					
						
							|  |  |  |             if res.weapon == weapon: | 
					
						
							|  |  |  |                 ret.append(res) | 
					
						
							|  |  |  |     return ret |