diff options
Diffstat (limited to 'extractor')
-rw-r--r-- | extractor/action_format.gd | 2 | ||||
-rw-r--r-- | extractor/gat_format.gd | 8 | ||||
-rw-r--r-- | extractor/gnd_format.gd | 294 | ||||
-rw-r--r-- | extractor/grf.gd | 12 | ||||
-rw-r--r-- | extractor/rsw_format.gd | 519 |
5 files changed, 825 insertions, 10 deletions
diff --git a/extractor/action_format.gd b/extractor/action_format.gd index 97f6771..d61c590 100644 --- a/extractor/action_format.gd +++ b/extractor/action_format.gd @@ -49,7 +49,7 @@ func get_byte_length() -> int: static func from_bytes(bytes: ByteStream) -> ActionFormat: var action_format = ActionFormat.new() - bytes.seek(2) + bytes.advance(action_format.signature.length()) @warning_ignore("shadowed_variable") var version = Version.new() diff --git a/extractor/gat_format.gd b/extractor/gat_format.gd index 213972a..712657b 100644 --- a/extractor/gat_format.gd +++ b/extractor/gat_format.gd @@ -45,22 +45,22 @@ static func from_bytes(bytes: ByteStream) -> GATFormat: class Tile: ## Byte Type: f32 [br] ## Byte Length: 4 [br] - ## Orignal Coordinates_ (0, 0) + ## Orignal Coordinates: (0, 0) var bottom_left_altitude: int ## Byte Type: f32 [br] ## Byte Length: 4 [br] - ## Orignal Coordinates_ (1, 0) + ## Orignal Coordinates: (1, 0) var bottom_right_altitude: int ## Byte Type: f32 [br] ## Byte Length: 4 [br] - ## Orignal Coordinates_ (0, 1) + ## Orignal Coordinates: (0, 1) var top_left_altitude: int ## Byte Type: f32 [br] ## Byte Length: 4 [br] - ## Orignal Coordinates_ (1, 1) + ## Orignal Coordinates: (1, 1) var top_right_altitude: int ## Byte Type: u32 ?[br] diff --git a/extractor/gnd_format.gd b/extractor/gnd_format.gd new file mode 100644 index 0000000..f13956e --- /dev/null +++ b/extractor/gnd_format.gd @@ -0,0 +1,294 @@ +class_name GNDFormat + + +## Byte Length: 4 [br] +## GRAT +var signature: String = "GRGN" + +## Byte Type: u8 [br] +## Byte Length: 2 +var version: Version + +## Byte Type: i32 [br] +## Byte Length: 4 +var width: int + +## Byte Type: i32 [br] +## Byte Length: 4 +var height: int + +## Byte Type: f32 [br] +## Byte Length: 4 [br] +## Always 10. +var scale: float + +## Byte Type: i32 [br] +## Byte Length: 4 +var texture_count: int + +## Byte Type: i32 [br] +## Byte Length: 4 +## Always 80. +var texture_path_length: int + +## Byte Length: [member texture_count] * [member texture_path_length] [br] +var texture_paths: Array + +## Byte Type: i32 [br] +## Byte Length: 4 +var light_map_slice_count: int + +## Byte Type: i32 [br] +## Byte Length: 4 +var light_map_width: int + +## Byte Type: i32 [br] +## Byte Length: 4 +var light_map_slice_height: int + +## Byte Type: i32 [br] +## Byte Length: 4 +var light_map_cells_per_grid: int + +## Length: [member light_map_slice_count] +var light_map_slices: Array + +## Byte Type: i32 [br] +## Byte Length: 4 +var surface_count: int + +## Length: [member surface_count] +var surfaces: Array[Surface] + +## Length: [member width] * [member height] +var ground_mesh_cubes: Array[GroundMeshCube] + + +static func from_bytes(bytes: ByteStream) -> GNDFormat: + var gnd_format = GNDFormat.new() + + bytes.advance(gnd_format.signature.length()) + + @warning_ignore("shadowed_variable") + var version = Version.new() + version.major = bytes.decode_u8() + version.minor = bytes.decode_u8() + gnd_format.version = version + + if version.minor > 7: + print(version) + return gnd_format + + gnd_format.width = bytes.decode_s32() + gnd_format.height = bytes.decode_s32() + gnd_format.scale = bytes.decode_float() + gnd_format.texture_count = bytes.decode_s32() + gnd_format.texture_path_length = bytes.decode_s32() + + gnd_format.texture_paths = [] + for _n in gnd_format.texture_count: + gnd_format.texture_paths.append( + bytes.get_string_from_ascii(gnd_format.texture_path_length) + ) + + gnd_format.light_map_slice_count = bytes.decode_s32() + gnd_format.light_map_width = bytes.decode_s32() + gnd_format.light_map_slice_height = bytes.decode_s32() + gnd_format.light_map_cells_per_grid = bytes.decode_s32() + + #gnd_format.light_map_slices = [] + #for _n in gnd_format.light_map_slice_count: + #gnd_format.light_map_slices.append(LightMapSlice.from_bytes(bytes)) + bytes.advance( + gnd_format.light_map_slice_count * + gnd_format.light_map_width * + gnd_format.light_map_slice_height * + 4 + ) + + gnd_format.surface_count = bytes.decode_s32() + + gnd_format.surfaces = [] as Array[Surface] + for _n in gnd_format.surface_count: + gnd_format.surfaces.append(Surface.from_bytes(bytes)) + + gnd_format.ground_mesh_cubes = [] as Array[GroundMeshCube] + for _n in (gnd_format.width * gnd_format.height): + gnd_format.ground_mesh_cubes.append(GroundMeshCube.from_bytes(bytes)) + + #print(inst_to_dict(gnd_format)) + return gnd_format + + +class LightMapSlice: + ## Byte Type: i32 [br] + ## Byte Length: 4 + var pixel_format: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var width: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var height: int + + ## Byte Length: [member width] * [member height] + var shadow_map_pixels: ByteStream + + ## Byte Length: [member width] * [member height] + var light_map_pixels: ByteStream + + + static func from_bytes(bytes: ByteStream) -> LightMapSlice: + var slice = LightMapSlice.new() + + slice.pixel_format = bytes.decode_s32() + slice.width = bytes.decode_s32() + slice.height = bytes.decode_s32() + slice.shadow_map_pixels = bytes.get_buffer(slice.width * slice.height) + slice.light_map_pixels = bytes.get_buffer(slice.width * slice.height) + + #print(inst_to_dict(slice)) + return slice + + +class Surface: + ## Byte Type: f32 [br] + ## Byte Length: 4 + var u_bottom_left: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var u_bottom_right: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var u_top_left: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var u_top_right: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var v_bottom_left: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var v_bottom_right: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var v_top_left: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var v_top_right: float + + ## Byte Type: i16 [br] + ## Byte Length: 2 + var texture_index: int + + ## Byte Type: i16 [br] + ## Byte Length: 2 + var light_map_index: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var vertex_color_blue: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var vertex_color_green: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var vertex_color_red: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var vertex_color_alpha: int + + + func get_uvs() -> Dictionary: # Dictionary[Vector2, Vector2] + return { + Vector2(0, 0): Vector2(u_top_left, v_top_left), + Vector2(1, 0): Vector2(u_top_right, v_top_right), + Vector2(0, 1): Vector2(u_bottom_left, v_bottom_left), + Vector2(1, 1): Vector2(u_bottom_right, v_bottom_right), + } + + + static func from_bytes(bytes: ByteStream) -> Surface: + var surface = Surface.new() + + surface.u_bottom_left = bytes.decode_float() + surface.u_bottom_right = bytes.decode_float() + surface.u_top_left = bytes.decode_float() + surface.u_top_right = bytes.decode_float() + surface.v_bottom_left = bytes.decode_float() + surface.v_bottom_right = bytes.decode_float() + surface.v_top_left = bytes.decode_float() + surface.v_top_right = bytes.decode_float() + surface.texture_index = bytes.decode_s16() + surface.light_map_index = bytes.decode_s16() + surface.vertex_color_blue = bytes.decode_u8() + surface.vertex_color_green = bytes.decode_u8() + surface.vertex_color_red = bytes.decode_u8() + surface.vertex_color_alpha = bytes.decode_u8() + + #print(inst_to_dict(surface)) + return surface + + +class GroundMeshCube: + ## Byte Type: f32 [br] + ## Byte Length: 4 + ## upper left? + var bottom_left_height: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + ## upper right? + var bottom_right_height: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + ## lower left? + var top_left_height: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + ## lower right? + var top_right_height: float + + ## Byte Type: i32 [br] + ## Byte Length: 4 + ## top surface index? + var upwards_facing_surface_index: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + ## front surface index? + var northern_wall_surface_index: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + ## right surface index? + var eastern_wall_surface_index: int + + + static func from_bytes(bytes: ByteStream) -> GroundMeshCube: + var mesh = GroundMeshCube.new() + + mesh.bottom_left_height = bytes.decode_float() + mesh.bottom_right_height = bytes.decode_float() + mesh.top_left_height = bytes.decode_float() + mesh.top_right_height = bytes.decode_float() + mesh.upwards_facing_surface_index = bytes.decode_s32() + mesh.northern_wall_surface_index = bytes.decode_s32() + mesh.eastern_wall_surface_index = bytes.decode_s32() + + #print(inst_to_dict(mesh)) + return mesh diff --git a/extractor/grf.gd b/extractor/grf.gd index 9917a74..08f72ee 100644 --- a/extractor/grf.gd +++ b/extractor/grf.gd @@ -190,23 +190,25 @@ static func open(path: String): return grf -func extract(destination: String = "res://data"): +func extract(destination: String = "user://client_data"): + DirAccess.make_dir_recursive_absolute(destination) + for file_entry in file_entries: var file_path: String = file_entry.get_file_path() var base_directory = DirAccess.open(destination) - base_directory.make_dir_recursive("extracted/" + file_path.get_base_dir()) + base_directory.make_dir_recursive(file_path.get_base_dir()) - var file = FileAccess.open("%s/extracted/%s" % [destination, file_path], FileAccess.WRITE_READ) + var file = FileAccess.open("%s/%s" % [destination, file_path], FileAccess.WRITE_READ) file.store_buffer(file_entry.get_contents(file_access)) -func convert(destination: String = "res://data"): +func convert(destination: String = "res://client_data"): for file_entry in file_entries: var file_path: String = file_entry.get_file_path() var base_directory = DirAccess.open(destination) - var base_directory_path = "extracted/%s" % file_path.get_base_dir() + var base_directory_path = "%s" % file_path.get_base_dir() base_directory.make_dir_recursive(base_directory_path) base_directory.change_dir(base_directory_path) diff --git a/extractor/rsw_format.gd b/extractor/rsw_format.gd new file mode 100644 index 0000000..18874e2 --- /dev/null +++ b/extractor/rsw_format.gd @@ -0,0 +1,519 @@ +class_name RSWFormat + + +## Byte Length: 4 [br] +## GRAT +var signature: String = "GRSW" + +## Byte Type: u8 [br] +## Byte Length: 2 +var version: Version + +## Byte Type: u8, u32 [br] +## Byte Length: 1, 4 +## Versions: [2.2, 2.5] +var build_number: int + +## Byte Type: u8 [br] +## Byte Length: 1 +## Versions: [2.5] +var unknown_render_flag: int + +## Byte Type: u8 [br] +## Byte Length: 40 +var ini_file: String + +## Byte Type: u8 [br] +## Byte Length: 40 +var gnd_file: String + +## Byte Type: u8 [br] +## Byte Length: 40 +var gat_file: String + +## Byte Type: u8 [br] +## Byte Length: 40 +## Versions: [1.4] +var source_file: String + +var water_configuration: WaterConfiguration +var lighting_parameters: LightingParameters + +## Byte Type: u32 [br] +## Byte Length: 4 +var map_resource_count: int + +## Length: [member map_resource_count] +var map_resources: Array[MapResource] + +var quad_tree + + +static func from_bytes(bytes: ByteStream) -> RSWFormat: + var rsw_format = RSWFormat.new() + + bytes.advance(rsw_format.signature.length()) + + @warning_ignore("shadowed_variable") + var version = Version.new() + version.major = bytes.decode_u8() + version.minor = bytes.decode_u8() + rsw_format.version = version + + if version.major < 2: + print(version) + return rsw_format + + if version.major >= 2 and version.minor >= 5: + rsw_format.build_number = bytes.decode_u32() + rsw_format.unknown_render_flag = bytes.decode_u8() + elif version.major >= 2 and version.minor >= 2: + rsw_format.build_number = bytes.decode_u8() + + rsw_format.ini_file = bytes.get_string_from_utf8(40) + rsw_format.gnd_file = bytes.get_string_from_utf8(40) + rsw_format.gat_file = bytes.get_string_from_utf8(40) + rsw_format.source_file = bytes.get_string_from_utf8(40) + + if version.major >= 2 and version.minor <= 5: + rsw_format.water_configuration = WaterConfiguration.from_bytes(bytes) + + rsw_format.lighting_parameters = LightingParameters.from_bytes(bytes) + + bytes.advance(16) # map boundaries - bounding box # TODO + + rsw_format.map_resource_count = bytes.decode_u32() + rsw_format.map_resources = [] as Array[MapResource] + for _n in rsw_format.map_resource_count: + var resource_type = bytes.decode_u32() + var resource: MapResource + + if resource_type == MapResourceType.Animated3DModel: + resource = Animated3DModel.from_bytes(bytes) + elif resource_type == MapResourceType.DynamicLightSource: + resource = DynamicLightSource.from_bytes(bytes) + elif resource_type == MapResourceType.SpatialAudioSource: + resource = SpatialAudioSource.from_bytes(bytes) + elif resource_type == MapResourceType.ParticleEffectEmitter: + resource = ParticleEffectEmitter.from_bytes(bytes) + + rsw_format.map_resources.append(resource) + + # TODO: quadtree + + #print(inst_to_dict(rsw_format)) + return rsw_format + + +class WaterConfiguration: + ## Byte Type: f32 [br] + ## Byte Length: 4 + var water_level: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var water_type: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var wave_height: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var wave_speed: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var wave_pitch: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var water_animation_speed: int + + + static func from_bytes(bytes: ByteStream) -> WaterConfiguration: + var config = WaterConfiguration.new() + + config.water_level = bytes.decode_float() + config.water_type = bytes.decode_u32() + config.wave_height = bytes.decode_float() + config.wave_speed = bytes.decode_float() + config.wave_pitch = bytes.decode_float() + config.water_animation_speed = bytes.decode_u32() + + return config + + +class LightingParameters: + ## Byte Type: i32 [br] + ## Byte Length: 4 + var longitude: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var latitude: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var diffuse_color_r: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var diffuse_color_g: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var diffuse_color_b: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var ambient_color_r: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var ambient_color_g: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var ambient_color_b: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + ## Shadow Map Alpha + var intensity: int + + + func get_diffuse_color() -> Color: + return Color(diffuse_color_r, diffuse_color_g, diffuse_color_b) + + + func get_ambient_color() -> Color: + return Color(ambient_color_r, ambient_color_g, ambient_color_b) + + + static func from_bytes(bytes: ByteStream) -> LightingParameters: + var params = LightingParameters.new() + + params.longitude = bytes.decode_s32() + params.latitude = bytes.decode_s32() + params.diffuse_color_r = bytes.decode_float() + params.diffuse_color_g = bytes.decode_float() + params.diffuse_color_b = bytes.decode_float() + params.ambient_color_r = bytes.decode_float() + params.ambient_color_g = bytes.decode_float() + params.ambient_color_b = bytes.decode_float() + params.intensity = bytes.decode_float() + + return params + + +class MapResource: + pass + +enum MapResourceType { + Animated3DModel = 1, + DynamicLightSource = 2, + SpatialAudioSource = 3, + ParticleEffectEmitter = 4, +} + +enum ModelAnimationType { + None = 0, + Looping = 2, +} + +enum ParticlePresetEffect { + None = -1, + Hit_1 = 1, + Hit_2 = 2, + Hit_3 = 3, + Torch = 47, + TunaParty = 1097, +} + + +class Animated3DModel extends MapResource: + ## Byte Type: u8 [br] + ## Byte Length: 40 + var name: String + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var animation_type: ModelAnimationType + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var animation_speed_percent: float + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var collision_flags: int + + ## Byte Type: u8 [br] + ## Byte Length: 80 + var model_file: String + + ## Byte Type: u8 [br] + ## Byte Length: 80 + var root_node_name: String + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_x: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_y: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_z: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var rotation_x: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var rotation_y: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var rotation_z: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var scale_x: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var scale_y: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var scale_z: float + + + func get_position() -> Vector3: + return Vector3(position_x, position_y, position_z) + + + func get_rotation() -> Vector3: + return Vector3(rotation_x, rotation_y, rotation_z) + + + func get_scale() -> Vector3: + return Vector3(scale_x, scale_y, scale_z) + + + static func from_bytes(bytes: ByteStream) -> Animated3DModel: + var resource = Animated3DModel.new() + + resource.name = bytes.get_string_from_ascii(40) + resource.animation_type = bytes.decode_u32() + resource.animation_speed_percent = bytes.decode_float() + resource.collision_flags = bytes.decode_u32() + resource.model_file = bytes.get_string_from_ascii(80) + resource.root_node_name = bytes.get_string_from_utf8(80) + resource.position_x = bytes.decode_float() + resource.position_y = bytes.decode_float() + resource.position_z = bytes.decode_float() + resource.rotation_x = bytes.decode_float() + resource.rotation_y = bytes.decode_float() + resource.rotation_z = bytes.decode_float() + resource.scale_x = bytes.decode_float() + resource.scale_y = bytes.decode_float() + resource.scale_z = bytes.decode_float() + + #print(inst_to_dict(resource)) + return resource + + +class DynamicLightSource extends MapResource: + ## Byte Type: u8 [br] + ## Byte Length: 80 + var name: String + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_x: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_y: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_z: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var diffuse_color_r: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var diffuse_color_g: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var diffuse_color_b: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var light_range: float + + + func get_position() -> Vector3: + return Vector3(position_x, position_y, position_z) + + + func get_diffuse_color() -> Color: + return Color(diffuse_color_r, diffuse_color_g, diffuse_color_b) + + + static func from_bytes(bytes: ByteStream) -> DynamicLightSource: + var resource = DynamicLightSource.new() + + resource.name = bytes.get_string_from_ascii(80) + resource.position_x = bytes.decode_float() + resource.position_y = bytes.decode_float() + resource.position_z = bytes.decode_float() + resource.diffuse_color_r = bytes.decode_float() + resource.diffuse_color_g = bytes.decode_float() + resource.diffuse_color_b = bytes.decode_float() + resource.light_range = bytes.decode_float() + + #print(inst_to_dict(resource)) + return resource + + +class SpatialAudioSource extends MapResource: + ## Byte Type: u8 [br] + ## Byte Length: 80 + var name: String + + ## Byte Type: u8 [br] + ## Byte Length: 80 + var audio_file: String + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_x: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_y: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_z: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var volume_gain: float + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var width: int + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var height: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var audio_range: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var duration: float + + + func get_position() -> Vector3: + return Vector3(position_x, position_y, position_z) + + + func get_size() -> Vector2: + return Vector2(width, height) + + + static func from_bytes(bytes: ByteStream) -> SpatialAudioSource: + var resource = SpatialAudioSource.new() + + resource.name = bytes.get_string_from_ascii(80) + resource.audio_file = bytes.get_string_from_ascii(80) + resource.position_x = bytes.decode_float() + resource.position_y = bytes.decode_float() + resource.position_z = bytes.decode_float() + resource.volume_gain = bytes.decode_float() + resource.width = bytes.decode_u32() + resource.height = bytes.decode_u32() + resource.audio_range = bytes.decode_float() + resource.duration = bytes.decode_float() + + return resource + + +class ParticleEffectEmitter extends MapResource: + ## Byte Type: u8 [br] + ## Byte Length: 80 + var name: String + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_x: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_y: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var position_z: float + + @warning_ignore("enum_variable_without_default") + ## Byte Type: u32 [br] + ## Byte Length: 4 + var preset_effect: ParticlePresetEffect + + ## Byte Type: f32 [br] + ## Byte Length: 4 [br] + ## in frames; 60 means 1 second + var emission_delay: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var launch_parameter_a: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var launch_parameter_b: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var launch_parameter_c: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var launch_parameter_d: float + + + func get_position() -> Vector3: + return Vector3(position_x, position_y, position_z) + + + static func from_bytes(bytes: ByteStream) -> ParticleEffectEmitter: + var resource = ParticleEffectEmitter.new() + + resource.name = bytes.get_string_from_ascii(80) + resource.position_x = bytes.decode_float() + resource.position_y = bytes.decode_float() + resource.position_z = bytes.decode_float() + resource.preset_effect = bytes.decode_u32() + resource.emission_delay = bytes.decode_float() + resource.launch_parameter_a = bytes.decode_float() + resource.launch_parameter_b = bytes.decode_float() + resource.launch_parameter_c = bytes.decode_float() + resource.launch_parameter_d = bytes.decode_float() + + return resource |