class_name EgretMovieClipCache extends RefCounted # Egret MovieClip atlas loader. # It reads the old {mc,res} JSON files and returns frame textures plus offsets. var _cache: Dictionary = {} var _atlases: Dictionary = {} func frames(atlas_path: String, clip_name: String) -> Array[Dictionary]: return _frames(atlas_path, clip_name, "") func frames_for_label(atlas_path: String, clip_name: String, label_name: String) -> Array[Dictionary]: return _frames(atlas_path, clip_name, label_name) func _frames(atlas_path: String, clip_name: String, label_name: String) -> Array[Dictionary]: var cache_key := "%s:%s:%s" % [atlas_path, clip_name, label_name] if _cache.has(cache_key): var cached: Array[Dictionary] = _cache[cache_key] return cached.duplicate(false) var output: Array[Dictionary] = [] var atlas := _load_atlas(atlas_path) if atlas == null: _cache[cache_key] = output return output var json_path := _movie_clip_json_path(atlas_path) var file := FileAccess.open(json_path, FileAccess.READ) if file == null: _cache[cache_key] = output return output var parsed = JSON.parse_string(file.get_as_text()) if not parsed is Dictionary: _cache[cache_key] = output return output var clips = parsed.get("mc", {}) var resources = parsed.get("res", {}) if not clips is Dictionary or not resources is Dictionary: _cache[cache_key] = output return output var clip = clips.get(clip_name, {}) if not clip is Dictionary: _cache[cache_key] = output return output var raw_frames = clip.get("frames", []) if not raw_frames is Array: _cache[cache_key] = output return output.duplicate(false) var start_index := 0 var end_index: int = raw_frames.size() - 1 if not label_name.is_empty(): var label_range := _label_range(clip, label_name, raw_frames.size()) if label_range.is_empty(): _cache[cache_key] = output return output.duplicate(false) start_index = int(label_range.get("start", 0)) end_index = int(label_range.get("end", raw_frames.size() - 1)) var frame_rate := int(clip.get("frameRate", 12)) for index in range(start_index, end_index + 1): var raw_frame = raw_frames[index] if not raw_frame is Dictionary: continue var resource_key := str(raw_frame.get("res", "")) var resource = resources.get(resource_key, {}) if not resource is Dictionary: continue var width := float(resource.get("w", resource.get("width", 1))) var height := float(resource.get("h", resource.get("height", 1))) var texture := AtlasTexture.new() texture.atlas = atlas texture.region = Rect2( float(resource.get("x", 0)), float(resource.get("y", 0)), width, height ) output.append({ "texture": texture, "offset": Vector2(float(raw_frame.get("x", 0)), float(raw_frame.get("y", 0))), "size": Vector2(width, height), "frame_rate": frame_rate, }) _cache[cache_key] = output return output.duplicate(false) func _label_range(clip: Dictionary, label_name: String, frame_count: int) -> Dictionary: var labels = clip.get("labels", []) if not labels is Array: return {} for label in labels: if not label is Dictionary: continue if str(label.get("name", "")) != label_name: continue var start_index := clampi(int(label.get("frame", 1)) - 1, 0, maxi(frame_count - 1, 0)) var end_index := clampi(int(label.get("end", start_index + 1)) - 1, start_index, maxi(frame_count - 1, 0)) return { "start": start_index, "end": end_index, } return {} func _movie_clip_json_path(atlas_path: String) -> String: var basename := atlas_path.get_basename() var candidates := ["%s.json" % basename] if basename.ends_with("_tex"): candidates.append("%s_mc.json" % basename.trim_suffix("_tex")) for candidate in candidates: if ResourceLoader.exists(candidate) or FileAccess.file_exists(candidate): return candidate return candidates[0] func _load_atlas(atlas_path: String) -> Texture2D: if _atlases.has(atlas_path): return _atlases[atlas_path] if not ResourceLoader.exists(atlas_path): return null var atlas := load(atlas_path) as Texture2D _atlases[atlas_path] = atlas return atlas