class_name ActionFormat ## Byte Length: 2 [br] ## AC var signature: String = "AC" ## Byte Type: u8 [br] ## Byte Length: 2 var version: Version ## Byte Type: u16 [br] ## Byte Length: 2 var action_count: int ## Byte Type: u8 [br] ## Byte Length: 10 var reserved: PackedByteArray ## Length: [member action_count] var actions: Array[ActionData] ## Byte Type: u32 [br] ## Byte Length: 4 var event_count: int ## Length: [member event_count] var events: Array[Event] ## Byte Type: f32 [br] ## Byte Length: [member action_count] [br] ## The times are given in the unit of "ticks per displayed frame". [br] ## Multiply by 24 to get the time in milliseconds. var frame_times: Array[float] func get_byte_length() -> int: var length := 20 for action in actions: length += action.get_byte_length() length += Event.BYTE_LENGTH * event_count length += 4 * action_count return length static func from_bytes(bytes: ByteStream) -> ActionFormat: var action_format = ActionFormat.new() bytes.advance(action_format.signature.length()) @warning_ignore("shadowed_variable") var version = Version.new() version.minor = bytes.decode_u8() version.major = bytes.decode_u8() action_format.version = version if version.major < 2 and version.minor < 3: print(version) return action_format action_format.action_count = bytes.decode_u16() action_format.reserved = bytes.get_buffer(10).bytes action_format.actions = [] as Array[ActionData] for idx in action_format.action_count: var action = ActionData.from_bytes(bytes, version) action_format.actions.append(action) action_format.event_count = bytes.decode_u32() action_format.events = [] as Array[Event] for idx in action_format.event_count: var event = Event.from_bytes(bytes, version) action_format.events.append(event) action_format.frame_times = [] as Array[float] for idx in action_format.action_count: action_format.frame_times.append(bytes.decode_float()) return action_format func convert(name: String, sprites_path: String) -> Node2D: var node := Node2D.new() node.name = name node.set_script(load("res://extractor/actions.gd")) var animation_player := AnimationPlayer.new() animation_player.name = "AnimationPlayer" animation_player.unique_name_in_owner = true node.add_child(animation_player) animation_player.owner = node var sprite_layers := CanvasGroup.new() sprite_layers.name = "SpriteLayers" sprite_layers.unique_name_in_owner = true node.add_child(sprite_layers) sprite_layers.owner = node var track_properties = [ "animation", "frame", "speed_scale", "position", "self_modulate", "scale", "rotation_degrees", "flip_h", "visible", ] var sprite_frames := SpriteFrames.new() #sprite_frames.add_animation("default") for img_file_path in DirAccess.get_files_at(sprites_path): if img_file_path.ends_with(".png"): sprite_frames.add_frame("default", load("%s/%s" % [sprites_path, img_file_path])) var animation_library := AnimationLibrary.new() # get max number of sprite layers for all actions var action_sprite_layers_max_count = actions.reduce(func(accum, action: ActionData): return max(accum, action.motions.reduce(func(accum2, motion: Motion): return max(accum2, motion.sprite_layer_count) , 0)) , 0) # add Nodes for each sprite layer for sprite_layer_idx in action_sprite_layers_max_count: var sprite = AnimatedSprite2D.new() sprite.centered = false # 必要!! sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST sprite.sprite_frames = sprite_frames sprite.name = str(sprite_layer_idx).pad_zeros(3) sprite_layers.add_child(sprite) sprite.owner = node for action_idx in actions.size(): var action: ActionData = actions[action_idx] var frame_timing_base := ((frame_times[action_idx] * 24) / 1000) # TODO: check why this is necessary if sprites_path.contains("cursors") and action_idx == 0: frame_timing_base = ((frame_times[action_idx] * 24 * 2) / 1000) # add animation for each action var animation := Animation.new() animation.loop_mode = Animation.LOOP_LINEAR animation.length = frame_timing_base * action.motion_count animation_library.add_animation(str(action_idx).pad_zeros(3), animation) # get max number of sprite layers for current action motions var motion_sprite_layers_max_count = action.motions.reduce(func(accum, motion: Motion): return max(accum, motion.sprite_layer_count) , 0) # add animation tracks for each sprite layer for sprite_layer_idx in motion_sprite_layers_max_count: var sprite := sprite_layers.get_child(sprite_layer_idx) for property_idx in track_properties.size(): var track_idx = (sprite_layer_idx * track_properties.size()) + property_idx animation.add_track(Animation.TYPE_VALUE, track_idx) animation.value_track_set_update_mode(track_idx, Animation.UPDATE_DISCRETE) animation.track_set_path( track_idx, "%s:%s" % ["SpriteLayers/" + sprite.name, track_properties[property_idx]] ) for i in range(motion_sprite_layers_max_count, action_sprite_layers_max_count): var sprite := sprite_layers.get_child(i) var track_idx = animation.add_track(Animation.TYPE_VALUE) animation.track_set_path( track_idx, "%s:visible" % ["SpriteLayers/" + sprite.name] ) animation.track_insert_key(track_idx, 0.0, false) # add animation tracks for motion_idx in action.motions.size(): var motion: Motion = action.motions[motion_idx] # TODO: no animations to speak of available ? sprite_index = -1 #if motion.event_id == -1: #continue var timing = motion_idx * frame_timing_base var visible_key = 0 # add visible = false animation tracks to other sprite_layers for i in motion_sprite_layers_max_count: var track_idx = i * track_properties.size() + track_properties.find("visible") visible_key = animation.track_insert_key(track_idx, timing, false) for sprite_layer_idx in motion.sprite_layers.size(): var layer: SpriteLayer = motion.sprite_layers[sprite_layer_idx] if layer.sprite_index == -1: continue var track_base_idx = sprite_layer_idx * track_properties.size() animation.track_insert_key( track_base_idx + track_properties.find("animation"), timing, "default" ) animation.track_insert_key( track_base_idx + track_properties.find("frame"), timing, layer.sprite_index ) animation.track_insert_key( track_base_idx + track_properties.find("speed_scale"), timing, 1.0 ) var layer_image := sprite_frames.get_frame_texture("default", layer.sprite_index) var position: Vector2 = layer.get_position() - ceil(layer_image.get_size() / 2) # for fixing half pixel drawing var rotated = layer_image.get_size().rotated(deg_to_rad(layer.rotation_degrees)) var distance = layer_image.get_size() - rotated animation.track_insert_key( track_base_idx + track_properties.find("position"), timing, position + (distance / 2) ) animation.track_insert_key( track_base_idx + track_properties.find("self_modulate"), timing, layer.get_color() ) animation.track_insert_key( track_base_idx + track_properties.find("scale"), timing, layer.get_scale() ) animation.track_insert_key( track_base_idx + track_properties.find("rotation_degrees"), timing, layer.rotation_degrees ) animation.track_insert_key( track_base_idx + track_properties.find("flip_h"), timing, layer.flip_h ) animation.track_set_key_value( track_base_idx + track_properties.find("visible"), visible_key, true ) animation_player.add_animation_library("", animation_library) return node class ActionData: ## Byte Type: u32 [br] ## Byte Length: 4 var motion_count: int ## Length: [member motion_count] var motions: Array[Motion] func get_byte_length() -> int: var length := 4 for motion in motions: length += motion.get_byte_length() return length static func from_bytes(bytes: ByteStream, version: Version) -> ActionData: var action = ActionData.new() action.motion_count = bytes.decode_u32() action.motions = [] as Array[Motion] for idx in action.motion_count: var motion = Motion.from_bytes(bytes, version) action.motions.append(motion) return action class Motion: ## Byte Type: u8 [br] ## Byte Length: 32 var unused: PackedByteArray ## Byte Type: u32 [br] ## Byte Length: 4 var sprite_layer_count: int ## Length: [member sprite_layer_count] var sprite_layers: Array[SpriteLayer] ## Byte Type: i32 [br] ## Byte Length: 4 var event_id: int ## Byte Type: u32 [br] ## Byte Length: 4 var sprite_anchor_count: int ## Length: [member sprite_anchor_count] var sprite_anchors: Array[SpriteAnchor] func get_byte_length() -> int: return 44 + SpriteLayer.BYTE_LENGTH * sprite_layer_count + SpriteAnchor.BYTE_LENGTH * sprite_anchor_count static func from_bytes(bytes: ByteStream, version: Version): var motion = Motion.new() motion.unused = bytes.get_buffer(32).bytes motion.sprite_layer_count = bytes.decode_u32() motion.sprite_layers = [] as Array[SpriteLayer] for idx in motion.sprite_layer_count: var sprite_layer = SpriteLayer.from_bytes(bytes, version) motion.sprite_layers.append(sprite_layer) motion.event_id = bytes.decode_s32() motion.sprite_anchor_count = bytes.decode_u32() motion.sprite_anchors = [] as Array[SpriteAnchor] for idx in motion.sprite_anchor_count: var sprite_anchor = SpriteAnchor.from_bytes(bytes, version) motion.sprite_anchors.append(sprite_anchor) return motion class SpriteLayer: const BYTE_LENGTH := 44 ## Byte Type: i32 [br] ## Byte Length: 4 var position_u: int ## Byte Type: i32 [br] ## Byte Length: 4 var position_v: int ## Byte Type: i32 [br] ## Byte Length: 4 var sprite_index: int ## Byte Type: u32 [br] ## Byte Length: 4 var flip_h: int ## Byte Type: u8 [br] ## Byte Length: 1 var color_r: int ## Byte Type: u8 [br] ## Byte Length: 1 var color_g: int ## Byte Type: u8 [br] ## Byte Length: 1 var color_b: int ## Byte Type: u8 [br] ## Byte Length: 1 var color_a: int ## Byte Type: f32 [br] ## Byte Length: 4 ## Versions: [2.3, 2.4] var scale: float ## Byte Type: f32 [br] ## Byte Length: 4 ## Versions: [2.5] var scale_u: float ## Byte Type: f32 [br] ## Byte Length: 4 ## Versions: [2.5] var scale_v: float ## Byte Type: i32 [br] ## Byte Length: 4 var rotation_degrees: int ## Byte Type: u32 [br] ## Byte Length: 4 var type: int ## Byte Type: u32 [br] ## Byte Length: 4 var width: int ## Byte Type: u32 [br] ## Byte Length: 4 var height: int func get_position() -> Vector2: return Vector2(position_u, position_v) func get_color() -> Color: return Color8(color_r, color_g, color_b, color_a) func get_scale() -> Vector2: if scale: return Vector2(scale, scale) else: return Vector2(scale_u, scale_v) func get_size() -> Vector2: if width and height: return Vector2(width, height) else: return Vector2.ZERO static func from_bytes(bytes: ByteStream, version: Version): var sprite_layer = SpriteLayer.new() sprite_layer.position_u = bytes.decode_s32() sprite_layer.position_v = bytes.decode_s32() sprite_layer.sprite_index = bytes.decode_s32() sprite_layer.flip_h = bytes.decode_u32() sprite_layer.color_r = bytes.decode_u8() sprite_layer.color_g = bytes.decode_u8() sprite_layer.color_b = bytes.decode_u8() sprite_layer.color_a = bytes.decode_u8() if version.major == 2 and version.minor >= 4: sprite_layer.scale_u = bytes.decode_float() sprite_layer.scale_v = bytes.decode_float() else: sprite_layer.scale = bytes.decode_float() sprite_layer.rotation_degrees = bytes.decode_s32() sprite_layer.type = bytes.decode_u32() if version.to_string() == "2.5": sprite_layer.width = bytes.decode_u32() sprite_layer.height = bytes.decode_u32() return sprite_layer class SpriteAnchor: const BYTE_LENGTH := 16 ## Byte Type: u8 [br] ## Byte Length: 4 var unused: PackedByteArray ## Byte Type: i32 [br] ## Byte Length: 4 var position_u: int ## Byte Type: i32 [br] ## Byte Length: 4 var position_v: int ## Byte Type: u32 [br] ## Byte Length: 4 var flag: int func get_position() -> Vector2: return Vector2(position_u, position_v) static func from_bytes(bytes: ByteStream, _version: Version): var sprite_anchor = SpriteAnchor.new() sprite_anchor.unused = bytes.get_buffer(4).bytes sprite_anchor.position_u = bytes.decode_s32() sprite_anchor.position_v = bytes.decode_s32() sprite_anchor.flag = bytes.decode_u32() return sprite_anchor class Event: const BYTE_LENGTH := 40 ## Byte Type: u8 [br] ## Byte Length: 40 var name: String static func from_bytes(bytes: ByteStream, _version: Version): var event = Event.new() event.name = bytes.get_string_from_utf8(BYTE_LENGTH) return event