def get_external_candidates(self, num=2, test=False): if not self.clusters: return None candidates = [] linked_clusters = [] for e in self.linked_entrances: for c in self.clusters: if e in c.entrances: linked_clusters.append(c) done_clusts = set([]) done_ents = set(self.linked_entrances) for _ in range(num): candclusts = [c for c in self.clusters if set(c.entrances)-done_ents] tempclusts = [c for c in candclusts if c not in done_clusts] if tempclusts: candclusts = tempclusts tempclusts = [c for c in candclusts if not set(c.entrances) & set(self.linked_edge)] if tempclusts: candclusts = tempclusts try: #chosen = self.get_max_edge_distance(candclusts) chosen = random.choice(candclusts) except IndexError: return None done_clusts.add(chosen) choices = [c for c in chosen.entrances if c not in done_ents] chosen = random.choice(choices) done_ents.add(chosen) candidates.append(chosen) if not test: self.linked_edge.extend(candidates) return candidates
def interconnect(self): self.links = [] if len(self.clusters) < 2: return starter = max(self.clusters, key=lambda c: len(c.entrances)) while True: links = [] done_ents = set([]) done_clusts = [starter] clusters = self.clusters random.shuffle(clusters) for c in clusters: if c in done_clusts: continue prelim = max(done_clusts, key=lambda c2: len(set(c2.entrances)-done_ents)) numents = len(set(prelim.entrances) - done_ents) if numents == 0: break candidates = [c2 for c2 in done_clusts if len(set(c2.entrances)-done_ents) == numents] chosen = random.choice(candidates) acands = [e for e in c.entrances if e not in done_ents] bcands = [e for e in chosen.entrances if e not in done_ents] a, b = random.choice(acands), random.choice(bcands) if c not in done_clusts: done_clusts.append(c) done_ents.add(a) done_ents.add(b) links.append((a, b)) if set(done_clusts) == set(self.clusters): break self.links = links
def set_spells(self, valid_spells, spellsets=None, spellclass=None): spellsets = spellsets or get_spellsets(spells=valid_spells) spellclass = spellclass or random.choice(spellsets.keys()) self.name = spellclass desc, spellset = spellsets[spellclass] self.spells_description = desc if spellclass.lower() in ["chaos"]: self.wild = True self.spells = [] return else: self.wild = False spellset = sorted([s for s in spellset if s in valid_spells], key=lambda s: s.spellid) if len(spellset) < 3: raise ValueError("Spellset %s not big enough." % spellclass) for setlength in [8, 16, 32]: if len(spellset) <= setlength: spells = sorted(spellset) while len(spells) < setlength: spells.append(random.choice(spellset)) break else: assert setlength == 32 spells = random.sample(sorted(spellset), min(setlength, len(spellset))) while len(spells) < setlength: spells.append(random.choice(spellset)) assert len(set(spells)) > 16 self.spells = sorted(spells) return self.spells
def get_external_candidates(self, num=2, test=False): if not self.clusters: return None candidates = [] linked_clusters = [] for e in self.linked_entrances: for c in self.clusters: if e in c.entrances: linked_clusters.append(c) done_clusts = set([]) done_ents = set(self.linked_entrances) for _ in xrange(num): candclusts = [c for c in self.clusters if set(c.entrances)-done_ents] tempclusts = [c for c in candclusts if c not in done_clusts] if tempclusts: candclusts = tempclusts tempclusts = [c for c in candclusts if not set(c.entrances) & set(self.linked_edge)] if tempclusts: candclusts = tempclusts try: #chosen = self.get_max_edge_distance(candclusts) chosen = random.choice(candclusts) except IndexError: return None done_clusts.add(chosen) choices = [c for c in chosen.entrances if c not in done_ents] chosen = random.choice(choices) done_ents.add(chosen) candidates.append(chosen) if not test: self.linked_edge.extend(candidates) return candidates
def generate_name(size=None, maxsize=10): if not size: size = random.randint(1, 5) + random.randint(1, 5) if size < 4: size += random.randint(0, 5) def has_vowel(text): for c in text: if c.lower() in "aeiouy": return True return False while True: starts = sorted([s for s in generator if s[0].isupper()]) name = random.choice(starts) name = name[:size] while len(name) < size: key = name[-lookback:] if key not in generator and size - len(name) < len(key): name = random.choice(starts) continue if key not in generator or (random.randint(1, 15) == 15 and has_vowel(name[-2:])): if len(name) <= size - lookback: if len(name) + len(key) < maxsize: name += " " name += random.choice(starts) continue else: name = random.choice(starts) continue c = random.choice(generator[key]) name = name + c for ename in enemynames: if name == ename: name = "" break if name: for ename in enemynames: if len(name) > (lookback + 1): length = min(len(name), len(ename)) if name[:length] == ename[:length]: name = "" break if len(name) >= size: enemynames.append(name) return name
def random_unused_code(self): candidates = [ c for c in NORMAL_CODES if c not in self.active_codes and c.name != "repairpalette" ] i = random.randint(1, 7) if i <= 2: secret_codes = [ c for c in NORMAL_CODES if c.is_cyphered and not self.is_code_active(c) ] if secret_codes: candidates = secret_codes elif i <= 4: new_codes = MAKEOVER_MODIFIER_CODES + [ c for c in NORMAL_CODES if c.name in ["alasdraco"] ] new_codes = [c for c in new_codes if not self.is_code_active(c)] if new_codes: candidates = new_codes if not candidates: candidates = NORMAL_CODES + MAKEOVER_MODIFIER_CODES selected = random.choice(candidates) if selected.is_cyphered: f = FourSquare(selected.key1, selected.key2) return f.decypher(selected.name) return selected.name
def convert_longs(self): for longentrance in self.longentrances: longentrance.dest &= 0xFE00 longentrance.dest |= self.location.locid & 0x1FF e = random.choice(self.entrances) longentrance.destx = e.x longentrance.desty = e.y
def get_orphaned_formations(): global orphaned_formations if orphaned_formations is not None: return orphaned_formations orphaned_formations = set([]) from monsterrandomizer import get_monsters monsters = get_monsters() extra_miabs = get_extra_miabs(0) for m in monsters: if m.id in EVENT_ENEMIES: m.auxloc = "Event Battle" continue if not m.is_boss: location = m.determine_location() if "missing" in location.lower() or not location.strip(): formations = set([f for f in get_formations() if m in f.present_enemies and not f.has_boss]) formations = sorted(formations, key=lambda f: f.formid) try: f = random.choice(formations) orphaned_formations.add(f) except IndexError: pass for x in extra_miabs: if m in x.present_enemies: if x == f: continue ens = set(x.present_enemies) if len(ens) == 1: banned_formids.append(x.formid) orphaned_formations = sorted(orphaned_formations, key=lambda f: f.formid) return get_orphaned_formations()
def mutate_feature(self, feature=None): if self.is_consumable or self.is_tool: return if feature is None: feature = random.choice(list(STATPROTECT.keys())) nochange = STATPROTECT[feature] if feature == 'special2': # Allow rare merit award bit on relics if self.is_relic: if random.randint(1, 10) == 10: nochange &= ~0x20 # Reduce chance of Genji Glove bit on non-relics elif random.randint(1, 4) != 4: nochange |= 0x10 self.features[feature] = bit_mutate(self.features[feature], op="on", nochange=nochange) new_features = self.get_feature(feature, self.features[feature], nochange) if new_features != "": if "Special Feature" in self.mutation_log.keys(): for new_feature in new_features.split(", "): if new_feature not in self.mutation_log["Special Feature"]: self.mutation_log.update({"Special Feature": self.mutation_log["Special Feature"] + ", " + new_feature}) else: self.mutation_log["Special Feature"] = new_features self.mutate_name()
def allocate_espers(ancient_cave, espers, characters, fout): char_ids = list(range(12)) + [13] # everyone but Gogo characters = [c for c in characters if c.id in char_ids] chars_for_esper = [] max_rank = max(espers, key=lambda e: e.rank).rank for e in espers: num_users = 1 if e.id not in [15, 16] and random.randint(1,25) >= 25 - max_rank + e.rank: num_users += 1 while num_users < 15 and random.choice([True] + [False] * (e.rank + 2)): num_users += 1 users = random.sample(characters, num_users) chars_for_esper.append([c.id for c in users]) if not ancient_cave: chars_for_esper[12] = chars_for_esper[11] # make Odin and Raiden equippable by the same person/people char_mask_for_esper = [ reduce( lambda x, y: x | y, [1 << char_id for char_id in chars_for_esper[e.id]] ) for e in espers ] for e in espers: e.chars = ", ".join([c.newname for c in characters if c.id in chars_for_esper[e.id]]) # do substitution esper_allocator_sub = Substitution() esper_allocator_sub.set_location(0x31B61) esper_allocator_sub.bytestring = [0x20,0x00,0xF8] esper_allocator_sub.write(fout) esper_allocator_sub.set_location(0x35524) esper_allocator_sub.bytestring = [0x20,0x07,0xF8] esper_allocator_sub.write(fout) esper_allocator_sub.set_location(0x358E1) esper_allocator_sub.write(fout) esper_allocator_sub.set_location(0x359B1) esper_allocator_sub.write(fout) esper_allocator_sub.set_location(0x35593) esper_allocator_sub.bytestring = [0xA9,0x2C] esper_allocator_sub.write(fout) esper_allocator_sub.set_location(0x355B2) esper_allocator_sub.bytestring = [0x20,0x2E,0xF8] esper_allocator_sub.write(fout) esper_allocator_sub.set_location(0x358E8) esper_allocator_sub.bytestring = [0xC9,0x20,0xF0,0x16] esper_allocator_sub.write(fout) esper_allocator_sub.set_location(0x3F800) esper_allocator_sub.bytestring = [0xAA, 0xB5, 0x69, 0x8D, 0xF8, 0x1C, 0x60, 0xDA, 0x08, 0x85, 0xE0, 0x0A, 0xAA, 0xE2, 0x10, 0xDA, 0xAD, 0xF8, 0x1C, 0x0A, 0xAA, 0xC2, 0x20, 0xBF, 0x67, 0x9C, 0xC3, 0xFA, 0x3F, 0x58, 0xF8, 0xC3, 0xF0, 0x05, 0x28, 0xFA, 0x4C, 0x76, 0x55, 0x28, 0xFA, 0xA9, 0x28, 0x4C, 0x95, 0x55, 0xBD, 0x02, 0x16, 0xC9, 0x80, 0xB0, 0x0F, 0xFA, 0xA6, 0x00, 0xBF, 0x4B, 0xF8, 0xC3, 0xF0, 0x07, 0x8D, 0x80, 0x21, 0xE8, 0x80, 0xF4, 0x60, 0x9C, 0x80, 0x21, 0x4C, 0xD9, 0x7F, 0x82, 0x9A, 0xA7, 0xC3, 0xAD, 0xFF, 0x9E, 0xAA, 0xAE, 0xA2, 0xA9, 0xBE, 0x00] + [i for sublist in map(int2bytes, char_mask_for_esper) for i in sublist] esper_allocator_sub.write(fout)
def manage_coral(fout): f = open_mei_fallback(CORAL_TABLE) coralnames = sorted( set(sanitize_coral([line.strip() for line in f.readlines()]))) f.close() sprite_log = "" newcoralname = random.choice(coralnames) sprite_log += str("Coral: ").ljust(17) + string.capwords( str(newcoralname)) + "\n" coraldescription1 = "Piece of " + newcoralname.lower() + "," coraldescription2 = "found near Ebot's Rock." load_patch_file("coral") set_dialogue_var("coralsub", newcoralname) newcoralnamebytes = name_to_bytes(newcoralname, 12) coraldescription1 = name_to_bytes(coraldescription1, 24) coraldescription2 = name_to_bytes(coraldescription2, 26) coraldescription = coraldescription1 + bytes([0x01]) + coraldescription2 coral_sub = Substitution() coral_sub.set_location(0xEFC08) ##change the name when opening a chest coral_sub.bytestring = newcoralnamebytes coral_sub.write(fout) coral_sub.set_location(0xEFD7F) ##Change the name in the description coral_sub.bytestring = coraldescription coral_sub.write(fout) return sprite_log
def intify(value): subintify = lambda x: int(x, 0x10) if ',' in value: return random.choice(list(map(subintify, value.split(',')))) else: return subintify(value)
def mutate_feature(self, feature=None): if self.is_consumable or self.is_tool: return if feature is None: feature = random.choice(STATPROTECT.keys()) self.features[feature] = bit_mutate(self.features[feature], op="on", nochange=STATPROTECT[feature])
def generate_spells(self, tierless=False): global used self.spells, self.learnrates = [], [] rank = self.rank if random.randint(1, 5) == 5: rank += 1 rank = min(rank, max(rankbounds.keys())) if random.randint(1, 10) != 10: candidates = self.get_candidates(rank, set_lower=not tierless, allow_quick=tierless) if candidates: s = random.choice(candidates) self.spells.append(s) used.add(s) rank = self.rank for _ in range(random.randint(0, 2) + random.randint(0, 2)): candidates = self.get_candidates(rank, set_lower=False, allow_quick=tierless) if candidates: s = random.choice(candidates) if s in self.spells: continue self.spells.append(s) used.add(s) self.spells = sorted(self.spells, key=lambda s: s.spellid) self.learnrates = [] esperrank = rankbounds[self.rank] if esperrank is None: esperrank = rankbounds[self.rank - 1] esperrank = esperrank * 3 for s in self.spells: spellrank = s.rank() learnrate = int(esperrank / float(spellrank)) learnrate = random.randint(0, learnrate) + random.randint( 0, learnrate) while random.randint(1, 3) == 3: learnrate += 1 learnrate = max(1, min(learnrate, 20)) self.learnrates.append(learnrate)
def make_secret_treasure_room(mapid, beltroom): from itemrandomizer import get_secret_item candidates = [] for line in open(TREASURE_ROOMS_TABLE): locid, entid, chestid = tuple(map(int, line.strip().split(','))) location = get_location(locid) entrance = location.get_entrance(entid) chest = location.get_chest(chestid) candidates.append((location, entrance, chest)) location, entrance, chest = random.choice(candidates) newlocation = Location(mapid) newlocation.copy(location) newlocation.make_tower_basic() newlocation.entrance_set.entrances = [] newlocation.events = [] newlocation.npcs = [] c = ChestBlock(pointer=None, location=newlocation.locid) c.copy(chest) c.set_content_type(0x40) item = get_secret_item() c.contents = item.itemid c.set_new_id() c.do_not_mutate = True c.ignore_dummy = True newlocation.chests = [c] newlocation.secret_treasure = True newlocation.ancient_rank = 0 e = Entrance(None) e.copy(entrance) e.set_location(newlocation) e.destx, e.desty, e.dest = 36, 27, 287 newlocation.entrances.append(e) assert len(newlocation.entrances) == 1 e2 = Entrance() e2.x = 36 e2.y = 25 e2.destx, e2.desty, e2.dest = e.x, e.y, mapid beltroom.entrance_set.entrances = [ ent for ent in beltroom.entrances if not (ent.x == 36 and ent.y == 25) ] beltroom.entrance_set.entrances.append(e2) final_room = get_location(411) e3 = Entrance() e3.x = 109 e3.y = 46 e3.destx, e3.desty, e3.dest = 82, 46, 412 | 0x2000 final_room.entrance_set.entrances.append(e3) newlocation.attacks = 0 newlocation.setid = 0 newlocation.music = 21 return newlocation, beltroom
def elemshuffle(elements): elemcount = bin(elements).count('1') while random.randint(1, 5) == 5: elemcount += random.choice([-1, 1]) elemcount = max(0, min(elemcount, 8)) elements = 0 while elemcount > 0: elements = elements | (1 << random.randint(0, 7)) elemcount += -1 return elements
def generate_attack(): if random.randint(1, 7) != 7: while True: modifier = random.choice(modifiers) move = random.choice(moves) if len(modifier) + len(move) <= 10: break else: modifier = "" if random.randint(1, 4) != 4: candidates = list(moves) else: candidates = list(modifiers) candidates = [c for c in candidates if len(c) >= 3] move = random.choice(candidates) if len(modifier) + len(move) < 10: return ("%s %s" % (modifier, move)).strip() return modifier + move
def mutate(self, ap=False): if ap and self.ap is not None and 0 < self.ap < 100: factor = self.levelrank() / 100 self.ap += int(round(self.ap * factor)) while random.choice([True, False]): self.ap += random.randint(-1, 1) self.ap = min(100, max(self.ap, 0)) if self.ambusher: if not (self.pincer_prohibited and self.back_prohibited): self.misc1 |= 0x90
def narrow_unknowns(self, strict=False): unknowns = [u for u in unknown_itemcodes if u in [i.itemtype for i in self.levelobjects]] if unknowns: u = random.choice(unknowns) self.levelobjects = [i for i in self.levelobjects if i.itemtype == u or i.itemtype not in unknowns] if strict: self.levelobjects = [i for i in self.levelobjects if i.itemtype == u]
def make_secret_treasure_room(mapid, beltroom): from itemrandomizer import get_secret_item candidates = [] for line in open(TREASURE_ROOMS_TABLE): locid, entid, chestid = tuple(map(int, line.strip().split(','))) location = get_location(locid) entrance = location.get_entrance(entid) chest = location.get_chest(chestid) candidates.append((location, entrance, chest)) location, entrance, chest = random.choice(candidates) newlocation = Location(mapid) newlocation.copy(location) newlocation.make_tower_basic() newlocation.entrance_set.entrances = [] newlocation.events = [] newlocation.npcs = [] c = ChestBlock(pointer=None, location=newlocation.locid) c.copy(chest) c.set_content_type(0x40) item = get_secret_item() c.contents = item.itemid c.set_new_id() c.do_not_mutate = True c.ignore_dummy = True newlocation.chests = [c] newlocation.secret_treasure = True newlocation.ancient_rank = 0 e = Entrance(None) e.copy(entrance) e.set_location(newlocation) e.destx, e.desty, e.dest = 36, 27, 287 newlocation.entrances.append(e) assert len(newlocation.entrances) == 1 e2 = Entrance() e2.x = 36 e2.y = 25 e2.destx, e2.desty, e2.dest = e.x, e.y, mapid beltroom.entrance_set.entrances = [ent for ent in beltroom.entrances if not (ent.x == 36 and ent.y == 25)] beltroom.entrance_set.entrances.append(e2) final_room = get_location(411) e3 = Entrance() e3.x = 109 e3.y = 46 e3.destx, e3.desty, e3.dest = 82, 46, 412 | 0x2000 final_room.entrance_set.entrances.append(e3) newlocation.attacks = 0 newlocation.setid = 0 newlocation.music = 21 return newlocation, beltroom
def get_max_edge_distance(self, clusters): if len(clusters) == 1: return random.choice(clusters) if not self.linked_edge: if len(clusters) == 2: return random.choice(clusters) linked = [random.choice(clusters)] clusters = [c for c in clusters if c not in linked] else: linked = [self.get_entrance_cluster(e) for e in self.linked_edge] scores = {} for c in clusters: scores[c] = 9999 for l in linked: d = self.calculate_distance(c, l) scores[c] = min(d, scores[c]) hiscore = max(scores.values()) assert hiscore < 999 clusters = [c for c in clusters if scores[c] == hiscore] return random.choice(clusters)
def generate_bonus(self): rank = self.rank candidates = set(bonus_ranks[rank]) candidates = candidates - used_bonuses if candidates: candidates = sorted(candidates) self.bonus = random.choice(candidates) used_bonuses.add(self.bonus) return if random.randint(1, 2) == 2: rank += 1 while random.randint(1, 10) == 10: rank += 1 rank = min(rank, 4) candidates = [] for i in range(rank + 1): candidates.extend(bonus_ranks[i]) if candidates: self.bonus = random.choice(candidates) used_bonuses.add(self.bonus)
def evade_is_screwed_up(value): while random.randint(1, 8) == 8: if value == 0: choices = [1, 6] elif value in [5, 0xA]: choices = [-1] elif value == 6: choices = [1, -6] else: choices = [1, -1] value += random.choice(choices) return value
def generate_bonus(self): rank = self.rank candidates = set(bonus_ranks[rank]) candidates = candidates - used_bonuses if candidates: candidates = sorted(candidates) self.bonus = random.choice(candidates) used_bonuses.add(self.bonus) return if random.randint(1, 2) == 2: rank += 1 while random.randint(1, 10) == 10: rank += 1 rank = min(rank, 4) candidates = [] for i in range(rank+1): candidates.extend(bonus_ranks[i]) if candidates: self.bonus = random.choice(candidates) used_bonuses.add(self.bonus)
def mutate(self, mp=False, mp_boost_value=None): if mp and self.mp is not None and 0 < self.mp < 100: factor = self.levelrank() / 100 self.mp += int(round(self.mp * factor)) while random.choice([True, False]): self.mp += random.randint(-1, 1) self.mp = min(100, max(self.mp, 0)) if mp_boost_value: self.mp = min(255, floor(self.mp * float(mp_boost_value))) if self.ambusher: if not (self.pincer_prohibited and self.back_prohibited): self.misc1 |= 0x90
def randomize_forest(): start = get_location(0x84) end = get_location(0x87) mids = [get_location(x) for x in (0x85, 0x86)] location86 = get_location(0x86) go_to_train_event = location86.events[0] location86.events.remove(go_to_train_event) world_location_exit = Entrance() world_location_exit.copy(location86.entrances[1]) location = random.choice(mids) exit = random.choice(location.entrances) map_entrance_to_exit(start.entrances[0], exit) location2 = [m for m in mids if m.locid != location.locid][0] entrances = [e for e in location.entrances if e != exit] entrance = random.choice(entrances) exit2 = random.choice(location2.entrances) map_entrance_to_exit(entrance, exit2) entrances = [e for e in location2.entrances if e != exit] entrance2 = random.choice(entrances) entrance2.dest = world_location_exit.dest entrance2.destx = world_location_exit.destx entrance2.desty = world_location_exit.desty go_to_train_event.x = entrance2.x go_to_train_event.y = entrance2.y location2.events.append(go_to_train_event) wrong_entrances = [ e for m in mids for e in m.entrances if e not in (entrance, entrance2) ] wrong_exits = [e for m in (mids + [start]) for e in m.entrances] for e in wrong_entrances: exit = random.choice(wrong_exits) map_entrance_to_exit(e, exit)
def generate_spells(self): global used self.spells, self.learnrates = [], [] rank = self.rank if random.randint(1, 5) == 5: rank += 1 rank = min(rank, max(rankbounds.keys())) if random.randint(1, 10) != 10: candidates = self.get_candidates(rank) if candidates: s = random.choice(candidates) self.spells.append(s) used.add(s) rank = self.rank for _ in xrange(random.randint(0, 2) + random.randint(0, 2)): candidates = self.get_candidates(rank, set_lower=False) if candidates: s = random.choice(candidates) if s in self.spells: continue self.spells.append(s) used.add(s) self.spells = sorted(self.spells, key=lambda s: s.spellid) self.learnrates = [] esperrank = rankbounds[self.rank] if esperrank is None: esperrank = rankbounds[self.rank-1] esperrank = esperrank * 3 for s in self.spells: spellrank = s.rank() learnrate = int(esperrank / float(spellrank)) learnrate = random.randint(0, learnrate) + random.randint(0, learnrate) while random.randint(1, 3) == 3: learnrate += 1 learnrate = max(1, min(learnrate, 20)) self.learnrates.append(learnrate)
def set_pronoun(name, gender, force=True): gender = gender.lower() name = name.lower().capitalize() if "random" in gender: force = True opts = ["male"] * 9 + ["female"] * 9 + [ "neutral" ] * 2 if "truerandom" not in gender else ["male", "female", "neutral"] if gender == "orandom": opts += ["object"] * 20 gender = random.choice(opts) if not force: if gender == "neutral": gender = random.choice(["male"] * 9 + ["female"] * 9 + ["neutral"] * 2) elif gender != "object": gender = random.choice([gender] * 19 + ["neutral"]) if gender == "male": pset = ("he", "him", "his", "his", "he's") set_dialogue_flag(name + "Plu", False) elif gender == "female": pset = ("she", "her", "her", "hers", "she's") set_dialogue_flag(name + "Plu", False) elif gender == "object": pset = ("it", "it", "its", "its", "it's") set_dialogue_flag(name + "Plu", False) else: pset = ("they", "them", "their", "theirs", "they're") set_dialogue_flag(name + "Plu") gender = "neutral" pmap = ("Ey", "Em", "Eir", "Eirs", "EyIs") for i in range(5): set_dialogue_var(name + pmap[i], pset[i]) return gender
def become_another(self, customdict=None, tier=None): customs = get_custom_items() if customdict is None: if tier is None: candidates = [customs[key] for key in sorted(customs)] else: candidates = [ customs[key] for key in sorted(customs) if customs[key]["tier"] == tier ] customdict = random.choice(candidates) for key in self.features: self.features[key] = 0 def convert_value(v): v = v.split() def intify(value): subintify = lambda x: int(x, 0x10) if ',' in value: return random.choice(list(map(subintify, value.split(',')))) else: return subintify(value) if len(v) == 1: return intify(v[0]) else: return list(map(intify, v)) name = bytes([]) for key, value in customdict.items(): if key == "name_text": name = name + name_to_bytes(value, 12) elif key == "description": pass elif key == "tier": pass else: value = convert_value(value) if key == "name_icon": name = bytes([value]) + name elif hasattr(self, key): setattr(self, key, value) elif key in self.features: self.features[key] = value self.dataname = name self.ban()
def fill_out(self, additional=None): linked = self.linked_entrances links = [] unlinked = [] for cluster in self.clusters: entrances = [e for e in cluster.entrances if e not in linked] random.shuffle(entrances) if ANCIENT: unlinked.extend(entrances) else: if len(cluster.entrances) <= 4: unlinked.extend(entrances) else: diff = len(cluster.entrances) - len(entrances) if diff < 3: remaining = 3 - diff unlinked.extend(entrances[:remaining]) if additional: unlinked.append(additional) if not unlinked: return random.shuffle(unlinked) locids = [e.location.locid for e in unlinked] maxlocid = max(locids, key=locids.count) mosts = [e for e in unlinked if e.location.locid == maxlocid] lesses = [e for e in unlinked if e not in mosts] for m in mosts: if not lesses: break l = random.choice(lesses) links.append((m, l)) lesses.remove(l) unlinked.remove(l) unlinked.remove(m) extra = None while unlinked: if len(unlinked) == 1: extra = unlinked[0] break u1 = unlinked.pop() u2 = unlinked.pop() links.append((u1, u2)) self.links += links return extra
def fill_out(self, additional=None): linked = self.linked_entrances links = [] unlinked = [] for cluster in self.clusters: entrances = [e for e in cluster.entrances if e not in linked] random.shuffle(entrances) if ANCIENT: unlinked.extend(entrances) else: if len(cluster.entrances) <= 4: unlinked.extend(entrances) else: diff = len(cluster.entrances) - len(entrances) if diff < 3: remaining = 3 - diff unlinked.extend(entrances[:remaining]) if additional: unlinked.append(additional) if not unlinked: return random.shuffle(unlinked) locids = [e.location.locid for e in unlinked] maxlocid = max(locids, key=lambda l: locids.count(l)) mosts = [e for e in unlinked if e.location.locid == maxlocid] lesses = [e for e in unlinked if e not in mosts] for m in mosts: if not lesses: break l = random.choice(lesses) links.append((m, l)) lesses.remove(l) unlinked.remove(l) unlinked.remove(m) extra = None while unlinked: if len(unlinked) == 1: extra = unlinked[0] break u1 = unlinked.pop() u2 = unlinked.pop() links.append((u1, u2)) self.links += links return extra
def randomize_passwords(): passwords = [[], [], []] with open_mei_fallback(PASSWORDS_TABLE) as passwords_file: i = 0 for line in passwords_file: line = line.split('#')[0].strip() if not line: continue if line.startswith("------") and i < len(passwords) - 1: i += 1 continue passwords[i].append(line) if all(passwords): text = get_dialogue(0xE0) text = text.replace("Rose bud", random.choice(passwords[0])) text = text.replace("Courage", random.choice(passwords[1])) text = text.replace("Failure", random.choice(passwords[2])) set_dialogue(0xE0, text)
def fill_out(self): entrances = list(self.consolidated_entrances) seen = [] for cluster, inter in zip(self.clusters, self.intersegments): if cluster.locid == 334 and 11 in cluster.entids: additionals = [e for e in cluster.entrances if e not in self.consolidated_entrances] assert len(additionals) == 1 extra = inter.fill_out(additionals[0]) else: extra = inter.fill_out() seen.extend([e for e in cluster.entrances if e in entrances]) for c in inter.clusters: seen.extend([e for e in c.entrances if e in entrances]) if extra is not None: backtrack = random.choice(seen) self.oneway_entrances.append((extra, backtrack))
def randomize_poem(fout): poems = [] with open_mei_fallback(POEMS_TABLE, encoding='utf8') as poems_file: current_poem = [] page_break = False wait = 0 for line in poems_file: line = line.split('#')[0].strip() if not line: if current_poem: page_break = True continue if line.startswith("---") and current_poem: current_poem.append("<wait 390 frames><wait 1 frame>") wait += 1 poems.append(("".join(current_poem), wait)) current_poem = [] page_break = False wait = 0 continue if page_break: current_poem.append(POEM_PAGE_BREAK) wait += 1 page_break = False elif current_poem: current_poem.append("<line>") current_poem.append(line) if not poems: return text, wait = random.choice(poems) set_dialogue(0x9FC, text) wait = min(wait * 26 + 2, 255) # Adjust wait to be long enough for the poem wait_sub = Substitution() wait_sub.set_location(0xC401D) wait_sub.bytestring = bytes([0xB5, wait]) wait_sub.write(fout)
def mutate_feature(self, feature=None): if self.is_consumable or self.is_tool: return if feature is None: feature = random.choice(list(STATPROTECT.keys())) nochange = STATPROTECT[feature] if feature == 'special2': # Allow rare merit award bit on relics if self.is_relic: if random.randint(1, 10) == 10: nochange &= ~0x20 # Reduce chance of Genji Glove bit on non-relics elif random.randint(1, 4) != 4: nochange |= 0x10 self.features[feature] = bit_mutate(self.features[feature], op="on", nochange=nochange)
def remove_adjacent_entrances(self): if not self.has_adjacent_entrances: return self.entgroups = {} for e in self.entrances: self.entgroups[e] = set([e]) for e1 in self.entrances: for e2 in self.entrances: if e1 == e2: continue if ((e1.x == e2.x and abs(e1.y - e2.y) == 1) or (abs(e1.x - e2.x) == 1 and e1.y == e2.y)): self.entgroups[e1].add(e2) for e1 in self.entgroups: for e2 in self.entgroups[e1]: assert self.entgroups[e1] == self.entgroups[e2] entgroups = [] keys = sorted(list(self.entgroups.keys()), key=lambda k: k.entid) entids = [k.entid for k in keys] assert len(entids) == len(set(entids)) for key in keys: entgroup = self.entgroups[key] if entgroup not in entgroups: entgroups.append(entgroup) for eg1 in entgroups: for eg2 in entgroups: if eg1 == eg2: continue assert eg1 & eg2 == set([]) for entgroup in entgroups: if len(entgroup) > 1: entgroup = sorted(entgroup, key=lambda e: e.entid) ent = random.choice(entgroup) for e in entgroup: if e != ent: self.entrances.remove(e) assert self.has_adjacent_entrances
def remove_adjacent_entrances(self): if not self.has_adjacent_entrances: return self.entgroups = {} for e in self.entrances: self.entgroups[e] = set([e]) for e1 in self.entrances: for e2 in self.entrances: if e1 == e2: continue if ((e1.x == e2.x and abs(e1.y - e2.y) == 1) or (abs(e1.x - e2.x) == 1 and e1.y == e2.y)): self.entgroups[e1].add(e2) for e1 in self.entgroups: for e2 in self.entgroups[e1]: assert self.entgroups[e1] == self.entgroups[e2] entgroups = [] keys = sorted(self.entgroups.keys(), key=lambda k: k.entid) entids = [k.entid for k in keys] assert len(entids) == len(set(entids)) for key in keys: entgroup = self.entgroups[key] if entgroup not in entgroups: entgroups.append(entgroup) for eg1 in entgroups: for eg2 in entgroups: if eg1 == eg2: continue assert eg1 & eg2 == set([]) for entgroup in entgroups: if len(entgroup) > 1: entgroup = sorted(entgroup, key=lambda e: e.entid) ent = random.choice(entgroup) for e in entgroup: if e != ent: self.entrances.remove(e) assert self.has_adjacent_entrances
def become_another(self, customdict=None): customs = get_custom_items() if customdict is None: customdict = random.choice( [customs[key] for key in sorted(customs)]) for key in self.features: self.features[key] = 0 def convert_value(v): v = v.split() def intify(value): subintify = lambda x: int(x, 0x10) if ',' in value: return random.choice(map(subintify, value.split(','))) else: return subintify(value) if len(v) == 1: return intify(v[0]) else: return map(intify, v) name = [] for key, value in customdict.items(): if key == "name_text": name = name + name_to_bytes(value, 12) elif key == "description": pass else: value = convert_value(value) if key == "name_icon": name = [value] + name elif hasattr(self, key): setattr(self, key, value) elif key in self.features: self.features[key] = value self.dataname = name self.ban()
def randomize_fanatics(unused_locids): stairs = [get_location(i) for i in [363, 359, 360, 361]] pitstops = [get_location(i) for i in [365, 367, 368, 369]] num_new_levels = random.randint(0, 1) + random.randint(1, 2) unused_locations = [get_location(l) for l in unused_locids] fsets = get_new_fsets("fanatics", 10, supplement=False) for _ in xrange(num_new_levels): stair = unused_locations.pop() stop = unused_locations.pop() stair.copy(random.choice(stairs[1:-1])) stop.copy(random.choice(pitstops[1:])) add_location_map("Fanatics Tower", stair.locid) add_location_map("Fanatics Tower", stop.locid) index = random.randint(1, len(stairs)-1) stairs.insert(index, stair) pitstops.insert(index, stop) chest = stop.chests[0] chest.set_new_id() entrance = stop.entrances[0] entrance.dest = (entrance.dest & 0xFE00) | (stair.locid & 0x1FF) entrance = sorted(stair.entrances, key=lambda e: e.y)[1] entrance.dest = (entrance.dest & 0xFE00) | (stop.locid & 0x1FF) stair.setid = random.choice(fsets).setid stop.setid = random.choice(fsets).setid for a, b in zip(stairs, stairs[1:]): lower = sorted(a.entrances, key=lambda e: e.y)[0] upper = sorted(b.entrances, key=lambda e: e.y)[-1] lower.dest = (lower.dest & 0xFE00) | (b.locid & 0x1FF) upper.dest = (upper.dest & 0xFE00) | (a.locid & 0x1FF) for stop in pitstops: if random.choice([True, False]): continue index = pitstops.index(stop) if index == 0: continue index2 = index + random.choice([-1, -1, -2]) if index2 < 0: index2 = 0 stair = stairs[index2] entrance = stop.entrances[0] entrance.dest = (entrance.dest & 0xFE00) | (stair.locid & 0x1FF)
def randomize_fanatics(unused_locids): stairs = [get_location(i) for i in [363, 359, 360, 361]] pitstops = [get_location(i) for i in [365, 367, 368, 369]] num_new_levels = random.randint(0, 1) + random.randint(1, 2) unused_locations = [get_location(l) for l in unused_locids] fsets = get_new_fsets("fanatics", 10, supplement=False) for _ in range(num_new_levels): stair = unused_locations.pop() stop = unused_locations.pop() stair.copy(random.choice(stairs[1:-1])) stop.copy(random.choice(pitstops[1:])) add_location_map("Fanatics Tower", stair.locid) add_location_map("Fanatics Tower", stop.locid) index = random.randint(1, len(stairs) - 1) stairs.insert(index, stair) pitstops.insert(index, stop) chest = stop.chests[0] chest.set_new_id() entrance = stop.entrances[0] entrance.dest = (entrance.dest & 0xFE00) | (stair.locid & 0x1FF) entrance = sorted(stair.entrances, key=lambda e: e.y)[1] entrance.dest = (entrance.dest & 0xFE00) | (stop.locid & 0x1FF) stair.setid = random.choice(fsets).setid stop.setid = random.choice(fsets).setid for a, b in zip(stairs, stairs[1:]): lower = sorted(a.entrances, key=lambda e: e.y)[0] upper = sorted(b.entrances, key=lambda e: e.y)[-1] lower.dest = (lower.dest & 0xFE00) | (b.locid & 0x1FF) upper.dest = (upper.dest & 0xFE00) | (a.locid & 0x1FF) for stop in pitstops: if random.choice([True, False]): continue index = pitstops.index(stop) if index == 0: continue index2 = index + random.choice([-1, -1, -2]) if index2 < 0: index2 = 0 stair = stairs[index2] entrance = stop.entrances[0] entrance.dest = (entrance.dest & 0xFE00) | (stair.locid & 0x1FF)
def mutate_nibble(byte, left=False, limit=7): if left: nibble = (byte & 0xf0) >> 4 byte = byte & 0x0f else: nibble = byte & 0x0f byte = byte & 0xf0 value = nibble & 0x7 if nibble & 0x8: value = value * -1 while random.randint(1, 6) == 6: value += random.choice([1, -1]) value = max(-limit, min(value, limit)) nibble = abs(value) if value < 0: nibble = nibble | 0x8 if left: return byte | (nibble << 4) else: return byte | nibble
def reset_special_relics(items, characters, fout): global changed_commands characters = [c for c in characters if c.id < 14] changedict = {} loglist = [] hidden_commands = set(range(0, 0x1E)) - set(invalid_commands) for c in characters: hidden_commands = hidden_commands - set(c.battle_commands) if 0x1D in hidden_commands and random.randint(1, 3) != 3: hidden_commands.remove(0x1D) flags = [0x04, 0x08, 0x10, 0x20, 0x40] random.shuffle(flags) for flag in flags: if changedict: donebefore, doneafter = tuple(zip(*changedict.values())) donebefore, doneafter = set(donebefore), set(doneafter) else: donebefore, doneafter = set([]), set([]) while True: if flag == 0x08: candidates = set([0x0, 0x1, 0x2, 0x12]) else: candidates = range(0, 0x1E) candidates = set(candidates) - set([0x04, 0x14, 0x15, 0x19]) if random.randint(1, 5) != 5: candidates = candidates - donebefore candidates = sorted(candidates) before = random.choice(candidates) if before == 0: tempchars = [c for c in characters] else: tempchars = [c for c in characters if before in c.battle_commands] if not tempchars: continue unused = set(range(0, 0x1E)) - set(invalid_commands) if len(tempchars) <= 4: for t in tempchars: unused = unused - set(t.battle_commands) if flag == 0x08: unused = unused - changed_commands if set(hidden_commands) & set(unused): unused = set(hidden_commands) & set(unused) if before in unused: unused.remove(before) if random.randint(1, 5) != 5: unused = unused - doneafter if not unused: continue after = random.choice(sorted(unused)) if after in hidden_commands: hidden_commands.remove(after) for ptrdict in [sperelic, sperelic2]: beforeptr, afterptr = ptrdict[flag] fout.seek(beforeptr) fout.write(chr(before)) fout.seek(afterptr) fout.write(chr(after)) break changedict[flag] = (before, after) for item in items: if (item.is_consumable or item.is_tool or not item.features['special1'] & 0x7C): continue if item.itemid == 0x67: continue item.equippable &= IMP_MASK item.equippable |= 1 << 12 # gogo for flag in [0x04, 0x08, 0x10, 0x20, 0x40]: if flag & item.features['special1']: before, after = changedict[flag] tempchars = [c for c in characters if before in c.battle_commands] for t in tempchars: item.equippable |= (1 << t.id) item.write_stats(fout) loglist.append((item.name, before, after)) return loglist
def __init__(self, spells): self.spells = spells self.spellsubs = [] self.random_order = random.choice([True, False, False]) for s in self.spells: self.spellsubs.append(SpellSub(spellid=s.spellid))
def interconnect(self): links = [] for segment in self.intersegments: segment.interconnect() for i, (a, b) in enumerate(zip(self.clusters, self.clusters[1:])): aid = self.entids[i] bid = self.entids[i+1] if a.singleton: acands = a.entrances elif i == 0: acands = [e for e in a.entrances if e.entid == aid] else: acands = [e for e in a.entrances if e.entid != aid] aent = random.choice(acands) bcands = [e for e in b.entrances if e.entid == bid] bent = bcands[0] inter = self.intersegments[i] if a.singleton: previnter = self.intersegments[i-1] if i > 0 else None thresh = 3 for j in xrange(thresh): k = thresh-j intercands = [] excands = inter.get_external_candidates(num=k, test=True) if excands: intercands.append(inter) k = max(1, k-1) if previnter is not None: excands = previnter.get_external_candidates(num=k, test=True) if excands: intercands.append(previnter) if intercands: break else: raise Exception("No available intersegments.") chosen = random.choice(intercands) excands = (chosen.get_external_candidates(num=1)) if excands is None: raise Exception("Routing error.") links.append((aent, excands[0])) a.entering, a.exiting = True, True if previnter and not previnter.empty: # TODO: Sometimes this fails for j in range(i, len(self.intersegments)): nextinter = self.intersegments[j] if nextinter.empty: continue c = previnter.get_external_candidates(num=1)[0] d = nextinter.get_external_candidates(num=1)[0] links.append((c, d)) break else: raise Exception("No exit segment available.") elif not inter.empty: if not b.singleton: excands = inter.get_external_candidates(num=2) if excands is None: raise Exception("No exit segment available. (2)") random.shuffle(excands) links.append((bent, excands[1])) b.entering = True else: excands = inter.get_external_candidates(num=1) links.append((aent, excands[0])) a.exiting = True elif (inter.empty and not b.singleton): links.append((aent, bent)) a.exiting = True b.entering = True elif (inter.empty and b.singleton): inter2 = self.intersegments[i+1] assert not inter2.empty excands = inter2.get_external_candidates(num=1) links.append((aent, excands[0])) a.exiting = True else: import pdb; pdb.set_trace() assert False for i, a in enumerate(self.clusters): aid = self.entids[i] if not (a.entering or i == 0): if a.singleton: aent = a.entrances[0] else: acands = [e for e in a.entrances if e.entid == aid] aent = acands[0] while i > 0: inter = self.intersegments[i-1] if not inter.empty: break i += -1 if inter.empty: raise Exception("Routing error.") excands = inter.get_external_candidates(num=1) links.append((aent, excands[0])) a.entering = True self.links = links self.check_links()
def intify(value): subintify = lambda x: int(x, 0x10) if ',' in value: return random.choice(map(subintify, value.split(','))) else: return subintify(value)
def parse_checkpoints(): if ANCIENT: checkpoints = ANCIENT_CHECKPOINTS_TABLE else: checkpoints = TOWER_CHECKPOINTS_TABLE def ent_text_to_ints(room, single=False): locid, entids = room.split(':') locid = int(locid) if '|' in entids: entids = entids.split('|') elif ',' in entids: entids = entids.split(',') elif '>' in entids: entids = entids.split('>')[:1] else: entids = [entids] entids = map(int, entids) if single: assert len(entids) == 1 entids = entids[0] return locid, entids done, fixed, remove, oneway = [], [], [], [] routes = [list([]) for _ in xrange(3)] for line in open(checkpoints): line = line.strip() if not line or line[0] == '#': continue if line[0] == 'R': rank = int(line[1:]) for route in routes: route[-1].append(("R", rank)) elif line[0] == '&': locid, entids = ent_text_to_ints(line[1:]) for e in entids: fixed.append((locid, e)) elif line[0] == '-': locid, entids = ent_text_to_ints(line[1:]) for e in entids: remove.append((locid, e)) elif '>>' in line: line = line.split('>>') line = [ent_text_to_ints(s, single=True) for s in line] first, second = tuple(line) oneway.append((first, second)) else: if line.startswith("!"): line = line.strip("!") for route in routes: route.append([]) elif line.startswith("$"): line = line.strip("$") for route in routes: subroute = route[-1] head, tail = subroute[0], subroute[1:] random.shuffle(tail) route[-1] = [head] + tail else: random.shuffle(routes) rooms = line.split(',') chosenrooms = [] for room in rooms: locid, entids = ent_text_to_ints(room) candidates = [(locid, entid) for entid in entids] candidates = [c for c in candidates if c not in done] chosen = random.choice(candidates) chosenrooms.append(chosen) done.append(chosen) for room, route in zip(chosenrooms, routes): route[-1].append(room) for first, second in oneway: done = False for route in routes: for subroute in route: if first in subroute: index = subroute.index(first) index = random.randint(1, index+1) subroute.insert(index, second) done = True if not done: raise Exception("Unknown oneway rule") for route in routes: for i in range(len(route)): route[i] = Segment(route[i]) for index in range(len(routes)): routes[index] = Route(routes[index]) FIXED_ENTRANCES.extend(fixed) REMOVE_ENTRANCES.extend(remove) return routes
def assign_maps(routes, nummaps=None): clusters = get_clusters() new_clusters = clusters for route in routes: for segment in route.segments: for cluster in segment.clusters: if cluster in new_clusters: new_clusters.remove(cluster) for c in new_clusters: c.remove_adjacent_entrances() similars = [] def is_too_similar(c): if c.locid in towerlocids: return False if len(c.entrances) == 1: return False loc = get_location(c.locid) layer1 = loc.layer1ptr palette = loc.palette_index entxys = set([(e.x, e.y) for e in c.entrances]) for l, p, xys in similars: if layer1 == l and palette == p: if xys & entxys: return True similars.append((layer1, palette, entxys)) return False # first phase - bare minimum max_new_maps = nummaps best_clusters = [c for c in new_clusters if len(c.entrances) >= 3] while True: random.shuffle(best_clusters) done_maps, done_clusters = set([]), set([]) for cluster in best_clusters: location = get_location(cluster.locid) if (cluster.locid not in towerlocids and len(location.chests) == 0 and random.choice([True, False])): continue if cluster.locid in done_maps: continue chosen = None for route in routes: for segment in route.segments: for inter in segment.intersegments: if chosen is None or chosen.need < inter.need: chosen = inter if chosen.need > 0: if is_too_similar(cluster): continue chosen.add_cluster(cluster, need=True) done_maps.add(cluster.locid) done_clusters.add(cluster.clusterid) if len(done_maps) <= max_new_maps: break else: for route in routes: for segment in route.segments: segment.intersegments = [InterSegment() for _ in segment.intersegments] # second phase -supplementary random.shuffle(new_clusters) for cluster in new_clusters: CAPACITY_RATIO = len(done_maps) / float(max_new_maps) if cluster.clusterid in done_clusters: continue if cluster.locid in done_maps and not ANCIENT: continue if cluster.locid not in towerlocids: if (cluster.locid not in done_maps and len(done_maps) >= max_new_maps): continue if (cluster.locid in done_maps and len(done_maps) >= max_new_maps and get_location(cluster.locid).longentrances): continue rank = None if cluster.locid in done_maps or cluster.locid in towerlocids: for route in routes: for segment in route.segments: for c1 in segment.clusters: if c1.locid == cluster.locid: temp = route.segments.index(segment) if rank is None: rank = temp else: rank = min(rank, temp) for inter in segment.intersegments: for c2 in inter.clusters: if c2.locid == cluster.locid: temp = route.segments.index(segment) if rank is None: rank = temp else: rank = min(rank, temp) location = get_location(cluster.locid) if (cluster.locid not in towerlocids and CAPACITY_RATIO > 0.2 and len(cluster.entrances) <= 2 and len(location.chests) == 0 and random.choice([True, False])): continue if len(cluster.entrances) == 1: candidates = [] for route in routes: for (i, segment) in enumerate(route.segments): if rank is not None and i != rank: continue for inter in segment.intersegments: if inter.need < 0: candidates.append(inter) if candidates: if is_too_similar(cluster): continue chosen = random.choice(candidates) chosen.add_cluster(cluster, need=True) done_maps.add(cluster.locid) done_clusters.add(cluster.clusterid) elif len(cluster.entrances) >= 2: if cluster.locid not in towerlocids: if (CAPACITY_RATIO > 0.5 and len(location.chests) == 0 and random.randint(1, 3) == 3): continue if is_too_similar(cluster): continue route = random.choice(routes) if rank is not None: segment = route.segments[rank] else: segment = random.choice(route.segments) chosen = random.choice(segment.intersegments) chosen.add_cluster(cluster, need=True) done_maps.add(cluster.locid) done_clusters.add(cluster.clusterid) for route in routes: for segment in route.segments: segment.interconnect()
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 izip_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() loc.unlock_chests(200, 1000) 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 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 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