def get_free_portrait_ids(swap_to, change_to, char_ids, char_portraits): # get unused portraits so we can overwrite them if needed sprite_swap_mode = options_.is_code_active('makeover') wild = options_.is_code_active('partyparty') if not sprite_swap_mode: return [], False def reserve_portrait_id(used_portrait_ids, new, swap, portrait): if swap is None: if portrait == 0 and wild and new != 0: used_portrait_ids.add(0xE) else: used_portrait_ids.add(new) elif not swap.has_custom_portrait(): used_portrait_ids.add(swap.fallback_portrait_id) else: return 1 return 0 needed = 0 used_portrait_ids = set() for c in char_ids: # skip characters who don't have their own portraits if (char_portraits[c] == 0 and c != 0) or c == 0x13: continue new = change_to[c] portrait = char_portraits[new] swap = swap_to[c] if c in swap_to else None needed += reserve_portrait_id(used_portrait_ids, new, swap, portrait) if not wild: for i in range(0xE, 0x13): used_portrait_ids.add(i) # Merchant normally uses the same portrait as soldier. # If we have a free slot because some others happen to be sharing, use the portrait for the merchant sprite. # If not, we have to use the same one as the soldier. merchant = False if wild and needed < 19 - len(used_portrait_ids): c = 0x13 new = change_to[c] portrait = char_portraits[new] swap = swap_to[c] if c in swap_to else None merchant = reserve_portrait_id(used_portrait_ids, new, swap, portrait) free_portrait_ids = list(set(range(19)) - used_portrait_ids) return free_portrait_ids, merchant
def manage_palettes(fout, change_to, char_ids): sabin_mode = options_.is_code_active('suplexwrecks') tina_mode = options_.is_code_active('bravenudeworld') christmas_mode = options_.is_code_active('christmas') new_palette_mode = not options_.is_code_active('sometimeszombies') characters = get_characters() npcs = get_npcs() charpal_options = {} for line in open(CHARACTER_PALETTE_TABLE): if line[0] == '#': continue charid, palettes = tuple(line.strip().split(':')) palettes = list(map(hex2int, palettes.split(','))) charid = hex2int(charid) charpal_options[charid] = palettes if new_palette_mode: twinpal = random.randint(0, 5) char_palette_pool = list(range(0, 6)) + list(range(0, 6)) char_palette_pool.remove(twinpal) char_palette_pool.append( random.choice(list(range(0, twinpal)) + list(range(twinpal, 6)))) while True: random.shuffle(char_palette_pool) #make sure terra, locke, and edgar are all different if twinpal in char_palette_pool[0:2]: continue if char_palette_pool[0] == char_palette_pool[1]: continue break char_palette_pool = char_palette_pool[:4] + [twinpal, twinpal ] + char_palette_pool[4:] palette_change_to = {} additional_celeses = [] for npc in npcs: if npc.graphics == 0x41: additional_celeses.append(npc) if npc.graphics not in charpal_options: continue # Don't recolor shadowy Sabin on Mt. Kolts if npc.locid in [0x60, 0x61]: continue if npc.graphics in change_to: new_graphics = change_to[npc.graphics] if (npc.graphics, npc.palette) in palette_change_to: new_palette = palette_change_to[(npc.graphics, npc.palette)] elif new_palette_mode and npc.graphics < 14: new_palette = char_palette_pool[npc.graphics] palette_change_to[(npc.graphics, npc.palette)] = new_palette npc.palette = new_palette else: while True: new_palette = random.choice(charpal_options[new_graphics]) if sabin_mode or tina_mode: new_palette = random.randint(0, 5) if (new_palette == 5 and new_graphics not in [3, 0xA, 0xC, 0xD, 0xE, 0xF, 0x12, 0x14] and random.randint(1, 10) != 10): continue break palette_change_to[(npc.graphics, npc.palette)] = new_palette npc.palette = new_palette npc.palette = new_palette for npc in additional_celeses: if (6, 0) in palette_change_to: npc.palette = palette_change_to[(6, 0)] main_palette_changes = {} for character in characters: c = character.id if c not in change_to: continue fout.seek(0x2CE2B + c) before = ord(fout.read(1)) new_graphics = change_to[c] new_palette = palette_change_to[(c, before)] main_palette_changes[c] = (before, new_palette) fout.seek(0x2CE2B + c) fout.write(bytes([new_palette])) pointers = [0, 4, 9, 13] pointers = [ptr + 0x18EA60 + (18 * c) for ptr in pointers] if c < 14: for ptr in pointers: fout.seek(ptr) byte = ord(fout.read(1)) byte = byte & 0xF1 byte |= ((new_palette + 2) << 1) fout.seek(ptr) fout.write(bytes([byte])) character.palette = new_palette if options_.is_code_active('repairpalette'): make_palette_repair(fout, main_palette_changes) if new_palette_mode: char_hues = [ 0, 10, 20, 30, 45, 60, 75, 90, 120, 150, 180, 200, 220, 240, 270, 300, 330 ] char_hues.append(random.choice([0, 0, 345, random.randint(105, 135)])) char_hues = shuffle_char_hues(char_hues) skintones = [((31, 24, 17), (25, 13, 7)), ((31, 23, 15), (25, 15, 8)), ((31, 24, 17), (25, 13, 7)), ((31, 25, 15), (25, 19, 10)), ((31, 25, 16), (24, 15, 12)), ((27, 17, 10), (20, 12, 10)), ((25, 20, 14), (19, 12, 4)), ((27, 22, 18), (20, 15, 12)), ((28, 22, 16), (22, 13, 6)), ((28, 23, 15), (22, 16, 7)), ((27, 23, 15), (20, 14, 9))] snowmanvampire = ((29, 29, 30), (25, 25, 27)) if christmas_mode or random.randint(1, 100) > 66: skintones.append(snowmanvampire) random.shuffle(skintones) # no vampire townsfolk if snowmanvampire in skintones[:6] and not christmas_mode: skintones.remove(snowmanvampire) skintones = skintones[:5] + [snowmanvampire] for i in range(6): pointer = 0x268000 + (i * 0x20) if new_palette_mode: palette = recolor_character_palette(fout, pointer, palette=None, flesh=(i == 5), santa=(christmas_mode and i == 3), skintones=skintones, char_hues=char_hues) else: palette = recolor_character_palette(fout, pointer, palette=None, flesh=(i == 5), santa=(christmas_mode and i == 3)) pointer = 0x2D6300 + (i * 0x20) recolor_character_palette(fout, pointer, palette=palette) # esper terra pointer = 0x268000 + (8 * 0x20) if new_palette_mode: palette = recolor_character_palette(fout, pointer, palette=None, trance=True) else: palette = recolor_character_palette(fout, pointer, palette=None, flesh=True, middle=False) pointer = 0x2D6300 + (6 * 0x20) palette = recolor_character_palette(fout, pointer, palette=palette) # recolor magitek and chocobos transformer = get_palette_transformer(middle=True) def recolor_palette(pointer, size): fout.seek(pointer) palette = [read_multi(fout, length=2) for _ in range(size)] palette = transformer(palette) fout.seek(pointer) for c in palette: write_multi(fout, c, length=2) recolor_palette(0x2cfd4, 23) recolor_palette(0x268000 + (7 * 0x20), 16) recolor_palette(0x12ee20, 16) recolor_palette(0x12ef20, 16) for line in open(EVENT_PALETTE_TABLE): if line[0] == '#': continue line = line.split(' ') if len(line) > 1: if line[1] == 'c' and options_.is_code_active( 'thescenarionottaken'): return if line[1] == 'd' and not options_.is_code_active( 'thescenarionottaken'): return pointer = hex2int(line[0].strip()) fout.seek(pointer) data = bytearray(fout.read(5)) char_id, palette = data[1], data[4] if char_id not in char_ids: continue try: data[4] = palette_change_to[(char_id, palette)] except KeyError: continue fout.seek(pointer) fout.write(data)
def manage_character_appearance(fout, preserve_graphics=False): 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') christmas_mode = options_.is_code_active('christmas') sprite_swap_mode = options_.is_code_active('makeover') and not ( sabin_mode or tina_mode or soldier_mode or moogle_mode or ghost_mode) new_palette_mode = not options_.is_code_active('sometimeszombies') if new_palette_mode: # import recolors for incompatible base sprites recolors = [("cyan", 0x152D40, 0x16A0), ("mog", 0x15E240, 0x16A0), ("umaro", 0x162620, 0x16A0), ("dancer", 0x1731C0, 0x5C0), ("lady", 0x1748C0, 0x5C0)] for rc in recolors: filename = os.path.join("data", "sprites", "RC" + rc[0] + ".bin") try: with open_mei_fallback(filename, "rb") as f: sprite = f.read() except OSError: continue if len(sprite) >= rc[2]: sprite = sprite[:rc[2]] fout.seek(rc[1]) fout.write(sprite) if (wild or tina_mode or sabin_mode or christmas_mode): if christmas_mode: char_ids = list(range(0, 0x15)) # don't replace kefka else: char_ids = list(range(0, 0x16)) else: char_ids = list(range(0, 0x0E)) male = None female = None if tina_mode: change_to = dict(list(zip(char_ids, [0x12] * 100))) elif sabin_mode: change_to = dict(list(zip(char_ids, [0x05] * 100))) elif soldier_mode: change_to = dict(list(zip(char_ids, [0x0e] * 100))) elif ghost_mode: change_to = dict(list(zip(char_ids, [0x14] * 100))) elif moogle_mode: # all characters are moogles except Mog, Imp, and Esper Terra if wild: # make mog human mog = random.choice( list(range(0, 0x0A)) + list(range(0x0B, 0x0F)) + [0x10, 0x11, 0x13, 0x15]) #esper terra and imp neither human nor moogle esper_terra, imp = random.sample([0x0F, 0x12, 0x14], 2) else: mog = random.choice(list(range(0, 0x0A)) + list(range(0x0B, 0x0E))) esper_terra = 0x12 imp = 0x0F change_to = dict(list(zip(char_ids, [0x0A] * 100))) change_to[0x0A] = mog change_to[0x12] = esper_terra change_to[0x0F] = imp else: female = [0, 0x06, 0x08] female += [ c for c in [0x03, 0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x14] if random.choice([True, False]) ] female = [c for c in char_ids if c in female] male = [c for c in char_ids if c not in female] if preserve_graphics: change_to = dict(list(zip(char_ids, char_ids))) elif wild: change_to = list(char_ids) random.shuffle(change_to) change_to = dict(list(zip(char_ids, change_to))) else: random.shuffle(female) random.shuffle(male) change_to = dict( list(zip(sorted(male), male)) + list(zip(sorted(female), female))) manage_character_names(fout, change_to, male) swap_to = get_sprite_swaps(char_ids, male, female, change_to) for c in characters: if c.id < 14: if sprite_swap_mode and c.id in swap_to: c.new_appearance = swap_to[c.id].name elif not preserve_graphics: c.new_appearance = NAME_ID_DICT[change_to[c.id]] else: c.new_appearance = c.original_appearance sprite_ids = list(range(0x16)) ssizes = ([0x16A0] * 0x10) + ([0x1560] * 6) spointers = dict([(c, sum(ssizes[:c]) + 0x150000) for c in sprite_ids]) ssizes = dict(list(zip(sprite_ids, ssizes))) char_portraits = {} char_portrait_palettes = {} sprites = {} riding_sprites = {} try: f = open(RIDING_SPRITE_TABLE, "r") except IOError: pass else: for line in f.readlines(): char_id, filename = line.strip().split(',', 1) try: g = open_mei_fallback( os.path.join("custom", "sprites", filename), "rb") except IOError: continue riding_sprites[int(char_id)] = g.read(0x140) g.close() f.close() for c in sprite_ids: fout.seek(0x36F1B + (2 * c)) portrait = read_multi(fout, length=2) char_portraits[c] = portrait fout.seek(0x36F00 + c) portrait_palette = fout.read(1) char_portrait_palettes[c] = portrait_palette fout.seek(spointers[c]) sprite = fout.read(ssizes[c]) if c in riding_sprites: sprite = sprite[:0x1560] + riding_sprites[c] sprites[c] = sprite if tina_mode: char_portraits[0x12] = char_portraits[0] char_portrait_palettes[0x12] = char_portrait_palettes[0] portrait_data = [] portrait_palette_data = [] fout.seek(0x2D1D00) for _ in range(19): portrait_data.append(fout.read(0x320)) fout.seek(0x2D5860) for _ in range(19): portrait_palette_data.append(fout.read(0x20)) free_portrait_ids, merchant = get_free_portrait_ids( swap_to, change_to, char_ids, char_portraits) for c in char_ids: new = change_to[c] portrait = char_portraits[new] portrait_palette = char_portrait_palettes[new] if c == 0x13 and sprite_swap_mode and not merchant: new_soldier = change_to[0xE] portrait = char_portraits[new_soldier] portrait_palette = char_portrait_palettes[new_soldier] elif (char_portraits[c] == 0 and c != 0): portrait = char_portraits[0xE] portrait_palette = char_portrait_palettes[0xE] elif sprite_swap_mode and c in swap_to: use_fallback = True fallback_portrait_id = swap_to[c].fallback_portrait_id if fallback_portrait_id < 0 or fallback_portrait_id > 18: fallback_portrait_id = 0xE portrait = fallback_portrait_id * 0x320 portrait_palette = bytes([fallback_portrait_id]) new_portrait_data = portrait_data[fallback_portrait_id] new_portrait_palette_data = portrait_palette_data[ fallback_portrait_id] if swap_to[c].has_custom_portrait(): use_fallback = False try: g = open_mei_fallback( os.path.join("custom", "sprites", swap_to[c].portrait_filename), "rb") h = open_mei_fallback( os.path.join("custom", "sprites", swap_to[c].portrait_palette_filename), "rb") except IOError: use_fallback = True print("failed to load portrait %s for %s, using fallback" % (swap_to[c].portrait_filename, swap_to[c].name)) else: new_portrait_data = g.read(0x320) new_portrait_palette_data = h.read(0x20) h.close() g.close() if not use_fallback or fallback_portrait_id in free_portrait_ids: portrait_id = free_portrait_ids[0] portrait = portrait_id * 0x320 portrait_palette = bytes([portrait_id]) free_portrait_ids.remove(free_portrait_ids[0]) fout.seek(0x2D1D00 + portrait) fout.write(new_portrait_data) fout.seek(0x2D5860 + portrait_id * 0x20) fout.write(new_portrait_palette_data) elif portrait == 0 and wild and change_to[c] != 0: portrait = char_portraits[0xE] portrait_palette = char_portrait_palettes[0xE] fout.seek(0x36F1B + (2 * c)) write_multi(fout, portrait, length=2) fout.seek(0x36F00 + c) fout.write(portrait_palette) if wild: fout.seek(spointers[c]) fout.write(sprites[0xE][:ssizes[c]]) fout.seek(spointers[c]) if sprite_swap_mode and c in swap_to: try: g = open_mei_fallback( os.path.join("custom", "sprites", swap_to[c].file), "rb") except IOError: newsprite = sprites[change_to[c]] for ch in characters: if ch.id == c: ch.new_appearance = NAME_ID_DICT[change_to[c]] else: newsprite = g.read(min(ssizes[c], swap_to[c].size)) # if it doesn't have riding sprites, it probably doesn't have a death sprite either if swap_to[c].size < 0x16A0: newsprite = newsprite[:0xAE0] + sprites[0xE][ 0xAE0:0xBA0] + newsprite[0xBA0:] g.close() else: newsprite = sprites[change_to[c]] newsprite = newsprite[:ssizes[c]] fout.write(newsprite) # celes in chains fout.seek(0x159500) chains = fout.read(192) fout.seek(0x17D660) fout.write(chains) manage_palettes(fout, change_to, char_ids)
def get_sprite_swaps(char_ids, male, female, vswaps): sprite_swap_mode = options_.is_code_active('makeover') wild = options_.is_code_active('partyparty') clone_mode = options_.is_code_active('cloneparty') replace_all = options_.is_code_active( 'novanilla') or options_.is_code_active('frenchvanilla') external_vanillas = False if options_.is_code_active('novanilla') else ( options_.is_code_active('frenchvanilla') or clone_mode) if not sprite_swap_mode: return [] class SpriteReplacement: def __init__(self, file, name, gender, riding=None, fallback_portrait_id=0xE, portrait_filename=None, uniqueids=None, groups=None): self.file = file.strip() self.name = name.strip() self.gender = gender.strip().lower() self.size = 0x16A0 if riding is not None and riding.lower( ) == "true" else 0x1560 self.uniqueids = [s.strip() for s in uniqueids.split('|') ] if uniqueids else [] self.groups = [s.strip() for s in groups.split('|')] if groups else [] if self.gender == "female": self.groups.append("girls") if self.gender == "male": self.groups.append("boys") self.weight = 1.0 if fallback_portrait_id == '': fallback_portrait_id = 0xE self.fallback_portrait_id = int(fallback_portrait_id) self.portrait_filename = portrait_filename if self.portrait_filename is not None: self.portrait_filename = self.portrait_filename.strip() if self.portrait_filename: self.portrait_palette_filename = portrait_filename.strip() if self.portrait_palette_filename and self.portrait_palette_filename: if self.portrait_palette_filename[-4:] == ".bin": self.portrait_palette_filename = self.portrait_palette_filename[: -4] self.portrait_palette_filename = self.portrait_palette_filename + ".pal" else: self.portrait_filename = None def has_custom_portrait(self): return self.portrait_filename is not None and self.portrait_palette_filename is not None def is_on(self, checklist): for g in self.uniqueids: if g in checklist: return True return False f = open_mei_fallback(SPRITE_REPLACEMENT_TABLE) known_replacements = [ SpriteReplacement(*line.strip().split(',')) for line in f.readlines() ] f.close() #uniqueids for sprites pulled from rom vuids = { 0: "terra", 1: "locke", 2: "cyan", 3: "shadow", 4: "edgar", 5: "sabin", 6: "celes", 7: "strago", 8: "relm", 9: "setzer", 10: "moogle", 11: "gau", 12: "gogo6", 13: "umaro", 16: "leo", 17: "banon", 18: "terra", 21: "kefka" } #determine which character ids are makeover'd blacklist = set() if replace_all: num_to_replace = len(char_ids) is_replaced = [True] * num_to_replace else: replace_min = 8 if not wild else 16 replace_max = 12 if not wild else 20 num_to_replace = min(len(known_replacements), random.randint(replace_min, replace_max)) is_replaced = [True] * num_to_replace + [False] * (len(char_ids) - num_to_replace) random.shuffle(is_replaced) for i, t in enumerate(is_replaced): if i in vuids and not t: blacklist.update([s.strip() for s in vuids[i].split('|')]) if external_vanillas: #include vanilla characters, but using the same system/chances as all others og_replacements = [ SpriteReplacement("ogterra.bin", "Terra", "female", "true", 0, None, "terra"), SpriteReplacement("oglocke.bin", "Locke", "male", "true", 1, None, "locke"), SpriteReplacement("ogcyan.bin", "Cyan", "male", "true", 2, None, "cyan"), SpriteReplacement("ogshadow.bin", "Shadow", "male", "true", 3, None, "shadow"), SpriteReplacement("ogedgar.bin", "Edgar", "male", "true", 4, None, "edgar"), SpriteReplacement("ogsabin.bin", "Sabin", "male", "true", 5, None, "sabin"), SpriteReplacement("ogceles.bin", "Celes", "female", "true", 6, None, "celes"), SpriteReplacement("ogstrago.bin", "Strago", "male", "true", 7, None, "strago"), SpriteReplacement("ogrelm.bin", "Relm", "female", "true", 8, None, "relm", "kids"), SpriteReplacement("ogsetzer.bin", "Setzer", "male", "true", 9, None, "setzer"), SpriteReplacement("ogmog.bin", "Mog", "neutral", "true", 10, None, "moogle"), SpriteReplacement("oggau.bin", "Gau", "male", "true", 11, None, "gau", "kids"), SpriteReplacement("oggogo.bin", "Gogo", "neutral", "true", 12, None, "gogo6"), SpriteReplacement("ogumaro.bin", "Umaro", "neutral", "true", 13, None, "umaro") ] if wild: og_replacements.extend([ SpriteReplacement("ogtrooper.bin", "Trooper", "neutral", "true", 14), SpriteReplacement("ogimp.bin", "Imp", "neutral", "true", 15), SpriteReplacement("ogleo.bin", "Leo", "male", "true", 16, None, "leo"), SpriteReplacement("ogbanon.bin", "Banon", "male", "true", 17, None, "banon"), SpriteReplacement("ogesperterra.bin", "Esper Terra", "female", "true", 0, "esperterra-p.bin", "terra"), SpriteReplacement("ogmerchant.bin", "Merchant", "male", "true", 1), SpriteReplacement("ogghost.bin", "Ghost", "neutral", "true", 18), SpriteReplacement("ogkefka.bin", "Kefka", "male", "true", 17, "kefka-p.bin", "kefka") ]) if clone_mode: used_vanilla = [ NAME_ID_DICT[vswaps[n]] for i, n in enumerate(char_ids) if not is_replaced[i] ] og_replacements = [ r for r in og_replacements if r.name not in used_vanilla ] known_replacements.extend(og_replacements) #weight selection based on no*/hate*/like*/love* codes whitelist = [ c.name[4:] for c in options_.active_codes if c.name.startswith("love") ] replace_candidates = [] for r in known_replacements: whitelisted = False for g in r.groups: if not r.weight: break if g in whitelist: whitelisted = True if options_.is_code_active("no" + g): r.weight = 0 elif options_.is_code_active("hate" + g): r.weight /= 3 elif options_.is_code_active("like" + g): r.weight *= 2 if whitelist and not whitelisted: r.weight = 0 if r.weight: replace_candidates.append(r) #select sprite replacements if not wild: female_candidates = [ c for c in replace_candidates if c.gender == "female" ] male_candidates = [c for c in replace_candidates if c.gender == "male"] neutral_candidates = [ c for c in replace_candidates if c.gender != "male" and c.gender != "female" ] swap_to = {} for char_id in random.sample(char_ids, len(char_ids)): if not is_replaced[char_id]: continue if wild: candidates = replace_candidates else: if char_id in female: candidates = female_candidates elif char_id in male: candidates = male_candidates else: candidates = neutral_candidates if random.randint(0, len(neutral_candidates) + 2 * len(candidates)) <= len(neutral_candidates): candidates = neutral_candidates if clone_mode: reverse_blacklist = [c for c in candidates if c.is_on(blacklist)] if reverse_blacklist: weights = [c.weight for c in reverse_blacklist] swap_to[char_id] = random.choices(reverse_blacklist, weights)[0] blacklist.update(swap_to[char_id].uniqueids) candidates.remove(swap_to[char_id]) continue final_candidates = [c for c in candidates if not c.is_on(blacklist)] if final_candidates: weights = [c.weight for c in final_candidates] swap_to[char_id] = random.choices(final_candidates, weights)[0] blacklist.update(swap_to[char_id].uniqueids) candidates.remove(swap_to[char_id]) else: print( f"custom sprite pool for {char_id} empty, using a vanilla sprite" ) return swap_to
def recolor_character_palette(fout, pointer, palette=None, flesh=False, middle=True, santa=False, skintones=None, char_hues=None, trance=False): fout.seek(pointer) if palette is None: palette = [read_multi(fout, length=2) for _ in range(16)] outline, eyes, hair, skintone, outfit1, outfit2, NPC = (palette[:2], palette[2:4], palette[4:6], palette[6:8], palette[8:10], palette[10:12], palette[12:]) def components_to_color(xxx_todo_changeme): (red, green, blue) = xxx_todo_changeme return red | (green << 5) | (blue << 10) new_style_palette = None if skintones and char_hues: new_style_palette = generate_character_palette(skintones, char_hues, trance=trance) # aliens, available in palette 5 only if flesh and random.randint(1, 20) == 1: transformer = get_palette_transformer(middle=middle) new_style_palette = transformer(new_style_palette) elif trance: new_style_palette = generate_character_palette(trance=True) new_palette = new_style_palette if new_style_palette else [] if not flesh: pieces = (outline, eyes, hair, skintone, outfit1, outfit2, NPC) if not new_style_palette else [NPC] for piece in pieces: transformer = get_palette_transformer(middle=middle) piece = list(piece) piece = transformer(piece) new_palette += piece if not new_style_palette: new_palette[6:8] = skintone if options_.is_code_active('christmas'): if santa: # color kefka's palette to make him look santa-ish new_palette = palette new_palette[8] = components_to_color((0x18, 0x18, 0x16)) new_palette[9] = components_to_color((0x16, 0x15, 0x0F)) new_palette[10] = components_to_color((0x1C, 0x08, 0x03)) new_palette[11] = components_to_color((0x18, 0x02, 0x05)) else: # give them red & green outfits red = [ components_to_color((0x19, 0x00, 0x05)), components_to_color((0x1c, 0x02, 0x04)) ] green = [ components_to_color((0x07, 0x12, 0x0b)), components_to_color((0x03, 0x0d, 0x07)) ] random.shuffle(red) random.shuffle(green) outfit = [red, green] random.shuffle(outfit) new_palette[8:10] = outfit[0] new_palette[10:12] = outfit[1] else: transformer = get_palette_transformer(middle=middle) new_palette = transformer(palette) if new_style_palette: new_palette = new_style_palette[0:12] + new_palette[12:] palette = new_palette fout.seek(pointer) for p in palette: write_multi(fout, p, length=2) return palette
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 remap_maps(routes): conlinks = [] cononeways = [] conentrances = [] conclusters = [] for route in routes: conlinks.extend(route.consolidated_links) cononeways.extend(route.consolidated_oneways) conentrances.extend(route.consolidated_entrances) conclusters.extend(route.consolidated_clusters) conclusters = sorted(set(conclusters), key=lambda c: c.clusterid) if ANCIENT: unused_maps = [ l.locid for l in get_locations() if l.locid not in towerlocids and l.locid not in PROTECTED ] rest_maps = [l.locid for l in get_unused_locations() if l.locid != 414] else: unused_maps = [l.locid for l in get_unused_locations()] rest_maps = [] for cluster in conclusters: if not isinstance(cluster, RestStop): continue locid = cluster.locid newlocid = rest_maps.pop() locexchange[(locid, locid)] = newlocid try: unused_maps.remove(newlocid) except: import pdb pdb.set_trace() for cluster in conclusters: if isinstance(cluster, RestStop): continue locid = cluster.locid if (locid, cluster.clusterid) in locexchange: continue locclusters = [ c for c in conclusters if not isinstance(c, RestStop) and c.locid == locid ] if locid in towerlocids: for c in locclusters: locexchange[(locid, c.clusterid)] = locid else: location = get_location(locid) if locid in unused_maps: newlocid = locid unused_maps = [u for u in unused_maps if u != newlocid] else: newlocid = unused_maps.pop() if location.longentrances: locexchange[(locid, cluster.clusterid)] = newlocid else: for c in locclusters: locexchange[(locid, c.clusterid)] = newlocid newlocations = [] for newlocid in sorted(set(locexchange.values())): keys = [ key for (key, value) in locexchange.items() if value == newlocid ] assert len(set([a for (a, b) in keys])) == 1 copylocid = keys[0][0] if copylocid >= 1000: cluster = [c for c in conclusters if c.locid == copylocid][0] copylocid = 413 location = get_location(413) newlocation = Location(locid=newlocid, dummy=True) newlocation.copy(location) newlocation.events = [] newlocation.npcs = [] newlocation.entrance_set.entrances = [] newlocation.restrank = cluster.rank else: location = get_location(copylocid) entrances = location.entrances newlocation = Location(locid=newlocid, dummy=True) newlocation.copy(location) newlocation.events = [] newlocation.npcs = [] newlocation.entrance_set.entrances = [] fixed = [ e for e in entrances if (e.location.locid, e.entid) in FIXED_ENTRANCES ] newlocation.entrance_set.entrances.extend(fixed) locclusters = [ c for c in conclusters if locexchange[(c.locid, c.clusterid)] == newlocid ] clustents = [e for c in locclusters for e in c.entrances] clustents = [e for e in clustents if e in conentrances] for ent in clustents: destent = [(a, b) for (a, b) in conlinks if ent in (a, b)] destent += [(a, b) for (a, b) in cononeways if ent == a] assert len(destent) == 1 destent = destent[0] destent = [d for d in destent if d != ent][0] destclust = [c for c in conclusters if destent in c.entrances] assert len(destclust) == 1 destclust = destclust[0] newdestlocid = locexchange[(destclust.locid, destclust.clusterid)] if destent.location.locid >= 1000: destloc = get_location(413) destent = [d for d in destloc.entrances if d.entid == 3][0] else: destloc = get_location(destent.location.locid) destent = [ d for d in destloc.entrances if d.entid == destent.entid ][0] mirror = destent.mirror if mirror: dest = mirror.dest & 0xFE00 destx, desty = mirror.destx, mirror.desty if abs(destx - destent.x) + abs(desty - destent.y) > 3: mirror = None if not mirror: dest, destx, desty = 0x2000, destent.x, destent.y dest &= 0x3DFF dest |= newdestlocid entrance = Entrance() entrance.x, entrance.y = ent.x, ent.y entrance.dest, entrance.destx, entrance.desty = dest, destx, desty entrance.set_location(newlocation) newlocation.entrance_set.entrances.append(entrance) newlocation.setid = 0 newlocation.ancient_rank = 0 newlocation.copied = copylocid adjents = [] for ent in newlocation.entrances: for clust in locclusters: if isinstance(clust, RestStop): continue assert clust.locid == newlocation.copied if clust.has_adjacent_entrances: x, y = ent.x, ent.y for ent2 in clust.entgroups.keys(): if ent2.x == ent.x and ent2.y == ent.y: break else: continue entgroup = clust.entgroups[ent2] for ent3 in entgroup: x3, y3 = ent3.x, ent3.y if x == x3 and y == y3: continue entrance = Entrance() entrance.x, entrance.y = x3, y3 entrance.dest, entrance.destx, entrance.desty = ( ent.dest, ent.destx, ent.desty) entrance.set_location(newlocation) adjents.append(entrance) newlocation.entrance_set.entrances.extend(adjents) newlocations.append(newlocation) locations = get_locations() newlocids = [l.locid for l in newlocations] assert len(newlocids) == len(set(newlocids)) for location in newlocations: for e in location.entrances: if (location.locid, e.entid) not in FIXED_ENTRANCES: assert e.dest & 0x1FF in newlocids assert location not in locations if location.locid not in towerlocids: location.entrance_set.convert_longs() # XXX: Unnecessary??? for i, loc in enumerate(newlocations): if loc.locid in towerlocids: oldloc = get_location(loc.locid) oldloc.entrance_set.entrances = loc.entrances oldloc.ancient_rank = loc.ancient_rank oldloc.copied = oldloc.locid newlocations[i] = oldloc ranked_clusters = [] for n in range(len(routes[0].segments)): rankedcsets = [route.segments[n].ranked_clusters for route in routes] for tricluster in zip_longest(*rankedcsets, fillvalue=None): tricluster = list(tricluster) random.shuffle(tricluster) for cluster in tricluster: if cluster is None: continue if cluster.locid not in ranked_clusters: cluster.routerank = n ranked_clusters.append(cluster) ranked_locations = [] for cluster in ranked_clusters: locid, clusterid = cluster.locid, cluster.clusterid newlocid = locexchange[locid, clusterid] newloc = [l for l in newlocations if l.locid == newlocid][0] if newloc not in ranked_locations: newloc.routerank = cluster.routerank ranked_locations.append(newloc) assert len(set(ranked_locations)) == len(set(newlocations)) ranked_locations = [ l for l in ranked_locations if not hasattr(l, "restrank") ] for i, loc in enumerate(ranked_locations): loc.ancient_rank = i loc.make_tower_basic() if not ANCIENT: if loc.locid not in towerlocids: loc.make_tower_flair() from options import options_ loc.unlock_chests( 200, 1000, uncapped_monsters=options_.is_code_active('bsiab')) fsets = get_new_fsets("kefka's tower", 20) fset = random.choice(fsets) for formation in fset.formations: formation.set_music(6) formation.set_continuous_music() loc.setid = fset.setid switch292, gate292 = (292, 0), (292, 1) switch334, gate334 = (334, 5), (334, 3) swd = {switch292: None, switch334: None, gate292: None, gate334: None} segments = [s for route in routes for s in route.segments] for segment in segments: for cluster in segment.ranked_clusters: for key in list(swd.keys()): locid, entid = key if cluster.locid == locid and entid in cluster.entids: assert swd[key] is None swd[key] = (segment, cluster) assert None not in list(swd.values()) s292segment, s292cluster = swd[switch292] s334segment, s334cluster = swd[switch334] g292segment, g292cluster = swd[gate292] g334segment, g334cluster = swd[gate334] if s292segment == g334segment and s334segment == g292segment: assert s292segment != s334segment ranked292 = s292segment.ranked_clusters ranked334 = s334segment.ranked_clusters if (ranked292.index(s292cluster) > ranked292.index(g334cluster) and ranked334.index(s334cluster) > ranked334.index(g292cluster)): raise Exception("Dungeon cannot be completed with this layout.") return newlocations, unused_maps