def __init__( self, file_id=-1, speaker=-1, speaking=False, sprite=SpriteId(), voice=VoiceId(), bgm=-1, headshot=-1, mode=None, special=None, format=None, box_color=common.BOX_COLORS.yellow, box_type=common.BOX_TYPES.normal, ammo=-1, present=-1, bgd=-1, cutin=-1, flash=-1, movie=-1, img_filter=None, chapter=-1, scene=-1, room=-1, extra_val=-1, goto_ch=-1, goto_scene=-1, goto_room=-1, ): self.file_id = file_id self.speaker = speaker self.speaking = speaking self.sprite = sprite self.voice = voice self.bgm = bgm self.headshot = headshot self.mode = mode self.special = special self.format = format self.box_color = box_color self.box_type = box_type self.ammo = ammo self.present = present self.bgd = bgd self.cutin = cutin self.flash = flash self.movie = movie self.img_filter = img_filter self.chapter = chapter self.scene = scene self.room = room self.extra_val = extra_val
def load(self, filename): filename = filename.lower() if not filename in MTB_DIR: _LOGGER.error("Invalid MTB file: %s" % filename) return self.filename = filename script_dir = MTB_DIR[filename] self.script_pack = ScriptPack(script_dir, common.editor_config.data01_dir) # --- MTB FORMAT --- # A nested pak of our standard game paks. # Each file comes with three paks (four in the first game). # The first pak has a single file with information about the MTB as a whole. # The second one contains information about individual lines. # The third one, I dunno. Always seems to have 120 items? mtb = ConstBitStream(filename=os.path.join( common.editor_config.data01_dir, BIN_DIR, self.filename)) paks = [data for name, data in get_pak_files(mtb)] mtb_data = paks[0] line_data = paks[1] for name, data in get_pak_files(mtb_data): mtb_index = data.read("uintle:16") sprite_char = data.read("uintle:16") voice_char = data.read("uintle:16") sprite_id = data.read("uintle:16") sprite = SpriteId(SPRITE_TYPE.stand, sprite_char, sprite_id) for name, data in get_pak_files(line_data): file_id = data.read("uintle:16") voice_ch = data.read("uintle:16") voice_id = data.read("uintle:16") voice = VoiceId(voice_char, voice_ch, voice_id) self.script_pack[file_id].scene_info.sprite = sprite self.script_pack[file_id].scene_info.voice = voice
def load(self, filename): filename = filename.lower() if not filename in DIR_MAP: _LOGGER.error("Invalid nonstop file: %s" % filename) return self.filename = filename script_dir = DIR_MAP[filename] self.script_pack = ScriptPack(script_dir, common.editor_config.data01_dir) file_order = [] # --- NONSTOP FORMAT --- # XX XX -- ??? # XX XX -- Number of lines (not necessarily files) # # [68 bytes per line] # XX XX -- File number # XX XX XX XX # * 0x00000000 = normal line # * 0x01000100 = chatter # * 0x01000000 = ??? (Only appears in SDR2) # * 0x02000000 = ??? (Only appears in SDR2) # * 0x03000000 = ??? (Only appears in SDR2) # * 0x04000000 = ??? (Only appears in SDR2) # XX XX -- Ammo ID that reacts to this line. # XX XX -- Converted line ID that reacts to this line. # XX XX -- ??? # XX XX -- 1 = has a weak point, 0 = has no weak point # XX XX -- The amount of time before the next line is shown (in sixtieths of seconds (frames?)). # XX XX -- Unknown (Possibly line orientation? Only 0 in the first game, but sometimes 2 in the second.) # XX XX -- Effect used when transitioning text in. # XX XX -- Effect used when transitioning text out. # * 0: fade # * 1: spin in/out # * 2: zoom out # * 3: slide in/out # XX XX -- The amount of the the line stays on-screen (in sixtieths of seconds (frames?)). # XX XX -- Initial X position (text centered around this pos). # XX XX -- Initial Y position (text centered around this pos). # XX XX -- Text velocity. # XX XX -- Angle of motion. # XX XX -- Initial text zoom (in percent). # XX XX -- Change in zoom over time (in percent). # * 90% means it gradually shrinks. # * 100% means it stays the same size the whole time. # * 110% means it gradually grows. # XX XX -- 0 = no shake, 1 = shake # XX XX -- Rotate the text clockwise to this angle. # XX XX -- Text spins clockwise at this rate. # XX XX -- Speaker (00 00 if chatter) # XX XX -- Sprite ID (00 00 if chatter) # XX XX XX XX -- ??? # XX XX -- Voice index (FF FF if chatter) # XX XX -- ??? # XX XX -- Chapter # XX XX XX XX -- ??? (padding?) nonstop = ConstBitStream(filename=os.path.join( common.editor_config.data01_dir, NONSTOP_DIR, self.filename)) self.magic = nonstop.read(16) num_lines = nonstop.read('uintle:16') # Four byte header plus 68 bytes per line. if nonstop.len < (4 + (num_lines * 68)) * 8: raise Exception("Invalid nonstop file.") prev_non_chatter = -1 self.lines = [] for i in range(num_lines): line = NonstopLine() line.file_num = nonstop.read('uintle:16') line.line_type = nonstop.read(32) if line.line_type in LINE_TYPE_MAP: line.line_type = LINE_TYPE_MAP[line.line_type] line.ammo_id = nonstop.read('intle:16') line.converted_id = nonstop.read('intle:16') line.unknown1 = nonstop.read(16) line.weak_point = nonstop.read('uintle:16') line.delay = nonstop.read('intle:16') line.orientation = nonstop.read('intle:16') line.in_effect = nonstop.read('intle:16') line.out_effect = nonstop.read('intle:16') line.time_visible = nonstop.read('intle:16') line.x_start = nonstop.read('intle:16') line.y_start = nonstop.read('intle:16') line.velocity = nonstop.read('intle:16') line.angle = nonstop.read('intle:16') line.zoom_start = nonstop.read('intle:16') line.zoom_change = nonstop.read('intle:16') line.shake = nonstop.read('intle:16') line.rot_angle = nonstop.read('intle:16') line.spin_vel = nonstop.read('intle:16') line.speaker = nonstop.read('intle:16') # Since we mess with speaker a little bit later, we want to keep the ID for the sprite. line.char_id = line.speaker line.sprite_id = nonstop.read('intle:16') line.unknown3 = nonstop.read(32) line.voice_id = nonstop.read('intle:16') line.unknown4 = nonstop.read(16) line.chapter = nonstop.read('intle:16') line.unknown5 = nonstop.read(32) line.unknown6 = nonstop.read(64) format = copy.deepcopy(TEXT_FORMATS[common.SCENE_MODES.debate]) format.orient = TEXT_ORIENT.hor if line.orientation == 0 else TEXT_ORIENT.ver format.align = TEXT_ALIGN.center if format.orient == TEXT_ORIENT.ver: format.y = format.h format.x = format.w / 3.5 self.script_pack[line.file_num].scene_info.format = format if line.line_type == NONSTOP_LINE_TYPE.normal: prev_non_chatter = line.file_num # Fixing some weirdness. # if filename in ["nonstop_06_003.dat", "nonstop_06_005.dat", "nonstop_06_006.dat", "nonstop_06_007.dat"] and line.speaker == 16: # line.speaker = 15 # if filename[:10] == "nonstop_06" and int(filename[11:14]) >= 10 and line.speaker == 10: # line.speaker = 18 # if filename in ["nonstop_02_003.dat", "nonstop_02_005.dat", "nonstop_04_005.dat", "nonstop_04_006.dat"] and line.speaker == 10: # line.speaker = 18 self.script_pack[ line.file_num].scene_info.speaker = line.speaker sprite = SpriteId(SPRITE_TYPE.stand, line.char_id, line.sprite_id) self.script_pack[line.file_num].scene_info.sprite = sprite voice = VoiceId(line.speaker, line.chapter, line.voice_id) self.script_pack[line.file_num].scene_info.voice = voice self.script_pack[ line. file_num].scene_info.special = common.SCENE_SPECIAL.debate elif "hanron" in str(line.line_type): self.script_pack[ line.file_num].scene_info.speaker = line.speaker sprite = SpriteId(SPRITE_TYPE.stand, line.char_id, line.sprite_id) self.script_pack[line.file_num].scene_info.sprite = sprite voice = VoiceId(line.speaker, line.chapter, line.voice_id) self.script_pack[line.file_num].scene_info.voice = voice self.script_pack[ line. file_num].scene_info.special = common.SCENE_SPECIAL.hanron elif line.line_type == NONSTOP_LINE_TYPE.chatter: self.script_pack[line.file_num].scene_info.speaker = -1 self.script_pack[ line. file_num].scene_info.special = common.SCENE_SPECIAL.chatter self.script_pack[ line.file_num].scene_info.extra_val = prev_non_chatter else: _LOGGER.error("Invalid line type: %s" % line.line_type) file_order.append(line.file_num) self.lines.append(line) for index in xrange(len(self.script_pack)): if not index in file_order: file_order.append(index) self.script_pack.script_files = [ self.script_pack[i] for i in file_order ]
def to_scene_info(commands): cur_speaker = 0x1F cur_sprite = SpriteId() last_sprite = -1 cur_voice = VoiceId() cur_bgm = -1 cur_trialcam = None cur_sprite_obj = None is_option = False is_option_pt = False option_val = None is_floating = False show_tag = True is_speaking = True img_filter = False cur_mode = None cur_room = -1 check_obj = -1 check_char = -1 cur_ammo = -1 cur_cutin = -1 cur_bgd = -1 cur_flash = -1 cur_movie = -1 # Because we can put flashes on top of flashes. flash_stack = [] # If we set the speaker with a speaker tag, # don't let a voice file/sprite override it. speaker_set = False loaded_sprites = {} box_color = common.BOX_COLORS.orange box_type = common.BOX_TYPES.normal wrd_info = [] for op, params in commands: ######################################## ### Show line ######################################## if op == WRD_SHOW_LINE: line = params["line"] scene_info = SceneInfo() scene_info.file_id = line if not cur_mode == None: scene_info.mode = cur_mode scene_info.room = cur_room if not show_tag: scene_info.speaker = -1 else: scene_info.speaker = cur_speaker scene_info.speaking = is_speaking scene_info.sprite = cur_sprite scene_info.voice = cur_voice scene_info.bgm = cur_bgm scene_info.box_color = box_color scene_info.box_type = box_type scene_info.ammo = cur_ammo scene_info.bgd = cur_bgd scene_info.cutin = cur_cutin scene_info.flash = cur_flash scene_info.movie = cur_movie scene_info.img_filter = img_filter if not check_obj == -1: scene_info.special = common.SCENE_SPECIAL.checkobj scene_info.extra_val = check_obj check_obj = -1 check_char = -1 elif not check_char == -1: scene_info.special = common.SCENE_SPECIAL.checkchar scene_info.extra_val = check_char check_obj = -1 check_char = -1 if is_option_pt: scene_info.special = common.SCENE_SPECIAL.showopt scene_info.extra_val = option_val elif is_option: scene_info.speaker = -1 scene_info.special = common.SCENE_SPECIAL.option scene_info.extra_val = option_val #scene_info.trialcam = cur_trialcam ############################## ### Reset stuff ############################## cur_ammo = -1 scene_info.headshot = cur_sprite_obj if is_option: is_option = False option_val = None # is_option_pt = False is_floating = False speaker_set = False cur_voice = VoiceId() wrd_info.append(scene_info) ######################################## ### Image filter ######################################## elif op == WRD_FILTER_IMG: filter = params["filter"] if filter == 0x00: img_filter = IMG_FILTERS.unfiltered elif filter == 0x01: img_filter = IMG_FILTERS.sepia elif filter == 0x05: img_filter = IMG_FILTERS.inverted else: img_filter = IMG_FILTERS.unfiltered ######################################## ### Play movie ######################################## elif op == WRD_MOVIE: id = params["id"] state = params["state"] # Clear everything first, since a new call takes # priority of display over an old call. cur_bgd = -1 cur_flash = -1 cur_movie = -1 if state == 1: cur_movie = id ######################################## ### Show flash/cutin ######################################## elif op == WRD_FLASH: # If id < 1000, then it's a flash event. # If id > 3000, it's a cutin. # I've seen numbers between 1000 and 3000, but I'm not sure what they're for. id = params["id"] state = params["state"] # An actual cutin. if id >= 3000: if state == 1: cur_cutin = id - 3000 else: cur_cutin = -1 # A flash event. elif id < 1000: # Clear other stuff first, since a new call takes # priority of display over an old call. cur_bgd = -1 cur_movie = -1 # These flash IDs are special trial animations that kind of screw things up. invalid_flash = [500, 501, 502, 503] if (state in [1, 3, 4]) and (id not in invalid_flash): if id in flash_stack: flash_stack.remove(id) flash_stack.append(id) elif state == -1 and len(flash_stack) > 0: if id in flash_stack: flash_stack.remove(id) else: flash_stack.pop() if len(flash_stack) == 0: cur_flash = -1 else: cur_flash = flash_stack[-1] cur_trialcam = None cur_sprite = SpriteId() ######################################## ### Play a voice ######################################## elif op == WRD_VOICE: char_id = params["char_id"] chapter = params["chapter"] voice_id = params["voice_id"] cur_voice = VoiceId(char_id, chapter, voice_id) if not speaker_set: cur_speaker = char_id ######################################## ### Play BGM ######################################## elif op == WRD_BGM: id = params["id"] transition = params["transition"] cur_bgm = id ######################################## ### Get/update/clear ammo ######################################## elif op == WRD_SET_AMMO: # If ID == 0xFF & State == 0x00, clear all ammo from ElectroiD # State == 01: Add to ElectroiD # State == 02: Update info id = params["id"] state = params["state"] if state in [0x01, 0x02]: cur_ammo = id else: cur_ammo = -1 ######################################## ### Move camera during Class Trial ######################################## elif op == WRD_TRIAL_CAM: char_id = params["char_id"] motion = params["motion"] cur_trialcam = char_id if not char_id in loaded_sprites: cur_sprite = SpriteId() else: cur_sprite = loaded_sprites[char_id] ######################################## ### Load 3D map ######################################## elif op == WRD_LOAD_MAP: room = params["room"] state = params["state"] if state == 0: cur_room = room cur_mode = common.SCENE_MODES.normal ######################################## ### ######################################## elif op == WRD_SPRITE: obj_id = params["obj_id"] char_id = params["char_id"] sprite_id = params["sprite_id"] # 00 = Kill (?) # 01 = Show (?) # 03 = Fade out (?) # 04 = Hide (?) sprite_state = params["sprite_state"] sprite_type = params["sprite_type"] cur_sprite_obj = obj_id sprite_info = SpriteId(SPRITE_TYPE.bustup, char_id, sprite_id) loaded_sprites[char_id] = sprite_info last_sprite = char_id # If we have a camera, that means we might not be showing a sprite # just because we loaded it. Wait for the camera flag to point at a sprite. if cur_trialcam == None: if sprite_state in [0x00, 0x03, 0x04, 0x05, 0x07, 0x0A]: cur_sprite = SpriteId() continue else: cur_sprite = sprite_info if not speaker_set: cur_speaker = char_id ######################################## ### Set speaker tag ######################################## elif op == WRD_SPEAKER: id = params["id"] if id in common.CHAR_IDS: cur_speaker = id speaker_set = True elif id == 0x1C: cur_speaker = last_sprite speaker_set = True ######################################## ### ######################################## elif op == WRD_CHANGE_UI: # Element / State # 00 00 = "Speaking" window # 00 01 = "Thoughts" window # 01 00 = Hide text box (?) # 01 01 = Show text box (?) # 02 00 = Hide nametag (?) # 02 01 = Show nametag (?) # 03 00 = Orange box (?) # 03 01 = Green box (?) # 03 02 = Blue box (?) # 04 YY = ?? # 06 YY = ?? # 07 YY = ?? # 09 YY = ?? # 0B YY = ?? # 0D 00 = Stop shaking # 0D 01 = Start shaking # 33 00 = Normal, round text box (?) # 33 01 = Flat, black overlay (?) element = params["element"] state = params["state"] # Text box if element == 0x00: if state == 0x00: is_speaking = True elif state == 0x01: is_speaking = False show_tag = True # Text box elif element == 0x01: if state == 0x00: # Is it safe to assume that when we kill the text box # we are also killing any existing BGDs and the like? # cur_bgd = -1 # cur_cutin = -1 # cur_flash = -1 # cur_movie = -1 # cur_ammo = -1 pass # Speaker tag elif element == 0x02: if state == 0x00: show_tag = False elif state == 0x01: show_tag = True # Box type elif element == 0x33: if state == 0x00: box_type = common.BOX_TYPES.normal elif state == 0x01: box_type = common.BOX_TYPES.flat ######################################## ### Check a person ######################################## elif op == WRD_CHECK_CHAR: check_char = params["id"] check_obj = -1 ######################################## ### Check an object ######################################## elif op == WRD_CHECK_OBJ: check_obj = params["id"] check_char = -1 ######################################## ### An options section ######################################## elif op == WRD_CHOICE: # Option flag: # 01 = Choice ID (?) # 02 = Choice ID (?) # 03 = Choice ID (?) # 12 = Out of time (?) # 13 = Options prompt (?) # FF = End of options section (?) option_flag = params["flag"] if option_flag == 0x13: is_option_pt = True is_option = False option_val = "Prompt" elif option_flag == 0x12: is_option_pt = True is_option = False option_val = "Time Up" elif option_flag == 0xFF: is_option_pt = False is_option = False elif option_flag < 0x10: is_option = True is_option_pt = False option_val = option_flag ######################################## ### Show a BGD ######################################## elif op == WRD_BGD: id = params["id"] state = params["state"] # Clear everything first, since a new call takes # priority of display over an old call. cur_bgd = -1 cur_flash = -1 cur_movie = -1 if state == 1: cur_bgd = id cur_trialcam = None cur_sprite = SpriteId() ### if op == WRD_??? ### ### for op, params in commands ### if len(wrd_info) == 0: return None else: return wrd_info ### EOF ###
def to_scene_info(commands): cur_speaker = 0x1F cur_sprite = SpriteId() last_sprite = -1 cur_voice = VoiceId() cur_bgm = -1 cur_trialcam = None cur_sprite_obj = None is_option = False is_option_pt = False option_val = None show_tag = True is_speaking = True img_filter = IMG_FILTERS.unfiltered cur_mode = None cur_room = -1 check_obj = -1 check_char = -1 cur_ammo = -1 cur_cutin = -1 cur_present = -1 cur_bgd = -1 cur_flash = -1 cur_movie = -1 # Because we can put flashes on top of flashes. flash_stack = [] # If we set the speaker with a speaker tag, # don't let a voice file/sprite override it. speaker_set = False loaded_sprites = {} char_objects = {} box_color = common.BOX_COLORS.yellow box_type = common.BOX_TYPES.normal wrd_info = [] for op, params in commands: ######################################## ### Show line ######################################## if op == WRD_SHOW_LINE or op == WRD_CALL_SCRIPT or op == WRD_GOTO_SCRIPT: scene_info = SceneInfo() if op == WRD_SHOW_LINE: scene_info.file_id = params["line"] else: scene_info.file_id = None scene_info.goto_ch = params["chapter"] scene_info.goto_scene = params["scene"] scene_info.goto_room = params["room"] if not cur_mode == None: scene_info.mode = cur_mode scene_info.room = cur_room if not show_tag: scene_info.speaker = -1 else: scene_info.speaker = cur_speaker scene_info.speaking = is_speaking scene_info.sprite = cur_sprite scene_info.voice = cur_voice scene_info.bgm = cur_bgm scene_info.box_color = box_color scene_info.box_type = box_type scene_info.ammo = cur_ammo scene_info.bgd = cur_bgd scene_info.cutin = cur_cutin scene_info.present = cur_present scene_info.flash = cur_flash scene_info.movie = cur_movie scene_info.img_filter = img_filter if not check_obj == -1: scene_info.special = common.SCENE_SPECIAL.checkobj scene_info.extra_val = check_obj # check_obj = -1 # check_char = -1 elif not check_char == -1: scene_info.special = common.SCENE_SPECIAL.checkchar scene_info.extra_val = (char_objects[check_char] if check_char in char_objects else "ID %d" % check_char) # check_obj = -1 # check_char = -1 if is_option: # scene_info.speaker = -1 scene_info.special = common.SCENE_SPECIAL.option scene_info.extra_val = option_val elif is_option_pt: scene_info.special = common.SCENE_SPECIAL.showopt scene_info.extra_val = option_val #scene_info.trialcam = cur_trialcam ############################## ### Reset stuff ############################## # cur_ammo = -1 scene_info.headshot = cur_sprite_obj if is_option: is_option = False # option_val = None # is_option_pt = False speaker_set = False cur_voice = VoiceId() wrd_info.append(scene_info) ######################################## ### Image filter ######################################## elif op == WRD_FILTER_IMG: filter = params["filter"] if filter == 0x00: img_filter = IMG_FILTERS.unfiltered elif filter == 0x01: img_filter = IMG_FILTERS.sepia elif filter == 0x05: img_filter = IMG_FILTERS.inverted else: img_filter = IMG_FILTERS.unfiltered ######################################## ### Play movie ######################################## elif op == WRD_MOVIE: id = params["id"] state = params["state"] # Clear everything first, since a new call takes # priority of display over an old call. cur_bgd = -1 cur_flash = -1 cur_movie = -1 if state == 1: cur_movie = id ######################################## ### Show flash/cutin ######################################## elif op == WRD_FLASH: # If id < 1000, then it's a flash event. # if id >= 1000, then it's ammo # if id >= 1500, then it's an ammo update # if id >= 2000, then it's a present # If id >= 3000, it's a cutin. id = params["id"] state = params["state"] # An actual cutin. if id >= 3000: if state == 1: cur_cutin = id - 3000 else: cur_cutin = -1 elif id >= 2000: if state == 1: cur_present = id - 2000 else: cur_present = -1 elif id >= 1500: if state == 1: cur_ammo = id - 1500 else: cur_ammo = -1 elif id >= 1000: if state == 1: cur_ammo = id - 1000 else: cur_ammo = -1 # A flash event. elif id < 1000: # Clear other stuff first, since a new call takes # priority of display over an old call. cur_bgd = -1 cur_movie = -1 # These flash IDs are special trial animations that kind of screw things up. invalid_flash = range(200, 245) + [2, 27, 246, 247] if state > 0 and (id not in invalid_flash): if id in flash_stack: flash_stack.remove(id) flash_stack.append(id) elif state == -1 and len(flash_stack) > 0: if id in flash_stack: flash_stack.remove(id) else: flash_stack.pop() if len(flash_stack) == 0: cur_flash = -1 else: cur_flash = flash_stack[-1] cur_trialcam = None cur_sprite = SpriteId() ######################################## ### Play a voice ######################################## elif op == WRD_VOICE: char_id = params["char_id"] chapter = params["chapter"] voice_id = params["voice_id"] cur_voice = VoiceId(char_id, chapter, voice_id) if not speaker_set: cur_speaker = char_id ######################################## ### Play BGM ######################################## elif op == WRD_BGM: id = params["id"] transition = params["transition"] cur_bgm = id ######################################## ### Get/update/clear ammo ######################################## elif op == WRD_SET_AMMO: # If ID == 0xFF & State == 0x00, clear all ammo from ElectroiD # State == 01: Add to ElectroiD # State == 02: Update info id = params["id"] state = params["state"] # if state in [0x01, 0x02]: # cur_ammo = id # else: # cur_ammo = -1 ######################################## ### Move camera during Class Trial ######################################## elif op == WRD_TRIAL_CAM: char_id = params["char_id"] motion = params["motion"] cur_trialcam = char_id if not char_id in loaded_sprites: cur_sprite = SpriteId() else: cur_sprite = loaded_sprites[char_id] ######################################## ### Load 3D map ######################################## elif op == WRD_LOAD_MAP: room = params["room"] state = params["state"] if state == 0: cur_room = room cur_mode = common.SCENE_MODES.normal ######################################## ### ######################################## elif op == WRD_SPRITE: obj_id = params["obj_id"] char_id = params["char_id"] sprite_id = params["sprite_id"] # 00 = Kill (?) # 01 = Show (?) # 03 = Fade out (?) # 04 = Hide (?) sprite_state = params["sprite_state"] sprite_type = params["sprite_type"] cur_sprite_obj = obj_id if not obj_id in char_objects: char_objects[obj_id] = char_id sprite_info = SpriteId(SPRITE_TYPE.bustup, char_id, sprite_id) loaded_sprites[char_id] = sprite_info last_sprite = char_id # If we have a camera, that means we might not be showing a sprite # just because we loaded it. Wait for the camera flag to point at a sprite. if cur_trialcam == None: if sprite_state in [0x00, 0x03, 0x04, 0x05, 0x07, 0x0A, 0x10]: cur_sprite = SpriteId() continue # Kind of hackish. Sprites in 2D mode always seem to use obj_id 0 or 1, # but those IDs are also used in 3D mode, so I'm not sure how to tell # the difference between the two modes yet. So, we ignore any obj_id # greater than 1, because we don't care about the sprites in 3D. elif obj_id > 1: continue # Also kind of hackish. ID 98 is the "blank" model for characters # displayed in 3D, and sometimes, if the previous check misses when # the game is removing a character, it shows up as an "unknown" sprite. elif char_id == 98: cur_sprite = SpriteId() continue else: cur_sprite = sprite_info if not speaker_set: cur_speaker = char_id ######################################## ### Set speaker tag ######################################## elif op == WRD_SPEAKER: id = params["id"] # if id in common.CHAR_IDS: if id == 0x3E: if not speaker_set: cur_speaker = last_sprite speaker_set = True else: cur_speaker = id speaker_set = True ######################################## ### ######################################## elif op == WRD_CHANGE_UI: # Element / State # 00 00 = "Speaking" window # 00 01 = "Thoughts" window # 01 00 = Hide text box (?) # 01 01 = Show text box (?) # 02 00 = Hide nametag (?) # 02 01 = Show nametag (?) # 03 00 = Orange box (?) # 03 01 = Green box (?) # 03 02 = Blue box (?) # 04 YY = ?? # 06 YY = ?? # 07 YY = ?? # 09 YY = ?? # 0B YY = ?? # 0D 00 = Stop shaking # 0D 01 = Start shaking # 33 00 = Normal, round text box (?) # 33 01 = Flat, black overlay (?) element = params["element"] state = params["state"] # Text box if element == 0x00: if state == 0x00: is_speaking = True elif state == 0x01: is_speaking = False show_tag = True # Text box elif element == 0x01: if state == 0x00: # Is it safe to assume that when we kill the text box # we are also killing any existing BGDs and the like? # cur_bgd = -1 # cur_cutin = -1 # cur_flash = -1 # cur_movie = -1 # cur_ammo = -1 pass # Speaker tag # elif element == 0x02: # if state == 0x00: # show_tag = False # elif state == 0x01: # show_tag = True # Box type elif element == 0x33: if state == 0x00: box_type = common.BOX_TYPES.normal elif state == 0x01: box_type = common.BOX_TYPES.flat ######################################## ### Check a person ######################################## elif op == WRD_CHECK_CHAR: check_char = params["id"] if check_char == 255: check_char = -1 check_obj = -1 ######################################## ### Check an object ######################################## elif op == WRD_CHECK_OBJ: check_obj = params["id"] if check_obj == 255: check_obj = -1 check_char = -1 ######################################## ### An options section ######################################## elif op == WRD_CHOICE: # Option flag: # 01 = Choice ID (?) # 02 = Choice ID (?) # 03 = Choice ID (?) # 12 = Out of time (?) # 13 = Options prompt (?) # FF = End of options section (?) option_flag = params["flag"] if option_flag in [0x13, 0xFE]: is_option = False is_option_pt = True option_val = "Prompt" elif option_flag in [0x12, 0xFD]: is_option = False is_option_pt = True option_val = "Time Up" elif option_flag == 0xFF: is_option = False is_option_pt = False elif option_flag < 0x10: is_option = True is_option_pt = True option_val = "Option %d" % option_flag elif option_flag == 0xFC: is_option = False is_option_pt = True option_val = "Generic Wrong" else: is_option = False is_option_pt = True option_val = "Unknown" ######################################## ### Show a BGD ######################################## elif op == WRD_BGD: id = params["id"] state = params["state"] # Clear everything first, since a new call takes # priority of display over an old call. if state == 1: cur_bgd = id cur_flash = -1 cur_movie = -1 cur_trialcam = None cur_sprite = SpriteId() elif id == 65535 or id == -1: cur_bgd = -1 cur_flash = -1 cur_movie = -1 else: cur_bgd = -1 ### if op == WRD_??? ### ### for op, params in commands ### if len(wrd_info) == 0: return None else: return wrd_info ### EOF ###
def load(self, filename): filename = filename.lower() if not filename in MTB_DIR: _LOGGER.error("Invalid MTB file: %s" % filename) return self.filename = filename script_dir = MTB_DIR[filename] self.script_pack = ScriptPack(script_dir, common.editor_config.umdimage_dir) # --- MTB FORMAT --- # 12 bytes -- ??? # XX XX XX XX -- Table offset # 24 bytes -- ??? # # XX XX -- MTB Index # XX XX -- Char ID for sprites # XX XX -- Char ID for voices (chapter for voices is 0x63) # XX XX -- Initial sprite ID (?) mtb = ConstBitStream(filename=os.path.join( common.editor_config.umdimage_dir, self.filename)) mtb.read(12 * 8) table_offset = mtb.read("uintle:32") mtb.read(24 * 8) mtb_index = mtb.read("uintle:16") sprite_char = mtb.read("uintle:16") voice_char = mtb.read("uintle:16") sprite_id = mtb.read("uintle:16") sprite = SpriteId(SPRITE_TYPE.stand, sprite_char, sprite_id) # --- TABLE FORMAT --- # XX XX XX XX -- Number of files # # [for each line] # XX XX XX XX -- Offset (from table start) of voice info. # # -- Voice Info -- # XX XX -- File ID # XX XX -- Voice ID (with char ID above and the chapter ID 0x63, we know which voice file to use) mtb.bytepos = table_offset num_files = mtb.read("uintle:32") for i in range(num_files): voice_offset = mtb.read("uintle:32") # Store our position in the table so we can jump back to it. table_pos = mtb.bytepos mtb.bytepos = table_offset + voice_offset file_id = mtb.read("uintle:16") voice_id = mtb.read("uintle:16") # Chapter is 0x63, which is where the non-Trial voice samples are stored, # but I don't see the information actually recorded in the MTB files, # so I'm magic-numbering it here. voice = VoiceId(voice_char, 0x63, voice_id) self.script_pack[file_id].scene_info.sprite = sprite self.script_pack[file_id].scene_info.voice = voice # Restore it to our old position. mtb.bytepos = table_pos