def get_patch(self): """ Returns: randomizer.logic.patch.Patch: Patch data """ patch = Patch() # Add boss data. data = bytearray() data += utils.ByteField(0x4a).as_bytes() data += utils.ByteField(self.pack.index).as_bytes() data += utils.ByteField(0x00).as_bytes() # If boss formation requires a specific battlefield, use that. Otherwise use the location battlefield. if self.formation.required_battlefield is not None: data += utils.ByteField( self.formation.required_battlefield).as_bytes() else: data += utils.ByteField(self.battlefield).as_bytes() # Check for list of addresses if spot has multiple addresses that need to be set. if isinstance(self.battle_address, (list, tuple)): addrs = self.battle_address else: addrs = [self.battle_address] for addr in addrs: patch.add_data(addr, data) return patch
def finalize(self): # Return a patch next time... acc = [] credit_start = 0x3FDBB0 credit_len = 3380 string_table_start = 0x3FE8E4 string_table_size = len(self.strings) * 2 assert len(self.acc) <= credit_len # Fill the unused section of credits script with 0. # This is very important. self.acc += (3380 - len(self.acc)) * [0] free_list = { 0x3f9c40: 952, credit_start + len(self.acc): credit_len - len(self.acc), string_table_start + string_table_size: 2080 - string_table_size } patch = Patch() patch.add_data(credit_start, bytearray(self.acc)) for i in range(len(self.strings)): string = inv_str(self.strings[i]) base = allocate_string(len(string), free_list) patch.add_data(base, string) patch.add_data( string_table_start + i * 2, utils.ByteField(base & 0xFFFF, num_bytes=2).as_bytes()) # Underscore patch.add_data(0x3FFDDA, '\x3F\xC0\x7F\x80') return patch
def get_patch(self): """Get patch for this spell. :return: Patch data. :rtype: randomizer.logic.patch.Patch """ patch = Patch() # FP is byte 3, power is byte 6, hit rate is byte 7. Each spell is 12 bytes. base_addr = self.BASE_ADDRESS + (self.index * 12) patch.add_data(base_addr + 2, utils.ByteField(self.fp).as_bytes()) data = utils.ByteField(self.power).as_bytes() data += utils.ByteField(self.hit_rate).as_bytes() patch.add_data(base_addr + 5, data) return patch
def as_bytes(self): """Return byte representation of this stat growth object for the patch. :rtype: bytearray """ data = bytearray() # HP is one byte on its own. Attack/defense stats are 4 bits each combined into a single byte together. data += utils.ByteField(self.max_hp).as_bytes() physical = self.attack << 4 physical |= self.defense data += utils.ByteField(physical).as_bytes() magical = self.magic_attack << 4 magical |= self.magic_defense data += utils.ByteField(magical).as_bytes() return data
def get_patch(self): """ Returns: randomizer.logic.patch.Patch: Patch data """ patch = Patch() for addr in self.addresses: patch.add_data(addr, utils.ByteField(self.item.index).as_bytes()) return patch
def get_patch(self): """ Returns: randomizer.logic.patch.Patch: Patch data """ patch = Patch() for dialog_id, pointer, question in self.questions: table_entry = DIALOG_POINTER_BASE_ADDRESS + dialog_id * 2 patch.add_data(table_entry, utils.ByteField((pointer - 8) & 0xFFFF, num_bytes=2).as_bytes()) if question: patch.add_data(pointer, question) return patch
def get_patch(self): """Get patch for exp required for each level up. :return: Patch data. :rtype: randomizer.logic.patch.Patch """ # Data is 29 blocks (starting at level 2), 2 bytes each block. data = bytearray() for level in range(2, 31): data += utils.ByteField(self.get_xp_for_level(level), num_bytes=2).as_bytes() patch = Patch() patch.add_data(self.BASE_ADDRESS, data) return patch
def get_patch(self): """ Returns: randomizer.logic.patch.Patch: Patch data """ patch = Patch() # Zero for no star, or 255 if this boss has a star. val = 0xff if self.has_star else 0x00 patch.add_data(self.star_address, utils.ByteField(val).as_bytes()) return patch
def get_patch(self): """Override patch generation because this is an overworld spot that needs special data. Returns: randomizer.logic.patch.Patch: Patch data """ patch = Patch() # Different values needed for this spot. val = 0x9c if self.has_star else 0x1c patch.add_data(self.star_address, utils.ByteField(val).as_bytes()) return patch
def get_patch(self): """ Returns: randomizer.logic.patch.Patch: Patch data """ patch = Patch() # A big assumption here is that the dialogs aren't getting relocated # out if the 0x240000 bank. for dialog_id, pointer, wish in self.wishes: table_entry = DIALOG_POINTER_BASE_ADDRESS + dialog_id * 2 patch.add_data(table_entry, utils.ByteField((pointer - 4) & 0xFFFF, num_bytes=2).as_bytes()) patch.add_data(pointer, wish) return patch
def get_patch(self): """ Returns: randomizer.logic.patch.Patch: Patch data """ patch = Patch() # Spots should be in 0-indexed bit order already. result = 0 for i, spot in enumerate(self.spots): if spot.pressed: result |= (1 << i) patch.add_data(self.BASE_ADDRESS, utils.ByteField(result, num_bytes=2).as_bytes()) return patch
def get_patch(self): """Get patch for this item. :return: Patch data. :rtype: randomizer.logic.patch.Patch """ patch = Patch() base_addr = self.BASE_ADDRESS + (self.index * 4) data = bytearray() # First byte is attack level + damage type flags in a bitmap. attack_flags = [i for i in range(3) if self.attack_level & (1 << i)] attack_flags += self.damage_types data += utils.BitMapSet(1, attack_flags).as_bytes() # Other bytes are hit rate, status effects, and buffs. data += utils.ByteField(self.hit_rate).as_bytes() data += utils.BitMapSet(1, self.status_effects).as_bytes() data += utils.BitMapSet(1, self.buffs).as_bytes() patch.add_data(base_addr, data) return patch
def get_patch(self): """Build patch data for this character. :return: Patch data for this character. :rtype: randomizer.logic.patch.Patch """ patch = Patch() # Build character patch data. char_data = bytearray() char_data += utils.ByteField(self.starting_level).as_bytes() char_data += utils.ByteField(self.max_hp, num_bytes=2).as_bytes() # Current HP char_data += utils.ByteField(self.max_hp, num_bytes=2).as_bytes() # Max HP char_data += utils.ByteField(self.speed).as_bytes() char_data += utils.ByteField(self.attack).as_bytes() char_data += utils.ByteField(self.defense).as_bytes() char_data += utils.ByteField(self.magic_attack).as_bytes() char_data += utils.ByteField(self.magic_defense).as_bytes() char_data += utils.ByteField(self.xp, num_bytes=2).as_bytes() # Set starting weapon/armor/accessory as blank for all characters. char_data += utils.ByteField(0xff).as_bytes() char_data += utils.ByteField(0xff).as_bytes() char_data += utils.ByteField(0xff).as_bytes() char_data.append(0x00) # Unused byte char_data += utils.BitMapSet(4, [spell.index for spell in self.starting_spells]).as_bytes() # Base address plus offset based on character index. addr = self.BASE_ADDRESS + (self.index * 20) patch.add_data(addr, char_data) # Add levelup stat growth and bonuses to the patch data for this character. Offset is 15 bytes for each stat # object, 3 bytes per character. for i, stat in enumerate(self.levelup_growths): addr = self.BASE_STAT_GROWTH_ADDRESS + (i * 15) + (self.index * 3) patch.add_data(addr, stat.as_bytes()) for i, stat in enumerate(self.levelup_bonuses): addr = self.BASE_STAT_BONUS_ADDRESS + (i * 15) + (self.index * 3) patch.add_data(addr, stat.as_bytes()) # Add learned spells data. # Data is 29 blocks (starting at level 2), 5 bytes each block (1 byte per character in order) base_addr = self.BASE_LEARNED_SPELLS_ADDRESS + self.index for level in range(2, 31): level_addr = base_addr + ((level - 2) * 5) # If we have a spell for this level, add the index. Otherwise it should be 0xff for no spell learned. if self.learned_spells.get(level): patch.add_data(level_addr, utils.ByteField(self.learned_spells[level].index).as_bytes()) else: patch.add_data(level_addr, utils.ByteField(0xff).as_bytes()) if self.palette: colourbytes = palette_to_bytes(self.palette.colours) poisonbytes = palette_to_bytes(self.palette.poison_colours) underwaterbytes = palette_to_bytes(self.palette.underwater_colours) for address in self.palette.starting_addresses: patch.add_data(address, colourbytes) for address in self.palette.poison_addresses: patch.add_data(address, poisonbytes) for address in self.palette.underwater_addresses: patch.add_data(address, underwaterbytes) if self.palette.rename_character: name = self.palette.name clone_name = self.palette.name.upper() while len(name) < 10: name += " " if len(clone_name) < 8: clone_name = clone_name + " CLONE" else: clone_name = clone_name + " 2" while len(clone_name) < 13: clone_name += " " patch.add_data(self.palette.name_address, name) patch.add_data(self.palette.clone_name_address, clone_name) return patch