def _fix_error(self, dungeons: List[DungeonDefinition], e: DungeonValidatorError): mappa = self.module.get_mappa() if isinstance(e, DungeonTotalFloorCountInvalidError): dungeons[e.dungeon_id].number_floors_in_group = e.expected_floor_count_in_group elif isinstance(e, InvalidFloorListReferencedError) or isinstance(e, FloorReusedError): dungeons[e.dungeon_id].mappa_index = self.module.mappa_generate_and_insert_new_floor_list() dungeons[e.dungeon_id].start_after = u8(0) dungeons[e.dungeon_id].number_floors = u8(1) dungeons[e.dungeon_id].number_floors_in_group = u8(1) elif isinstance(e, InvalidFloorReferencedError): valid_floors = len(mappa.floor_lists[e.dungeon.mappa_index]) - e.dungeon.start_after if valid_floors > 0: dungeons[e.dungeon_id].number_floors = u8_checked(valid_floors) else: mappa.floor_lists[e.dungeon.mappa_index].append(self.module.mappa_generate_new_floor()) dungeons[e.dungeon_id].number_floors = u8(1) elif isinstance(e, DungeonMissingFloorError): # Special case for Regigigas Chamber if self._is_regigias_special_case(dungeons, e): # Remove additional floors mappa.floor_lists[e.dungeon.mappa_index] = [ f for i, f in enumerate(mappa.floor_lists[e.dungeon.mappa_index]) if i not in e.floors_in_mappa_not_referenced ] else: # Add additional floors # TODO: Raise error or warning if we can't fix it? It should really always be consecutive. if min(e.floors_in_mappa_not_referenced) == e.dungeon.start_after + e.dungeon.number_floors: if check_consecutive(e.floors_in_mappa_not_referenced): max_floor_id = max(e.floors_in_mappa_not_referenced) dungeons[e.dungeon_id].number_floors = u8_checked(max_floor_id - dungeons[e.dungeon_id].start_after + 1)
def on_cr_td_floor_number_edited(self, widget, path, text): store: Gtk.ListStore = self.builder.get_object('dungeon_tileset_store') try: u8_checked(int(text)) except ValueError: return store[path][5] = text self._save_td()
def on_entry_range_max_changed(self, w, *args): try: val = u8_checked(int(w.get_text())) except ValueError: return self.item_p.range_max = val self.mark_as_modified()
def _convert_unknown_data_layer(self) -> bytes: """ Converts the unknown data layer back into bytes Every row is NRL encoded separately, because the game decodes the rows separately! """ from skytemple_files.common.types.file_types import FileType size = self.model.map_width_camera * self.model.map_height_camera assert self.model.unknown_data_block is not None assert size == len(self.model.unknown_data_block) layer_bytes = bytearray(size) layer_bytes_cursor = 0 # Each tile is separately encoded, so we also build them separately for row in range(0, self.model.map_height_camera): row_bytes = bytearray(int(size / self.model.map_height_camera)) for col in range(0, self.model.map_width_camera): i = row * self.model.map_width_camera + col actual_value = self.model.unknown_data_block[i] write_u8(row_bytes, u8_checked(actual_value), col) assert len(row_bytes) == int(size / self.model.map_height_camera) comp_row_bytes = FileType.GENERIC_NRL.compress(row_bytes) len_comp_row_bytes = len(comp_row_bytes) layer_bytes[layer_bytes_cursor:layer_bytes_cursor+len_comp_row_bytes] = comp_row_bytes layer_bytes_cursor += len_comp_row_bytes return layer_bytes[:layer_bytes_cursor]
def on_entry_base_pp_changed(self, w, *args): try: val = u8_checked(int(w.get_text())) except ValueError: return self.move.base_pp = val self.mark_as_modified()
def on_entry_message_id_changed(self, w, *args): try: val = u8_checked(int(w.get_text())) except ValueError: return self.move.message_id = val self.mark_as_modified()
def on_entry_ai_condition1_chance_changed(self, w, *args): try: val = u8_checked(int(w.get_text())) except ValueError: return self.move.ai_condition1_chance = val self.mark_as_modified()
def on_entry_number_chained_hits_changed(self, w, *args): try: val = u8_checked(int(w.get_text())) except ValueError: return self.move.number_chained_hits = val self.mark_as_modified()
def on_entry_max_upgrade_level_changed(self, w, *args): try: val = u8_checked(int(w.get_text())) except ValueError: return self.move.max_upgrade_level = val self.mark_as_modified()
def on_entry_miss_accuracy_changed(self, w, *args): try: val = u8_checked(int(w.get_text())) except ValueError: return self.move.miss_accuracy = val self.mark_as_modified()
def on_cr_other_iq_gain_edited(self, widget, path, text): store: Gtk.ListStore = self.builder.get_object('iq_gain_other_items') static_data = self.module.project.get_rom_module().get_static_data() try: val = u8_checked(int(text)) except ValueError: return typ: IqGainOtherItem = store[path][0] store[path][2] = text if typ == IqGainOtherItem.NECTAR: self.module.project.modify_binary( BinaryName.OVERLAY_29, lambda bin: HardcodedIq.set_nectar_gain(val, bin, static_data) ) self.module.mark_iq_as_modified() elif typ == IqGainOtherItem.WONDER_GUMMI: self.module.project.modify_binary( BinaryName.ARM9, lambda bin: HardcodedIq.set_wonder_gummi_gain(val, bin, static_data) ) self.module.mark_iq_as_modified() elif typ == IqGainOtherItem.JUICE_BAR_NECTAR: self.module.project.modify_binary( BinaryName.ARM9, lambda bin: HardcodedIq.set_juice_bar_nectar_gain(val, bin, static_data) ) self.module.mark_iq_as_modified()
def on_cr_stats_sp_def_edited(self, widget, path, text): store: Gtk.Store = self.builder.get_object('model_stats') try: v = u8_checked(int(text)) except ValueError: return store[path][7] = text self.lst_stats[int(store[path][0])].special_defense = v self._save()
def on_entry_palette_changed(self, w, *args): try: val = u8_checked(int(w.get_text())) except ValueError: return self.item_p.palette = val self.mark_as_modified() self._sprite_provider.reset() self.builder.get_object('draw_sprite').queue_draw()
def on_cr_tiles_room_id_edited(self, widget, path, text): store: Gtk.Store = self.builder.get_object('model_tiles') try: v = u8_checked(int(text)) except ValueError: return store[path][10] = text self.lst_tile[int(store[path][0])].room_id = v self._save()
def on_entry_text_speed_changed(self, widget, *args): try: val = u8_checked(int(widget.get_text())) except ValueError: return static_data = self.module.project.get_rom_module().get_static_data() self.module.project.modify_binary(BinaryName.ARM9, lambda bin: HardcodedTextSpeed.set_text_speed(val, bin, static_data)) self.module.mark_misc_settings_as_modified()
def _build_list(self): dungeon = self._get_current_dungeon() self.inter_d.list_dungeons[dungeon] = [] store_inter: Gtk.ListStore = self.builder.get_object('interrupt_store') for v in store_inter: e = InterDEntry() e.floor = u8_checked(v[0]) e.ent_type = InterDEntryType(v[1]) # type: ignore e.game_var_id = u16_checked(v[2]) e.param1 = u8_checked(v[3]) e.param2 = u8_checked(v[4]) e.continue_music = v[7] self.inter_d.list_dungeons[dungeon].append(e) self.module.mark_dungeon_interrupts_as_modified()
def to_bytes(self): mevo_data = bytearray(MEVO_ENTRY_LENGTH) write_u16(mevo_data, u16_checked(len(self.evos)), 0) for j, x in enumerate(self.evos): write_u16(mevo_data, x, j * 2 + 2) write_u8(mevo_data, u8_checked(len(self.eggs)), 0x12) for j, x in enumerate(self.eggs): write_u16(mevo_data, x, j * 2 + 0x14) return mevo_data
def write(self) -> bytes: buffer = bytearray(GRAPHIC_FONT_ENTRY_LEN * len(self.model.entries)) # Font Data for i, e in enumerate(self.model.entries): if e: write_u8(buffer, u8_checked(e.width), i * GRAPHIC_FONT_ENTRY_LEN + 0x00) write_u8(buffer, u8_checked(e.height), i * GRAPHIC_FONT_ENTRY_LEN + 0x01) write_u16(buffer, u16_checked(len(buffer)), i * GRAPHIC_FONT_ENTRY_LEN + 0x02) data_raw = e.tobytes("raw", "P") buffer += bytearray(data_raw) else: write_u32(buffer, u32(0xffff0000), i * GRAPHIC_FONT_ENTRY_LEN + 0x00) return buffer
def from_xml(cls, ele: Element) -> 'MappaMonster': validate_xml_tag(ele, XML_MONSTER) validate_xml_attribs(ele, [ XML_MONSTER__LEVEL, XML_MONSTER__WEIGHT, XML_MONSTER__WEIGHT2, XML_MONSTER__MD_INDEX ]) return cls( u8_checked(int(ele.get(XML_MONSTER__LEVEL))), u16_checked(int(ele.get(XML_MONSTER__WEIGHT))), u16_checked(int(ele.get(XML_MONSTER__WEIGHT2))), u16_checked(int(ele.get(XML_MONSTER__MD_INDEX))), )
def _adjust_actual_dimensions(self, canvas_width: int, canvas_height: int): """ Adjusts the actual dimensions specified by the image mode so the image can fit into those dimensions. """ width = 0 while 8 * (2**width) < canvas_width: width += 1 height = 0 while 8 * (2**height) < canvas_height: height += 1 if width >= 8 or height >= 8: # Max theoretical size: (2**(7+3), 2**(7+3)) = (1024, 1024) raise ValueError('Canvas size too large.') self.actual_dim = u8_checked(width + (height << 3))
def set_gummi_iq_gains(value: List[List[int]], arm9: bytearray, config: Pmd2Data, add_types_patch_applied: bool) -> None: dim, byte_size = IQ_GAINS_TABLES[add_types_patch_applied] block = config.binaries['arm9.bin'].symbols['IqGummiGain'] lst_flattened = list(chain.from_iterable(value)) if len(lst_flattened) != dim * dim: raise ValueError("IQ gain table does not match ROM size") if byte_size == 1: for i, b in enumerate(lst_flattened): write_u8(arm9, u8_checked(b), block.begin + i * byte_size) else: for i, b in enumerate(lst_flattened): write_u16(arm9, u16_checked(b), block.begin + i * byte_size)
def _mappa_group_count_invalid(self, dungeons: List[DungeonDefinition], mappa_index: int) -> Tuple[u8, List[int]]: count_floor_expected = 0 dungeons_to_check = [] invalid = [] for i, dungeon in enumerate(dungeons): if dungeon.mappa_index == mappa_index: count_floor_expected += dungeon.number_floors dungeons_to_check.append(i) for i in dungeons_to_check: if dungeons[i].number_floors_in_group != count_floor_expected: invalid.append(i) return u8_checked(count_floor_expected), invalid
def on_cr_location_edited(self, widget, path, text): match = PATTERN_LOCATION_ENTRY.match(text) if match is None: return try: location_id = u8_checked(int(match.group(1))) except ValueError: return # location_id: self._list_store[path][2] = str(location_id) # ent_name: self._list_store[path][5] = self._location_names[location_id]
def sir0_serialize_parts(self) -> Tuple[bytes, List[int], Optional[int]]: data = bytearray() pointers = [] pointer_offsets = [] for i, color_frames in enumerate(self.colors): pointers.append(u32_checked(len(data))) number_colors = len(color_frames) // 3 buffer_entry = bytearray(((number_colors + 1) * 4)) # Number colors write_u8(buffer_entry, u8_checked(number_colors), 0) # Unk write_u8(buffer_entry, u8_checked(self.durations_per_frame_for_colors[i]), 2) # Always one null color null_color = False if len(color_frames) == 0: null_color = True color_frames = [0, 0, 0] cursor = 4 for j, (r, g, b) in enumerate(chunk(color_frames, 3)): write_u8(buffer_entry, r, cursor) write_u8(buffer_entry, g, cursor + 1) write_u8(buffer_entry, b, cursor + 2) write_u8(buffer_entry, u8(128 if not null_color else 0), cursor + 3) cursor += 4 data += buffer_entry data_offset = cursor = len(data) data += bytes(4 * len(pointers)) for pnt in pointers: write_u32(data, pnt, cursor) pointer_offsets.append(cursor) cursor += 4 return data, pointer_offsets, data_offset
def on_cr_belly_heal_edited(self, widget, path, text, *, type_id): store: Gtk.ListStore = self.builder.get_object('tree_belly_gain').get_model() try: val = u8_checked(int(text)) except ValueError: return store[path][type_id + 2] = text gummi_id = store[path][0] arm9 = self.module.project.get_binary(BinaryName.ARM9) patch_applied = self.module.project.is_patch_applied("AddTypes") static_data = self.module.project.get_rom_module().get_static_data() gains = HardcodedIq.get_gummi_belly_heal(arm9, static_data, patch_applied) gains[type_id][gummi_id] = val self.module.project.modify_binary( BinaryName.ARM9, lambda bin: HardcodedIq.set_gummi_belly_heal(gains, bin, static_data, patch_applied) ) self.module.mark_iq_as_modified()
def on_cr_skill_to_group(self, widget, path, *, group_id): selected = not widget.get_active() store: Gtk.ListStore = self.builder.get_object('tree_iq_skills').get_model() store[path][group_id + 4] = selected skill_id = u8_checked(int(store[path][0])) arm9 = self.module.project.get_binary(BinaryName.ARM9) static_data = self.module.project.get_rom_module().get_static_data() assert self.module.project.is_patch_applied(PATCH_IQ_SKILL_GROUPS) groups = IqGroupsSkills.read_compressed(arm9, static_data) if selected: if skill_id not in groups[group_id]: groups[group_id].append(skill_id) groups[group_id].sort() else: if skill_id in groups[group_id]: groups[group_id].remove(skill_id) self.module.project.modify_binary( BinaryName.ARM9, lambda bin: IqGroupsSkills.write_compressed(bin, groups, static_data) ) self.module.mark_iq_as_modified()
def change_floor_count(self, dungeon_id, number_floors_new): #TODO: Unchanged """ This will update the floor count for the given dungeon: - Will add or remove floors from the dungeon's mappa entry, starting at the end of this dungeon's floor based on the current floor count for this dungeon - Update the dungeon's data entry (floor count + total floor count in group) - For all other dungeons in the same group: Update data entries (total floor count + start offset) - Regenerate the UI in SkyTemple (dungeon tree) """ dungeon_definitions = self.get_dungeon_list() is_group: Union[Literal[False], DungeonGroup] = False for dungeon_or_group in self.load_dungeons(): if dungeon_or_group == dungeon_id: break elif isinstance(dungeon_or_group, DungeonGroup): if dungeon_id in dungeon_or_group.dungeon_ids: is_group = dungeon_or_group break mappa_index = dungeon_definitions[dungeon_id].mappa_index floor_offset = dungeon_definitions[dungeon_id].start_after number_floors_old = dungeon_definitions[dungeon_id].number_floors floor_list = self.get_mappa().floor_lists[mappa_index] floors_added = number_floors_new - number_floors_old # Update Mappa if floors_added == 0: return # nothing to do if floors_added < 0: # We removed floors for _ in range(0, -floors_added): del floor_list[floor_offset + number_floors_new] else: # We added floors last_floor_xml = floor_list[floor_offset + number_floors_old - 1].to_xml() for i in range(0, floors_added): floor_list.insert(floor_offset + number_floors_old + i, MappaFloor.from_xml(last_floor_xml)) # Update floor ranks self.extend_nb_floors_ranks(dungeon_id, floor_offset+number_floors_old, floors_added, self.get_floor_rank( # type: ignore dungeon_id, floor_offset+number_floors_old-1 )) if self.has_floor_ranks(): self.project.mark_as_modified(FLOOR_RANKS) # Update mission forbidden self.extend_nb_floors_mf(dungeon_id, floor_offset+number_floors_old, floors_added, self.get_floor_mf( # type: ignore dungeon_id, floor_offset+number_floors_old-1 )) if self.has_mission_forbidden(): self.project.mark_as_modified(FLOOR_MISSION_FORBIDDEN) # Update dungeon data dungeon_definitions[dungeon_id].number_floors = number_floors_new if is_group: new_total_floor_count = sum([dungeon_definitions[x].number_floors for x in is_group.dungeon_ids]) dungeon_definitions[dungeon_id].number_floors_in_group = new_total_floor_count for dungeon_in_group in (x for x in is_group.dungeon_ids if x != dungeon_id): # Update dungeon data of group floors if dungeon_definitions[dungeon_in_group].start_after > dungeon_definitions[dungeon_id].start_after: dungeon_definitions[dungeon_in_group].start_after += floors_added dungeon_definitions[dungeon_in_group].number_floors_in_group = u8_checked(new_total_floor_count) else: dungeon_definitions[dungeon_id].number_floors_in_group = number_floors_new # Re-count floors for i, floor in enumerate(floor_list): floor.layout.floor_number = i + 1 # Mark as changed self.mark_dungeon_as_modified(dungeon_id, True) self.save_dungeon_list(dungeon_definitions) if is_group: for dungeon_in_group in is_group.dungeon_ids: self._regenerate_dungeon_floors(dungeon_in_group, dungeon_definitions[dungeon_in_group].start_after) else: self._regenerate_dungeon_floors(dungeon_id, floor_offset) recursive_generate_item_store_row_label(self._tree_model[self._root_iter])
def on_partner_start_level_changed(self, w: Gtk.Entry): try: val = u8_checked(int(w.get_text())) except ValueError: return self.module.set_starter_level_partner(val)
def regroup_dungeons(self, new_groups: Iterable[Union[DungeonGroup, int]]): """ Apply new dungeon groups. This updates the dungeon list file, the mappa files and the UI tree. start_ids of the DungeonGroups may be empty, it is ignored and calculated from the current dungeons instead. The the list MUST contain all regular dungeons (before DOJO_DUNGEONS_FIRST), just like self.load_dungeons would return it. """ mappa = self.get_mappa() old_floor_lists = mappa.floor_lists reorder_list: List[List[Tuple[int, Optional[int], Optional[int]]]] = [] dojo_floors = old_floor_lists[DOJO_MAPPA_ENTRY] new_floor_lists: List[List[MappaFloor]] = [] dungeons = self.get_dungeon_list() # Sanity check list. dungeons_not_visited = set((i for i in range(0, len(dungeons)))) # TODO Build new floor lists and update dungeon entries. Insert dojo dungeons at DOJO_MAPPA_ENTRY for group_or_dungeon in new_groups: # At DOJO_MAPPA_ENTRY insert the dojo: if len(new_floor_lists) == DOJO_MAPPA_ENTRY: new_floor_lists.append(dojo_floors) reorder_list.append([(DOJO_MAPPA_ENTRY, None, None)]) reorder_list.append([]) # Process this entry next_index = len(new_floor_lists) new_floor_list: List[MappaFloor] = [] if isinstance(group_or_dungeon, DungeonGroup): group = group_or_dungeon.dungeon_ids else: group = [group_or_dungeon] floor_count_in_group = sum(dungeons[i].number_floors for i in group) for i in group: dungeons_not_visited.remove(i) old_first = dungeons[i].start_after old_last = old_first + dungeons[i].number_floors new_floors = old_floor_lists[dungeons[i].mappa_index][old_first:old_last] reorder_list[-1].append((dungeons[i].mappa_index, old_first, old_last)) floor_i = len(new_floor_list) for floor in new_floors: floor.layout.floor_number = u8_checked(floor_i) floor_i += 1 dungeons[i].number_floors_in_group = u8_checked(floor_count_in_group) dungeons[i].mappa_index = u8_checked(next_index) dungeons[i].start_after = u8_checked(len(new_floor_list)) new_floor_list += new_floors new_floor_lists.append(new_floor_list) assert len(dungeons_not_visited) == 0, _("Some dungeons were missing in the new group list. " "This is a bug.") # If we haven't inserted the dojo dungeon floor list yet, do it now and pad with empty lists. if len(new_floor_lists) < DOJO_MAPPA_ENTRY: for i in range(len(new_floor_lists), DOJO_MAPPA_ENTRY + 1): if i == DOJO_MAPPA_ENTRY: new_floor_lists.append(dojo_floors) reorder_list.append([(DOJO_MAPPA_ENTRY, None, None)]) else: new_floor_lists.append([]) reorder_list.append([]) mappa.floor_lists = new_floor_lists assert self._validator self._validator.floors = new_floor_lists self.mark_root_as_modified() self.save_mappa() self.save_dungeon_list(dungeons) # Update floor attributes self.reorder_floors_ranks(reorder_list) self.reorder_floors_mf(reorder_list) if self.has_floor_ranks(): self.project.mark_as_modified(FLOOR_RANKS) if self.has_mission_forbidden(): self.project.mark_as_modified(FLOOR_MISSION_FORBIDDEN) self.rebuild_dungeon_tree()
def from_xml(cls, ele: Element) -> 'MappaFloorLayout': validate_xml_tag(ele, XML_FLOOR_LAYOUT) generator_settings = None chances = None terrain_settings = None misc = None for child in ele: if child.tag == XML_FLOOR_LAYOUT__GENSET: generator_settings = child elif child.tag == XML_FLOOR_LAYOUT__CHANCES: chances = child elif child.tag == XML_FLOOR_LAYOUT__TERRAINSET: terrain_settings = child elif child.tag == XML_FLOOR_LAYOUT__MISCSET: misc = child else: raise XmlValidateError( f( _("Unexpected sub-node for {XML_FLOOR_LAYOUT}: {child.tag}" ))) if generator_settings is None: raise XmlValidateError( f( _("{XML_FLOOR_LAYOUT__GENSET} missing for {XML_FLOOR_LAYOUT}." ))) if chances is None: raise XmlValidateError( f( _("{XML_FLOOR_LAYOUT__CHANCES} missing for {XML_FLOOR_LAYOUT}." ))) if terrain_settings is None: raise XmlValidateError( f( _("{XML_FLOOR_LAYOUT__TERRAINSET} missing for {XML_FLOOR_LAYOUT}." ))) if misc is None: raise XmlValidateError( f( _("{XML_FLOOR_LAYOUT__MISCSET} missing for {XML_FLOOR_LAYOUT}." ))) validate_xml_attribs(ele, [ XML_FLOOR_LAYOUT__STRUCTURE, XML_FLOOR_LAYOUT__TILESET, XML_FLOOR_LAYOUT__BGM, XML_FLOOR_LAYOUT__WEATHER, XML_FLOOR_LAYOUT__NUMBER, XML_FLOOR_LAYOUT__FIXED_FLOOR_ID, XML_FLOOR_LAYOUT__DARKNESS_LEVEL ]) validate_xml_attribs(generator_settings, [ XML_FLOOR_LAYOUT__GENSET__ROOM_DENSITY, XML_FLOOR_LAYOUT__GENSET__FLOOR_CONNECTIVITY, XML_FLOOR_LAYOUT__GENSET__INITIAL_ENEMY_DENSITY, XML_FLOOR_LAYOUT__GENSET__DEAD_ENDS, XML_FLOOR_LAYOUT__GENSET__ITEM_DENSITY, XML_FLOOR_LAYOUT__GENSET__TRAP_DENSITY, XML_FLOOR_LAYOUT__GENSET__EXTRA_HALLWAY_DENSITY, XML_FLOOR_LAYOUT__GENSET__BURIED_ITEM_DENSITY, XML_FLOOR_LAYOUT__GENSET__WATER_DENSITY, XML_FLOOR_LAYOUT__GENSET__MAX_COIN_AMOUNT ]) validate_xml_attribs(chances, [ XML_FLOOR_LAYOUT__CHANCES__SHOP, XML_FLOOR_LAYOUT__CHANCES__MONSTER_HOUSE, XML_FLOOR_LAYOUT__CHANCES__UNUSED, XML_FLOOR_LAYOUT__CHANCES__STICKY_ITEM, XML_FLOOR_LAYOUT__CHANCES__EMPTY_MONSTER_HOUSE, XML_FLOOR_LAYOUT__CHANCES__HIDDEN_STAIRS ]) validate_xml_attribs(terrain_settings, [ XML_FLOOR_LAYOUT__TERRAINSET__SECONDARY_USED, XML_FLOOR_LAYOUT__TERRAINSET__SECONDARY_TYPE, XML_FLOOR_LAYOUT__TERRAINSET__IMPERFECT_ROOMS, XML_FLOOR_LAYOUT__TERRAINSET__UNK1, XML_FLOOR_LAYOUT__TERRAINSET__UNK3, XML_FLOOR_LAYOUT__TERRAINSET__UNK4, XML_FLOOR_LAYOUT__TERRAINSET__UNK5, XML_FLOOR_LAYOUT__TERRAINSET__UNK6, XML_FLOOR_LAYOUT__TERRAINSET__UNK7 ]) validate_xml_attribs(misc, [ XML_FLOOR_LAYOUT__MISCSET__UNKE, XML_FLOOR_LAYOUT__MISCSET__KECLEON_SHOP_ITEM_POSITIONS, XML_FLOOR_LAYOUT__MISCSET__UNK_HIDDEN_STAIRS, XML_FLOOR_LAYOUT__MISCSET__ENEMY_IQ, XML_FLOOR_LAYOUT__MISCSET__IQ_BOOSTER_BOOST ]) if not hasattr(MappaFloorStructureType, ele.get(XML_FLOOR_LAYOUT__STRUCTURE)): raise XmlValidateError( f( _("Invalid structure type {ele.get(XML_FLOOR_LAYOUT__STRUCTURE)}" ))) structure = getattr(MappaFloorStructureType, ele.get(XML_FLOOR_LAYOUT__STRUCTURE)) if not hasattr(MappaFloorWeather, ele.get(XML_FLOOR_LAYOUT__WEATHER)): raise XmlValidateError( f( _("Invalid weather type {ele.get(XML_FLOOR_LAYOUT__WEATHER)}" ))) weather = getattr(MappaFloorWeather, ele.get(XML_FLOOR_LAYOUT__WEATHER)) if not hasattr(MappaFloorDarknessLevel, ele.get(XML_FLOOR_LAYOUT__DARKNESS_LEVEL)): raise XmlValidateError( f( _("Invalid darkness level type {ele.get(XML_FLOOR_LAYOUT__DARKNESS_LEVEL)}" ))) darkness_level = getattr(MappaFloorDarknessLevel, ele.get(XML_FLOOR_LAYOUT__DARKNESS_LEVEL)) return cls( structure=structure, room_density=i8_checked( int( generator_settings.get( XML_FLOOR_LAYOUT__GENSET__ROOM_DENSITY))), tileset_id=u8_checked(int(ele.get(XML_FLOOR_LAYOUT__TILESET))), music_id=u8_checked(int(ele.get(XML_FLOOR_LAYOUT__BGM))), weather=weather, floor_connectivity=u8_checked( int( generator_settings.get( XML_FLOOR_LAYOUT__GENSET__FLOOR_CONNECTIVITY))), initial_enemy_density=i8_checked( int( generator_settings.get( XML_FLOOR_LAYOUT__GENSET__INITIAL_ENEMY_DENSITY))), kecleon_shop_chance=u8_checked( int(chances.get(XML_FLOOR_LAYOUT__CHANCES__SHOP))), monster_house_chance=u8_checked( int(chances.get(XML_FLOOR_LAYOUT__CHANCES__MONSTER_HOUSE))), unusued_chance=u8_checked( int(chances.get(XML_FLOOR_LAYOUT__CHANCES__UNUSED))), sticky_item_chance=u8_checked( int(chances.get(XML_FLOOR_LAYOUT__CHANCES__STICKY_ITEM))), dead_ends=bool( int(generator_settings.get( XML_FLOOR_LAYOUT__GENSET__DEAD_ENDS))), secondary_terrain=u8_checked( int( terrain_settings.get( XML_FLOOR_LAYOUT__TERRAINSET__SECONDARY_TYPE))), terrain_settings=MappaFloorTerrainSettings( has_secondary_terrain=bool( int( terrain_settings.get( XML_FLOOR_LAYOUT__TERRAINSET__SECONDARY_USED))), unk1=bool( int( terrain_settings.get( XML_FLOOR_LAYOUT__TERRAINSET__UNK1))), generate_imperfect_rooms=bool( int( terrain_settings.get( XML_FLOOR_LAYOUT__TERRAINSET__IMPERFECT_ROOMS))), unk3=bool( int( terrain_settings.get( XML_FLOOR_LAYOUT__TERRAINSET__UNK3))), unk4=bool( int( terrain_settings.get( XML_FLOOR_LAYOUT__TERRAINSET__UNK4))), unk5=bool( int( terrain_settings.get( XML_FLOOR_LAYOUT__TERRAINSET__UNK5))), unk6=bool( int( terrain_settings.get( XML_FLOOR_LAYOUT__TERRAINSET__UNK6))), unk7=bool( int( terrain_settings.get( XML_FLOOR_LAYOUT__TERRAINSET__UNK7))), ), unk_e=bool(int(misc.get(XML_FLOOR_LAYOUT__MISCSET__UNKE))), item_density=u8_checked( int( generator_settings.get( XML_FLOOR_LAYOUT__GENSET__ITEM_DENSITY))), trap_density=u8_checked( int( generator_settings.get( XML_FLOOR_LAYOUT__GENSET__TRAP_DENSITY))), floor_number=u8_checked(int(ele.get(XML_FLOOR_LAYOUT__NUMBER))), fixed_floor_id=u8_checked( int(ele.get(XML_FLOOR_LAYOUT__FIXED_FLOOR_ID))), extra_hallway_density=u8_checked( int( generator_settings.get( XML_FLOOR_LAYOUT__GENSET__EXTRA_HALLWAY_DENSITY))), buried_item_density=u8_checked( int( generator_settings.get( XML_FLOOR_LAYOUT__GENSET__BURIED_ITEM_DENSITY))), water_density=u8_checked( int( generator_settings.get( XML_FLOOR_LAYOUT__GENSET__WATER_DENSITY))), darkness_level=darkness_level, max_coin_amount=int( generator_settings.get( XML_FLOOR_LAYOUT__GENSET__MAX_COIN_AMOUNT)), kecleon_shop_item_positions=u8_checked( int( misc.get( XML_FLOOR_LAYOUT__MISCSET__KECLEON_SHOP_ITEM_POSITIONS) )), empty_monster_house_chance=u8_checked( int(chances.get( XML_FLOOR_LAYOUT__CHANCES__EMPTY_MONSTER_HOUSE))), unk_hidden_stairs=u8_checked( int(misc.get(XML_FLOOR_LAYOUT__MISCSET__UNK_HIDDEN_STAIRS))), hidden_stairs_spawn_chance=u8_checked( int(chances.get(XML_FLOOR_LAYOUT__CHANCES__HIDDEN_STAIRS))), enemy_iq=u16_checked( int(misc.get(XML_FLOOR_LAYOUT__MISCSET__ENEMY_IQ))), iq_booster_boost=i16_checked( int(misc.get(XML_FLOOR_LAYOUT__MISCSET__IQ_BOOSTER_BOOST))), )