Example #1
0
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