def intershuffle(cls): super(MonsterObject, cls).intershuffle() valid = [m for m in cls.every if m.intershuffle_valid] hitspecs = [m.hit_special_defense & 0xFC for m in valid] random.shuffle(hitspecs) for hs, m in zip(hitspecs, valid): m.hit_special_defense = (m.hit_special_defense & 0x03) | hs
def mutate_all(cls): numgroups = len(cls.groups) reassignments = range(numgroups) random.shuffle(reassignments) reassignments = dict(zip(range(numgroups), reassignments)) for o in cls.every: o.groupindex = reassignments[o.groupindex] for n, group in sorted(cls.groups.items()): for i, o in enumerate(group): done = [o2.item for o2 in group[:i]] item = ItemObject.get(o.item) for _ in xrange(100): new_item = item.get_similar() if new_item.full_index not in done: break else: candidates = [i for i in ItemObject.every_buyable if i.full_index not in done] new_item = random.choice(candidates) o.item = new_item.full_index indices_sorted = sorted(group[:-1], key=lambda i: i.item) indices_sorted = [i.item for i in indices_sorted] for (a, b) in zip(group, indices_sorted): a.item = b
def mutate(self): MAX_ENEMIES = 6 if self.bosses or self.enemies_hidden or not self.enemies_present: return candidates = list(self.leaders) while len(candidates) < 3: base = random.choice(candidates) new = base.get_similar() if new not in candidates: candidates.append(new) assert len(set(candidates)) <= 3 num_enemies = random.randint(1, random.randint(3, MAX_ENEMIES)) num_enemies = max(num_enemies, len(self.leaders)) chosen_enemies = list(self.leaders) while len(chosen_enemies) < num_enemies: vram_total = sum([e.vram_value for e in chosen_enemies]) sub_candidates = candidates + chosen_enemies sub_candidates = [e for e in sub_candidates if vram_total + e.vram_value <= 64] if not sub_candidates: num_enemies = len(chosen_enemies) break chosen_enemies.append(random.choice(sub_candidates)) random.shuffle(chosen_enemies) def mutate_coordinate((x, y)): x = mutate_normal(x, minimum=self.lower_x, maximum=self.upper_x) y = mutate_normal(y, minimum=self.lower_y, maximum=self.upper_y) return (x, y) def get_distance((x1, y1), (x2, y2)): return ((x1-x2)**2 + (y1-y2)**2)**0.5
def full_randomize(cls): if hasattr(cls, "after_order"): for cls2 in cls.after_order: if not (hasattr(cls2, "randomized") and cls2.randomized): raise Exception("Randomize order violated.") for c in CharacterObject.every: c.known_spells = 0 spells = range(0x1b) spells.remove(7) # group hug random.shuffle(spells) supplemental = [0xFF] * 3 spells = spells + supplemental charspells = defaultdict(list) while spells: valid = [i for i in range(5) if len(charspells[i]) < 5 or (len(charspells[i]) < 6 and i != 1)] chosen = random.choice(valid) spell = spells.pop(0) if spell == 0xFF: valid = [s for s in range(0x1b) if s not in charspells[i] and s != 7] # group hug spell = random.choice(valid) charspells[chosen].append(spell) charspells[1].insert(random.randint(0, 5), 7) for l in LearnObject.every: l.spell = 0xFF for i in range(5): charlevels = sorted(random.sample(range(2, 20), 5)) spells = charspells[i] c = CharacterObject.get(i) c.known_spells |= (1 << spells[0]) for l, s in zip(charlevels, spells[1:]): l = LearnObject.get_by_character(i, l-2) l.spell = s cls.randomized = True
def mutate_all(cls): numgroups = len(cls.groups) reassignments = range(numgroups) random.shuffle(reassignments) reassignments = dict(zip(range(numgroups), reassignments)) for o in cls.every: o.groupindex = reassignments[o.groupindex] for n, group in sorted(cls.groups.items()): for i, o in enumerate(group): done = [o2.item for o2 in group[:i]] item = ItemObject.get(o.item) for _ in xrange(100): new_item = item.get_similar() if new_item.full_index not in done: break else: candidates = [ i for i in ItemObject.every_buyable if i.full_index not in done ] new_item = random.choice(candidates) o.item = new_item.full_index indices_sorted = sorted(group[:-1], key=lambda i: i.item) indices_sorted = [i.item for i in indices_sorted] for (a, b) in zip(group, indices_sorted): a.item = b
def randomize_rng(address=0xFE00): numbers = range(0x100) random.shuffle(numbers) f = open(get_outfile(), 'r+b') f.seek(address) f.write("".join([chr(n) for n in numbers])) f.close()
def randomize_crystal_shards(): if "y" not in get_flags(): return FREELANCER, MIME = 0x84fc3, 0x91baf def map_crystal_job(value): return (7-(value%8)) + ((value/8)*8) def map_job_crystal(value): crystals = [c for c in range(24) if map_crystal_job(c) == value] assert len(crystals) == 1 return crystals[0] values = [] # galuf has no mime sprite addrs = [a for a in CRYSTAL_ADDRS if a not in [FREELANCER, MIME]] assert len(addrs) == 20 f = open(get_outfile(), "r+b") start_candidates = [j.index for j in JobCommandObject.every if j.commands[0] == 5 and j.index < 20] start_choice = random.choice(start_candidates) f.seek(FREELANCER) f.write(chr(map_job_crystal(start_choice))) remaining = [j.index for j in JobCommandObject.every if j.index != 20 and j.index != start_choice] random.shuffle(remaining) assert len(addrs) == len(remaining) == 20 for addr, j in zip(addrs, remaining): f.seek(addr) f.write(chr(map_job_crystal(j))) f.close() return values
def mutate_all(self): brs = list(self.every) random.shuffle(brs) for o in brs: if hasattr(o, "mutated") and o.mutated: continue o.mutate() o.mutated = True
def randomize_rng(): filename = get_outfile() f = open(filename, "r+b") f.seek(0xFEC0) random_numbers = range(0x100) random.shuffle(random_numbers) f.write("".join(map(chr, random_numbers))) f.close()
def randomize_counts(self): if self.index <= 0xf: # boss formation enumerated_fcounts = list(enumerate(self.fcounts)) random.shuffle(enumerated_fcounts) aas = [self.enemy_indexes[2] == 0 and self.index > 0, False] for ((i, fcount), aa) in zip(enumerated_fcounts, aas): counts = fcount.old_data['counts'] candidates = [f for f in FormationCountObject.every if f.validate_boss(counts, allow_add=aa)] if not candidates: chosen = fcount else: chosen = random.choice(candidates) self.counts[i] = chosen.index if aa: old_monsters = [self.monsters[j] for (j, c) in enumerate(counts) if c > 0] avg_rank = (sum([m.rank for m in old_monsters]) / len(old_monsters)) avg_hp = (sum([m.hp for m in old_monsters]) / len(old_monsters)) candidates = [m for m in MonsterObject.ranked if m.intershuffle_valid and m.rank < avg_rank and m.hp < avg_hp] max_index = len(candidates) - 1 randval = random.random() randomness = (1-self.random_degree) if randomness > 0: randval = randval ** (1 / randomness) else: randval = 0 index = int(round((1-randval) * max_index)) chosen = candidates[index] self.enemy_indexes[2] = chosen.index return for (i, fcount) in enumerate(self.fcounts): counts = fcount.old_data['counts'] candidates = sorted( FormationCountObject.every, key=lambda fc: (fc.get_distance(counts), fc.signature)) max_index = len(candidates) - 1 randval = random.random() if self.random_degree > 0: randval = randval ** (1 / self.random_degree) else: randval = 0 assert 0 <= randval <= 1 index = int(round(randval * max_index)) chosen = candidates[index] self.counts[i] = chosen.index assert self.fcounts[i] is chosen
def mutate(self): if random.choice([True, False]): level = max([r for r in self.reqs if 1 <= r <= 8]) else: level = random.randint(1, 4) + random.randint(0, 4) newreqs = [level, random.randint(1, level)] if self.is_triple: newreqs.append(random.randint(1, level)) random.shuffle(newreqs) self.reqs[:len(newreqs)] = newreqs
def mutate_all(cls): battlefields = [cls.get(i) for i in range(20)] everything_else = [c for c in cls.every if c not in battlefields] random.shuffle(battlefields) random.shuffle(everything_else) for o in everything_else + battlefields: if hasattr(o, "mutated") and o.mutated: continue o.mutate() o.mutated = True
def shuffle(self): random.shuffle(self.common) random.shuffle(self.uncommon) full = self.common + self.uncommon + self.rare + self.super_rare full = shuffle_normal(full, random_degree=self.random_degree) self.common = full[:4] self.uncommon = full[4:6] self.rare = full[6:7] self.super_rare = full[7:] assert full == (self.common + self.uncommon + self.rare + self.super_rare)
def full_randomize(cls): super(ShopObject, cls).full_randomize() ShopObject.class_reseed("shops") types = ["Magic", "Weapons", "Armor", "Items"] if 'o' not in get_flags(): types.remove('Items') for pretty_shop_type in types: shops = [ s for s in ShopObject.ranked if s.rank > 0 and s.pretty_shop_type == pretty_shop_type ] itemranks = defaultdict(set) all_items = set([]) avg = 0 for n, s in enumerate(shops): items = [i for i in s.items if i] itemranks[n] |= set(items) all_items |= set(items) avg += len(items) magic_shop = pretty_shop_type == "Magic" if magic_shop: all_items = [SpellPriceObject.get(i) for i in all_items] else: all_items = [PriceObject.get(i) for i in all_items] all_items = sorted(all_items, key=lambda i: (i.rank, i.signature)) done_items = set([]) random.shuffle(shops) for s in shops: s.reseed("wares") shop_items = [i for i in s.items if i > 0] chosen_items = [] while len(chosen_items) < len(shop_items): base_index = random.choice(shop_items) candidates = [i for i in all_items if i not in done_items] if not candidates: candidates = list(all_items) candidates = [ c for c in candidates if c not in chosen_items ] price = SpellPriceObject.get( base_index) if magic_shop else PriceObject.get( base_index) chosen = price.get_similar(candidates, override_outsider=True) chosen_items.append(chosen) assert len(chosen_items) == len(set(chosen_items)) chosen_items = sorted(chosen_items, reverse=True, key=lambda i: i.price) s.items = [c.index for c in chosen_items]
def full_randomize(cls): super(JobAbilityObject, cls).full_randomize() new_groups = defaultdict(list) jaos = list(JobAbilityObject.every) group_indexes = sorted(set([jao.groupindex for jao in jaos])) random.shuffle(jaos) while jaos: valid_groups = [i for i in group_indexes if not new_groups[i]] if not valid_groups: valid_groups = [i for i in group_indexes if len(new_groups[i]) < 7] index = random.choice(valid_groups) jao = jaos.pop() jao.groupindex = index new_groups[index].append(jao)
def randomize_all(cls): super(ShopItemObject, cls).randomize_all() ShopItemObject.class_reseed('brands') ShopItemObject.randomize_brands() ShopItemObject.class_reseed('shops') to_assign = list(ShopItemObject.every) random.shuffle(to_assign) shops = defaultdict(list) for sio in to_assign: sio.reseed('shop') candidates = [ c for c in ShopItemObject.every if len(shops[c.old_data['shop_index']]) <= MAX_SHOP_INVENTORY] if sio.get_brand() is None: temp = [s2 for s2 in candidates if s2.get_brand() is None and type(sio.item) is type(s2.old_item)] else: temp = [ s2 for s2 in candidates if s2.get_brand(old=True) is not None and s2.get_brand(old=True) == sio.get_brand()] candidates = temp if temp else candidates if shops: temp = [c for c in candidates if sio.item not in shops[c.old_data['shop_index']]] candidates = temp if temp else candidates max_shop = max(shops, key=lambda s: len(shops[s])) max_shop_size = len(shops[max_shop]) if max_shop_size > 0: max_shops = [s for s in shops if len(shops[s]) >= max_shop_size] temp = [c for c in candidates if c.old_data['shop_index'] not in max_shops] candidates = temp if temp else candidates chosen = random.choice(candidates) sio.shop_index = chosen.old_data['shop_index'] shops[sio.shop_index].append(sio.item) assert len(shops[sio.shop_index]) <= MAX_SHOP_INVENTORY
def intershuffle(cls): white_shops = [s for s in cls.ranked if s.shop_type == 5] black_shops = [s for s in cls.ranked if s.shop_type == 6] for shops in [white_shops, black_shops]: wares = [] for s in shops: s.reseed(salt="spell_shuffle") shop_wares = s.wares_indexes random.shuffle(shop_wares) wares.extend(shop_wares) wares = shuffle_normal(wares, random_degree=cls.random_degree) for s in shops: assert len(s.wares_indexes) <= len(wares) s.wares_indexes = wares[:len(s.wares_indexes)] wares = wares[len(s.wares_indexes):] assert len(wares) == 0
def shuffle(self): spells = [s for s in self.spell_queue if s != 0xFF] if spells: length = random.randint(len(set(spells)), 8) temp = sorted(set(spells)) while len(temp) < length: temp.append(random.choice(spells)) random.shuffle(temp) self.spell_queue = temp skills = [s for s in self.skill_queue if s != 0xFF] if skills: length = random.randint(len(set(skills)), 4) temp = sorted(set(skills)) while len(temp) < length: temp.append(random.choice(skills)) random.shuffle(temp) self.skill_queue = temp
def randomize_all(cls): super(MonsterMeatObject, cls).randomize_all() random_degree = MonsterMeatObject.random_degree families = list(range(12)) random.shuffle(families) meat_map = {} for i, f in enumerate(families): meat_classes = [0, 1, 2] if random.random() < (random_degree ** 0.5): random.shuffle(meat_classes) checks = ['a', 'b'] random.shuffle(checks) for check in checks: if check == 'a' and (meat_classes[0] == max(meat_classes) and random.random() > random_degree): meat_classes[:2] == list(reversed(meat_classes[:2])) if check == 'b' and (meat_classes[-1] == min(meat_classes) and random.random() > random_degree): meat_classes[-2:] == list(reversed(meat_classes[-2:])) assert len(set(meat_classes)) == 3 for j, c in enumerate(meat_classes): old_value = (f << 4) | c new_value = (i << 4) | j meat_map[old_value] = new_value assert sorted(meat_map.keys()) == sorted(meat_map.values()) for mmo in MonsterMeatObject.every: if (mmo.meat in meat_map and MonsterObject.get(mmo.index).is_monster): mmo.meat = meat_map[mmo.meat]
def mutate(self): consumables = set(range(16, 47)) weapons = set(range(58, 112)) weapons |= set([192, 193, 194]) armors = set(range(49, 58)) | set(range(112, 152)) armors.add(195) tomes = set(range(152, 192)) for itemset in [consumables, weapons, armors, tomes]: if set(self.wares) <= itemset: chosen = itemset break else: chosen = consumables | weapons | armors | tomes wares = list(self.wares) random.shuffle(wares) new_wares = [] for w in wares: while True: new = ff2_get_similar_item(w, chosen, random_degree=self.random_degree) if new not in new_wares: new_wares.append(new) break assert len(new_wares) == len(wares) self.wares = sorted(new_wares) new_prices = [] for w in self.wares: prices = sorted(FF2PriceObject.every, key=lambda po: abs(po.price - ff2_get_price(w))) chosen = prices[0] temp = chosen.get_similar() if temp.price >= (ff2_get_price(w) / 2): chosen = temp new_prices.append(chosen.index) assert len(new_prices) == len(self.prices) self.prices = new_prices
def shuffle_char_hues(hues): while True: tryagain = False random.shuffle(hues) for c in range( 0, 18, 3 ): #check for too close colors vertically (within one palette) chunk = hues[c:c + 3] chunk += [h + 360 for h in chunk] for i in range(6): for j in range(i + 1, 6): if abs(chunk[i] - chunk[j]) <= 16: tryagain = True break if tryagain: break if tryagain: break if tryagain: continue for c in range( 0, 3 ): #check for too close colors horizontally (same element on different palettes) chunk = hues[c:len(hues):3] chunk += [h + 360 for h in chunk] for i in range(12): for j in range(i + 1, 12): if abs(chunk[i] - chunk[j]) <= 16: tryagain = True break if tryagain: break if tryagain: break if tryagain: continue break return list(map(hue_rgb, hues))
def mutate(self): if self.shop_type >= 5: return if self.index == 0x26: assert self.shop_type == 4 assert len(self.wares) == 1 return if self.rank < 0: return new_wares = [] candidates = [ c for c in self.valid_wares if c.old_data["buy_price"] > 2 ] wares = self.wares random.shuffle(wares) for w in wares: while True: nw = w.get_similar(candidates, random_degree=self.random_degree) if nw in new_wares: nw = random.choice(self.valid_wares) if nw not in new_wares: break if nw.rank >= 65536: price = max(30000, nw.buy_price, nw.sell_price * 2) if price < 50000: price *= 2 price = min(price, 99999) price = mutate_normal(price, 0, 99999, wide=True, random_degree=nw.random_degree) nw.buy_price = price new_wares.append(nw) self.wares_indexes = sorted([nw.index for nw in new_wares])
def mutate_all(self): chests = list(self.every) random.shuffle(chests) random.shuffle(self.desirable_left) random.shuffle(self.undesirable_left) for o in chests: if hasattr(o, "mutated") and o.mutated: continue o.mutate() o.mutated = True
def choose_item(self, aggression=3): if hasattr(self, "item_set_in_progress") and self.item_set_in_progress: chosen = random.choice(self.item_set_in_progress) self.item_set_in_progress.remove(chosen) return chosen requirements = sorted([ r for r in self.ranked_requirements if r not in self.assigned_items and r not in self.custom_assignments.values() ]) unlocked = {} for r in requirements: unlocked[r] = self.get_item_unlocked_locations(r) candidates = [r for r in requirements if len(unlocked[r]) > 0] unused = [r for r in requirements if r not in candidates] unused_unlocked = self.get_item_unlocked_locations(unused) if unused_unlocked: random.shuffle(unused) for u in list(unused): if u not in unused: continue unused.remove(u) temp = self.get_item_unlocked_locations(unused) if temp: failure = False for key in unlocked: if (set(unlocked[key]) >= set(temp) or (unlocked[key] and set(unlocked[key]) <= set(temp))): failure = True break if failure: unused = [] break if not temp: unused.append(u) if unused: key = tuple(sorted(unused)) unlocked[key] = self.get_item_unlocked_locations(key) candidates.append(key) if not candidates: return None chosen = random.choice(candidates) if len(unlocked[chosen]) > 0: candidates = [ r for r in requirements if unlocked[r] and unlocked[r] < unlocked[chosen] and r not in self.assigned_items ] if len(candidates) > 0: while True: candidates = [ c for c in candidates if unlocked[c] and unlocked[c] < unlocked[chosen] and c not in self.assigned_items ] if not candidates: break c = random.choice(candidates) if (chosen in self.preferences and c in self.preferences[chosen]): candidates.remove(c) continue ratio = len(unlocked[c]) / float(len(unlocked[chosen])) ratio = ratio**aggression if random.random() > ratio: chosen = c candidates.remove(c) if not isinstance(chosen, basestring): self.item_set_in_progress = sorted(chosen) return self.choose_item(aggression=aggression) return chosen
def get_palette_transformer(use_luma=False, always=None, middle=True, basepalette=None): def get_ratio(a, b): if a > 0 and b > 0: return max(a, b) / float(min(a, b)) if abs(a - b) <= 1: return 1.0 return 9999 def color_to_components(color): blue = (color & 0x7c00) >> 10 green = (color & 0x03e0) >> 5 red = color & 0x001f return (red, green, blue) def components_to_color(xxx_todo_changeme): (red, green, blue) = xxx_todo_changeme return red | (green << 5) | (blue << 10) if always is not None and basepalette is not None: raise Exception("'always' argument incompatible with 'basepalette'") swapmap = {} if basepalette is not None and not use_luma: threshold = 1.2 def color_to_index(color): red, green, blue = color_to_components(color) a = red >= green b = red >= blue c = green >= blue d = get_ratio(red, green) >= threshold e = get_ratio(red, blue) >= threshold f = get_ratio(green, blue) >= threshold index = (d << 2) | (e << 1) | f index |= ((a and not d) << 5) index |= ((b and not e) << 4) index |= ((c and not f) << 3) return index colordict = defaultdict(set) for color in basepalette: index = color_to_index(color) colordict[index].add(color) saturated = dict((k, v) for (k, v) in colordict.items() if k & 0x7) satlist = sorted(saturated) random.shuffle(satlist) grouporder = sorted(satlist, key=lambda k: len(saturated[k]), reverse=True) if grouporder: dominant = grouporder[0] domhue, domsat = dominant >> 3, dominant & 0x7 for key in grouporder[1:]: colhue, colsat = key >> 3, key & 0x7 if (domhue ^ colhue) & (domsat | colsat) == 0: continue secondary = key break else: secondary = dominant sechue, secsat = secondary >> 3, secondary & 0x7 else: dominant, domhue, domsat = 0, 0, 0 secondary, sechue, secsat = 0, 0, 0 while True: domswap = random.randint(0, 7) secswap = random.randint(0, 7) tertswap = random.randint(0, 7) if domswap == secswap: continue break for key in colordict: colhue, colsat = key >> 3, key & 0x7 if ((domhue ^ colhue) & (domsat | colsat)) == 0: if ((sechue ^ colhue) & (secsat | colsat)) == 0: swapmap[key] = random.choice([domswap, secswap]) else: swapmap[key] = domswap elif ((sechue ^ colhue) & (secsat | colsat)) == 0: swapmap[key] = secswap elif ((domhue ^ colhue) & domsat) == 0: if ((sechue ^ colhue) & secsat) == 0: swapmap[key] = random.choice([domswap, secswap]) else: swapmap[key] = domswap elif ((sechue ^ colhue) & secsat) == 0: swapmap[key] = secswap elif ((domhue ^ colhue) & colsat) == 0: if ((sechue ^ colhue) & colsat) == 0: swapmap[key] = random.choice([domswap, secswap]) else: swapmap[key] = domswap elif ((sechue ^ colhue) & colsat) == 0: swapmap[key] = secswap else: swapmap[key] = tertswap elif basepalette is not None and use_luma: def color_to_index(color): red, green, blue = color_to_components(color) index = red + green + blue return index values = [] for color in basepalette: index = color_to_index(color) values.append(index) values = sorted(values) low, high = min(values), max(values) median = values[len(values) // 2] clusters = [set([low]), set([high])] done = set([low, high]) if median not in done and random.choice([True, False]): clusters.append(set([median])) done.add(median) to_cluster = sorted(basepalette) random.shuffle(to_cluster) for color in to_cluster: index = color_to_index(color) if index in done: continue done.add(index) def cluster_distance(cluster): distances = [abs(index - i) for i in cluster] return sum(distances) // len(distances) nearest = min(cluster, key=lambda x: abs(x - index)) return abs(nearest - index) chosen = min(clusters, key=cluster_distance) chosen.add(index) swapmap = {} for cluster in clusters: swapcode = random.randint(0, 7) for index in cluster: try: assert index not in swapmap except: import pdb pdb.set_trace() swapmap[index] = swapcode remaining = [i for i in range(94) if i not in swapmap.keys()] random.shuffle(remaining) def get_nearest_swapcode(index): nearest = min(swapmap, key=lambda x: abs(x - index)) return nearest for i in remaining: nearest = get_nearest_swapcode(i) swapmap[i] = swapmap[nearest] else: def color_to_index(unused_color): return 0 if always: swapmap[0] = random.randint(1, 7) else: swapmap[0] = random.randint(0, 7) for key in swapmap: swapmap[key] = generate_swapfunc(swapmap[key]) if middle: degree = utran.randint(-75, 75) def palette_transformer(raw_palette, single_bytes=False): if single_bytes: raw_palette = list(zip(raw_palette, raw_palette[1:])) raw_palette = [p for (i, p) in enumerate(raw_palette) if not i % 2] raw_palette = [(b << 8) | a for (a, b) in raw_palette] transformed = [] for color in raw_palette: index = color_to_index(color) swapfunc = swapmap[index] red, green, blue = color_to_components(color) red, green, blue = swapfunc((red, green, blue)) if middle: red, green, blue = shift_middle((red, green, blue), degree) color = components_to_color((red, green, blue)) transformed.append(color) if single_bytes: major = [p >> 8 for p in transformed] minor = [p & 0xFF for p in transformed] transformed = [] for a, b in zip(minor, major): transformed.append(a) transformed.append(b) return transformed return palette_transformer
def shuffle_key_values(d): keys = list(d.keys()) random.shuffle(keys) shuffled = dict(zip(keys, d.values())) d.update(shuffled)
def full_randomize(cls): # fix debug bombs before this if hasattr(cls, "after_order"): for cls2 in cls.after_order: if not (hasattr(cls2, "randomized") and cls2.randomized): raise Exception("Randomize order violated.") cls.randomized = True assignments = {} # phase 1: frog coin shops frog_candidates = [i for i in ItemObject.every if i.price and (i.rare or i.is_consumable or i.is_accessory) and not i.banned] frog_not_rare = [i for i in frog_candidates if not i.rare] unfrog = random.randint( random.randint(0, len(frog_not_rare)), len(frog_not_rare)) unfrog = random.sample(frog_not_rare, unfrog) for i in sorted(unfrog, key=lambda i2: i2.index): frog_candidates.remove(i) frog_chosen = random.sample(frog_candidates, 25) disciple_shop = 3 frog_coin_emporium = 6 one_only = [i for i in frog_chosen if (i.is_equipment and bin(i.equippable).count("1") == 1) or (i.is_consumable and i.reuseable)] num_choose = min(10, len(one_only)) num_choose = random.randint(random.randint(0, num_choose), num_choose) num_choose = min(num_choose, len(one_only)) chosen = random.sample(one_only, num_choose) choose_again = [i for i in frog_chosen if i not in chosen and ( i in one_only or i.is_equipment)] num_choose = 10 - len(chosen) num_choose = random.randint(random.randint(0, num_choose), num_choose) num_choose = min(num_choose, len(choose_again)) if num_choose and choose_again: chosen += random.sample(choose_again, num_choose) num_choose = 10 - len(chosen) if num_choose: choose_again = [i for i in frog_chosen if i not in chosen] random.shuffle(choose_again) chosen += choose_again[:num_choose] assert len(chosen) == 10 assignments[disciple_shop] = chosen assignments[frog_coin_emporium] = [ i for i in frog_chosen if i not in chosen] # phase 2: non-frog coin shops carryover = [i for i in frog_candidates if i not in assignments[disciple_shop] and i not in assignments[frog_coin_emporium]] random.shuffle(carryover) num_choose = random.randint(0, random.randint(0, len(carryover))) carryover = carryover[:num_choose] shop_items = carryover + [i for i in ItemObject.every if i not in assignments[disciple_shop] and i not in assignments[frog_coin_emporium] and not i.banned and not i.rare] shop_items = sorted(set(shop_items), key=lambda i: i.rank) juice_bar_partial = [9, 10, 11] # full: 12 special_conditions = { 0: lambda i: i.is_consumable or ( i.is_equipment and i.equippable & 0b10001), 1: lambda i: i.is_consumable, 2: lambda i: i.is_equipment and i.equippable & 0b11001, 4: lambda i: i.is_consumable or ( i.is_equipment and i.equippable & 0b11001), 8: lambda i: i.is_consumable and ( (i.misc_attack not in [1, 2, 4, 5] and not i.get_bit("status_nullification")) or i.get_bit("all")), 12: lambda i: special_conditions[8](i) and not i.reuseable, 13: lambda i: i.is_weapon, 14: lambda i: i.is_armor, 15: lambda i: i.is_accessory, 16: lambda i: i.is_consumable, 18: lambda i: i.is_consumable, 19: lambda i: i.is_equipment, 20: lambda i: special_conditions[8](i), 24: lambda i: i.is_consumable, } done_already = set([]) for p in range(25): if p in juice_bar_partial + [disciple_shop, frog_coin_emporium]: continue shop = ShopObject.get(p) if p == 12: num_items = 15 else: num_items = len([i for i in shop.items if i != 0xFF]) num_items = mutate_normal(num_items, minimum=1, maximum=15) valid_items = list(shop_items) if p in special_conditions: valid_items = [i for i in valid_items if special_conditions[p](i)] temp = [i for i in valid_items if i not in done_already or (i.is_consumable and not i.reuseable and not i.rare and not i.get_bit("all") and (i.misc_attack in [1, 2, 4] or i.get_bit("status_nullification")))] if temp and p not in [12, 13, 14, 20]: valid_items = temp extras = [i for i in valid_items if i not in temp] extras = random.sample(extras, random.randint(0, len(extras))) valid_items = sorted(set(valid_items + extras), key=lambda i: i.rank) assert valid_items num_items = min(num_items, len(valid_items)) if p != 20 and len(valid_items) > num_items: valid_items = valid_items[:random.randint( num_items, random.randint(num_items, len(valid_items)))] consumables = [i for i in valid_items if i.is_consumable] others = [i for i in valid_items if i not in consumables] if consumables and others and num_items >= 4: num_con = (random.randint(0, num_items) + random.randint(0, num_items)) / 2 num_con = max(num_con, num_items-num_con) num_con = min(num_con, num_items-2) num_oth = num_items-num_con num_con = min(num_con, len(consumables)) num_oth = min(num_oth, len(others)) valid_items = (random.sample(consumables, num_con) + random.sample(others, num_oth)) num_items = num_con + num_oth chosen_items = random.sample(valid_items, num_items) assignments[p] = chosen_items for i in chosen_items: done_already.add(i) # phase 2.5: juice bar for n, p in enumerate(sorted(juice_bar_partial, reverse=True)): n = len(juice_bar_partial) - n previous_items = assignments[p+1] minimum = n maximum = len(previous_items)-1 average = (minimum + maximum) / 2 num_items = random.randint( random.randint(minimum, average), maximum) chosen_items = random.sample(previous_items, num_items) assignments[p] = chosen_items # phase 3: repricing repriced = set([]) for p, items in assignments.items(): for item in items: if item in repriced: continue if p in [disciple_shop, frog_coin_emporium]: item.become_frog_coin_item() else: if not item.unbecome_frog_coin_item() and item.rare: PriceObject.get(item.index).price = max( item.price, int(item.rank)) price = min(999, max(2, item.price)) price = mutate_normal(price, minimum=2, maximum=999) PriceObject.get(item.index).price = price repriced.add(item) for p, items in assignments.items(): final = [0xFF] * 15 items = sorted(items, key=lambda i: ITEM_ORDER.index(i.index)) final[:len(items)] = [i.index for i in items] ShopObject.get(p).items = final ShopObject.get(20).set_bit("discount50", True)
def full_randomize(cls): if hasattr(cls, "after_order"): for cls2 in cls.after_order: if not (hasattr(cls2, "randomized") and cls2.randomized): raise Exception("Randomize order violated.") cls.randomized = True curves = defaultdict(list) for character_index in range(5): c = CharacterObject.get(character_index) for attr in LEVEL_STATS: value = getattr(c, attr) for l in cls.every: if l.character_id == c.index and l.level <= 20: value += getattr(l, attr) value = mutate_normal(value, maximum=255) fixed_points = [(1, 0), (20, value)] for _ in xrange(3): dex = random.randint(1, len(fixed_points)-1) lower_level, lower_value = fixed_points[dex-1] upper_level, upper_value = fixed_points[dex] if upper_level - lower_level < 4: continue level_interval = (upper_level - lower_level) / 2 value_interval = (upper_value - lower_value) / 2 level = (lower_level + random.randint(0, level_interval) + random.randint(0, level_interval)) if level <= lower_level or level >= upper_level: continue value = (lower_value + random.randint(0, value_interval) + random.randint(0, value_interval)) fixed_points.append((level, value)) fixed_points = sorted(fixed_points) for ((l1, v1), (l2, v2)) in zip(fixed_points, fixed_points[1:]): ldist = l2 - l1 vdist = v2 - v1 for l in range(l1+1, l2): factor = (l - l1) / float(ldist) v = v1 + (factor * vdist) fixed_points.append((l, int(round(v)))) fixed_points = sorted(fixed_points) levels, values = zip(*fixed_points) assert len(fixed_points) == 20 assert levels == tuple(sorted(levels)) assert values == tuple(sorted(values)) increases = [] for v1, v2 in zip(values, values[1:]): increases.append(v2-v1) frontload_factor = random.random() * random.random() if attr in ["defense", "magic_defense"]: frontload_factor *= random.random() frontloaded = 0 for n, inc in enumerate(increases): max_index = len(increases) - 1 factor = (((max_index-n) / float(max_index)) * frontload_factor) amount = int(round(inc * factor)) frontloaded += amount increases[n] = (inc - amount) frontloaded = max(frontloaded, 1) while max(increases) > 15: i = increases.index(max(increases)) increases[i] = increases[i] - 1 choices = [n for (n, v) in enumerate(increases) if v < 15] if random.randint(0, len(choices)) == 0: frontloaded += 1 elif choices: i = random.choice(choices) increases[i] = increases[i] + 1 curves[attr].append((frontloaded, increases)) for attr in LEVEL_STATS: attr_curves = curves[attr] random.shuffle(attr_curves) for character_index in xrange(5): (base, increases) = attr_curves.pop() c = CharacterObject.get(character_index) if c.index == 0 and attr in ["max_hp", "attack"]: # ensure basic starting stats for Mario while base < 20: base += 1 for i in range(len(increases)): if increases[i] > 0: increases[i] = increases[i] - 1 break getattr(c, attr) setattr(c, attr, base) assert len(increases) == 19 for s in StatGrowthObject.every: if s.character_id == c.index: if increases: s.set_stat(attr, increases.pop(0)) else: s.set_stat(attr, mutate_normal(2))
] kicker = [ m for m in MonsterObject.every if m.soul_type == 3 and m.soul == 4 ] replaceable = kicker + winged headhunter = MonsterObject.get(0x6a) legion = MonsterObject.get(0x6c) balore = MonsterObject.get(0x6d) bosses = [headhunter, legion, balore] if hard_mode: banned = [(8, 0x05), (6, 0x02)] else: banned = [] random.shuffle(bosses) for boss in bosses: if boss.signature in assigned_locations: continue if boss is legion: locations = [addresses.legion1, addresses.legion2] souls = set([(8, 0x03), (8, 0x05), (6, 0x02)]) elif boss is balore: locations = [ addresses.balore1, addresses.balore2, addresses.balore3 ] souls = set([(8, 0x03), (8, 0x05), (6, 0x03), (6, 0x02)]) elif boss is headhunter: locations = [ addresses.headhunter1, addresses.headhunter4, addresses.headhunter5
def randomize_skills_and_attributes(self): if self.index in MonsterObject.banned_monster_indexes: return old_attributes = [AttributeObject.get(i) for i in self.attribute_indexes] new_attributes = [o for o in old_attributes if o.rank < 0 and o.index != 0xFF] old_attributes = [o for o in old_attributes if o.rank >= 0] num_attributes = len({a for a in new_attributes + old_attributes}) num_attributes = mutate_normal(num_attributes, minimum=1, maximum=8, random_degree=self.random_degree, wide=True) if not old_attributes: return while len(new_attributes) < num_attributes: if len([a for a in new_attributes if a.get_bit('use_battle')]) == 0: candidates = [a for a in self.new_family_attributes if a.get_bit('use_battle')] elif len([a for a in new_attributes if a.get_bit('use_battle')]) == 7: candidates = [a for a in self.new_family_attributes if not a.get_bit('use_battle')] else: candidates = self.new_family_attributes candidates = [c for c in candidates if c not in new_attributes] old_attribute = random.choice(old_attributes) if not candidates: if (old_attribute in new_attributes and len(new_attributes) >= self.num_attributes): break new_attribute = old_attribute else: new_attribute = old_attribute.get_similar( candidates, random_degree=self.random_degree, override_outsider=True) if new_attribute not in new_attributes: new_attributes.append(new_attribute) if not new_attributes: return use_battle = [a for a in new_attributes if a.get_bit('use_battle')] if len(use_battle) >= 8: use_battle = use_battle[:7] random.shuffle(use_battle) no_use = [a for a in new_attributes if not a.get_bit('use_battle')] no_use = sorted(no_use) candidates = [m for m in MoveSelectionObject.every if m.num_moves == len(use_battle)] assert 1 <= len(use_battle) <= 7 chosen = random.choice(candidates) MonsterLevelObject.get(self.index).set_move_selection_index( chosen.index) self.attribute_indexes = [a.index for a in use_battle + no_use]