def manage_character_names(fout, change_to, male): characters = get_characters() wild = options_.is_code_active('partyparty') sabin_mode = options_.is_code_active('suplexwrecks') tina_mode = options_.is_code_active('bravenudeworld') soldier_mode = options_.is_code_active('quikdraw') moogle_mode = options_.is_code_active('kupokupo') ghost_mode = options_.is_code_active('halloween') names = [] if tina_mode: names = ["Tina"] * 30 + ["MADUIN"] + ["Tina"] * 3 elif sabin_mode: names = [ "Teabin", "Loabin", "Cyabin", "Shabin", "Edabin", "Sabin", "Ceabin", "Stabin", "Reabin", "Seabin", "Moabin", "Gaubin", "Goabin", "Umabin", "Baabin", "Leabin", "??abin", "??abin", "Kuabin", "Kuabin", "Kuabin", "Kuabin", "Kuabin", "Kuabin", "Kuabin", "Kuabin", "Kuabin", "Kaabin", "Moabin", "??abin", "MADUIN", "??abin", "Viabin", "Weabin" ] elif moogle_mode: names = [ "Kumop", "Kupo", "Kupek", "Kupop", "Kumama", "Kuku", "Kutan", "Kupan", "Kushu", "Kurin", "Mog", "Kuru", "Kamog", "Kumaro", "Banon", "Leo", "?????", "?????", "Cyan", "Shadow", "Edgar", "Sabin", "Celes", "Strago", "Relm", "Setzer", "Gau", "Gogo" ] gba_moogle_names = [ "Moglin", "Mogret", "Moggie", "Molulu", "Moghan", "Moguel", "Mogsy", "Mogwin", "Mog", "Mugmug", "Cosmog" ] random_name_ids = [] # Terra, Locke, and Umaro get a specific name, or a random moogle name from another ff game for moogle_id in [0, 1, 13]: if random.choice([True, True, False]): random_name_ids.append(moogle_id) # Other party members get either the name of their counterpart from snes or gba, or moogle name from another ff game for moogle_id in itertools.chain(range(2, 10), range(11, 13)): chance = random.randint(1, 4) if chance == 2: names[moogle_id] = gba_moogle_names[moogle_id - 2] elif chance != 1: random_name_ids.append(moogle_id) f = open_mei_fallback(MOOGLE_NAMES_TABLE) mooglenames = sorted( set(sanitize_names([line.strip() for line in f.readlines()]))) f.close() random_moogle_names = random.sample(mooglenames, len(random_name_ids)) for index, moogle_id in enumerate(random_name_ids): names[moogle_id] = random_moogle_names[index] # Human Mog gets a human name, maybe if random.choice([True, True, False]): f = open_mei_fallback(MALE_NAMES_TABLE) malenames = sorted( set(sanitize_names([line.strip() for line in f.readlines()]))) f.close() names[10] = random.choice(malenames) else: f = open_mei_fallback(MALE_NAMES_TABLE) malenames = sorted( set(sanitize_names([line.strip() for line in f.readlines()]))) f.close() f = open_mei_fallback(FEMALE_NAMES_TABLE) femalenames = sorted( set(sanitize_names([line.strip() for line in f.readlines()]))) f.close() for c in range(14): choose_male = False if wild or soldier_mode or ghost_mode: choose_male = random.choice([True, False]) elif change_to[c] in male: choose_male = True if choose_male: name = random.choice(malenames) else: name = random.choice(femalenames) if name in malenames: malenames.remove(name) if name in femalenames: femalenames.remove(name) names.append(name) umaro_name = names[13] for umaro_id in [0x10f, 0x110]: change_enemy_name(fout, umaro_id, umaro_name) if not options_.is_code_active('capslockoff'): names = [name.upper() for name in names] for c in characters: if c.id < 14: c.newname = names[c.id] c.original_appearance = NAME_ID_DICT[c.id] for c, name in enumerate(names): name = name_to_bytes(name, 6) assert len(name) == 6 fout.seek(0x478C0 + (6 * c)) fout.write(name)
def randomize_magicite(fout, sourcefile): magicite = [] # Some espers use 128x128 graphics, and those look like crap in the Ifrit/Shiva fight # So make sure Ifrit and Shiva have espers with small graphics. Tritoch also has # Issues with large sprites in the cutscene with the MagiTek armor espers = get_espers(sourcefile) shuffled_espers = {} espers_by_name = {e.name: e for e in espers} esper_graphics = [ MonsterGraphicBlock(pointer=0x127780 + (5 * i), name="") for i in range(len(espers)) ] for eg in esper_graphics: eg.read_data(sourcefile) # Ifrit's esper graphics are large. But he has separate enemy graphics that are fine. ifrit_graphics = copy.copy(get_monster(0x109).graphics) ifrit_id = espers_by_name["Ifrit"].id esper_graphics[ifrit_id] = ifrit_graphics # Pick the replacements for Ragnarok/Crusader out of high-rank espers high_rank_espers = [e for e in espers if e.rank >= 4] replace_ids = [ espers_by_name[name].id for name in ["Ragnarok", "Crusader"] ] special_espers = select_magicite(high_rank_espers, replace_ids) shuffled_espers.update(special_espers) # Pick replacements for Shiva, Ifrit, and Tritoch, which must not be large # Shiva and Ifrit must be picked from rank < 3, Tritoch can be any small_espers = [ e for e in espers if not esper_graphics[e.id].large and e not in shuffled_espers.values() ] low_ranked_small_espers = [e for e in small_espers if e.rank < 3] replace_ids = [espers_by_name[name].id for name in ["Shiva", "Ifrit"]] enemy_espers = select_magicite(low_ranked_small_espers, replace_ids) shuffled_espers.update(enemy_espers) remaining_small_espers = [ e for e in small_espers if e not in enemy_espers.values() ] replace_ids = [espers_by_name["Tritoch"].id] enemy_espers = select_magicite(remaining_small_espers, replace_ids) shuffled_espers.update(enemy_espers) # TODO: maybe allow tritoch to be big if we skip cutscenes #tritoch_id = [e.id for e in espers if e.name == "Tritoch"][0] #if esper_graphics[tritoch_id].large: # tritoch_formations = [0x1BF, 0x1C0, 0x1E7, 0x1E8] # for g in tritoch_formations: # f = get_formation(g) # f.mouldbyte = 6 << 4 # f.enemy_pos[0] = f.enemy_pos[0] & 0xF0 + 3 # f.write_data(fout) # Make sure Odin's replacement levels up odin_id = espers_by_name["Odin"].id raiden_id = espers_by_name["Raiden"].id while True: odin_candidates = [ e for e in espers if e not in shuffled_espers.values() and e.rank <= 3 ] odin_replacement = select_magicite(odin_candidates, [odin_id]) odin_replacement_rank = odin_replacement[odin_id].rank raiden_candidates = [ e for e in espers if e not in shuffled_espers.values() and e.rank > odin_replacement_rank ] if not raiden_candidates: continue raiden_replacement = select_magicite(raiden_candidates, [raiden_id]) shuffled_espers.update(odin_replacement) shuffled_espers.update(raiden_replacement) break # Shuffle all remaining espers for rank in range(0, 5): remaining_keys = [ e.id for e in espers if e.id not in shuffled_espers.keys() and e.rank <= rank ] remaining_values = [ e for e in espers if e not in shuffled_espers.values() and e.rank <= max(rank + 1, 2) ] random.shuffle(remaining_values) shuffled_espers.update(zip(remaining_keys, remaining_values)) assert (sorted([e.id for e in espers], key=id) == sorted(shuffled_espers.keys())) assert (sorted(espers, key=id) == sorted(shuffled_espers.values(), key=id)) locations = [e.location for e in espers] for i, e in shuffled_espers.items(): e.location = locations[i] with open(sourcefile, 'br') as s: for line in open(MAGICITE_TABLE, 'r'): line = line.split('#')[0].strip() l = line.split(',') address = int(l[0], 16) dialogue = [int(d, 16) for d in l[1:]] s.seek(address) instruction = ord(s.read(1)) esper_index = ord(s.read(1)) if instruction not in [ 0x86, 0x87 ] or esper_index < 0x36 or esper_index > 0x50: print("Error in magicite table") return magicite.append(Magicite(address, esper_index - 0x36, dialogue)) for m in magicite: original_name = espers[m.original_esper_index].name m.esper_index = shuffled_espers[m.original_esper_index].id new_name = shuffled_espers[m.original_esper_index].name for d in m.dialogue: patch_dialogue(d, original_name, "{" + original_name + "}") patch_dialogue(d, original_name + "'s", "{" + original_name + "Possessive}") dotted_name = "".join(chain(*zip(original_name, repeat('.'))))[:-1] patch_dialogue(d, dotted_name, "{" + original_name + "Dotted}") set_dialogue_var(original_name, new_name) set_dialogue_var(original_name + "Possessive", new_name + "'s") dotted_new_name = "".join(chain(*zip(new_name, repeat('.'))))[:-1] set_dialogue_var(original_name + "Dotted", dotted_new_name) fout.seek(m.address + 1) fout.write(bytes([m.esper_index + 0x36])) phoenix_replacement = shuffled_espers[espers_by_name["Phoenix"].id] set_location_name(71, f"{phoenix_replacement.name.upper()} CAVE") esper_monsters = [(0x108, "Shiva"), (0x109, "Ifrit"), (0x114, "Tritoch"), (0x115, "Tritoch"), (0x144, "Tritoch")] for monster_id, name in esper_monsters: monster = get_monster(monster_id) esper_id = [e.id for e in espers if e.name == name][0] replacement = shuffled_espers[esper_id] change_enemy_name(fout, monster_id, replacement.name) mg = esper_graphics[replacement.id] monster.graphics.copy_data(mg) monster.graphics.write_data(fout) ragnarok = get_item(27) ragnarok.dataname = bytes([0xd9]) + name_to_bytes( shuffled_espers[espers_by_name["Ragnarok"].id].name, 12) ragnarok.write_stats(fout) return shuffled_espers