def cleanup(self): growth = CharGrowthObject.get(self.index) for attr in ["power", "stamina", "magic", "hit", "evade", "mdef"]: baseattr = "%s_base" % attr increase = getattr(growth, attr) * (self.level-1) / 100 initial = getattr(self, baseattr) + increase setattr(self, attr, initial) hpgroup = HPGrowthObject.getgroup(self.index) mpgroup = MPGrowthObject.getgroup(self.index) max_hp = mutate_normal(85, minimum=1, maximum=999, random_degree=self.random_degree) max_mp = mutate_normal(9, minimum=1, maximum=99, random_degree=self.random_degree) temp_level = 1 while temp_level < self.level: temp_level += 1 hpo = [o for o in hpgroup if temp_level <= o.level][-1] mpo = [o for o in mpgroup if temp_level <= o.level][-1] max_hp += hpo.increase max_mp += mpo.increase max_hp = min(999, max(max_hp, 1)) max_mp = min(99, max(max_mp, 1)) self.max_hp, self.hp = max_hp, max_hp self.max_mp, self.mp = max_mp, max_mp if self.level > 1: self.xp = sum([e.experience for e in ExperienceObject if e.index < (self.level-1)]) else: self.xp = 0 self.xpnext = ExperienceObject.get(self.level-1).experience for attr in ["helmet", "armor"]: current = getattr(self, attr) if self.can_equip(current): continue candidates = [c for c in ItemObject.every_equippable if getattr(c, "is_%s" % attr) and self.can_equip(c) and c.buyable] if not candidates: candidates = [c for c in ItemObject.every_equippable if getattr(c, "is_%s" % attr) and c.buyable] chosen = min(candidates, key=lambda c: c.rank_price) chosen.make_equippable(self.index) setattr(self, attr, chosen.full_index) current = ItemObject.get(self.accessory) if not self.can_equip(current): candidates = [c for c in Accessory2Object.every if self.can_equip(c)] if candidates: chosen = random.choice(candidates) chosen.make_equippable(self.index) self.accessory = chosen.full_index
def mutate_stat_curve(cls, class_index, attr): lus = [ lu for lu in LevelUpObject.every if lu.class_index == class_index ] assert len(lus) == 99 lus = lus[:98] assert len(lus) == 98 lus[0].reseed(salt="fullmut" + attr) bits = [lu.get_bit(attr) for lu in lus] value = len([b for b in bits if b]) base_ratio = value / float(len(lus)) max_ratio = max([ cls.get_class_stat_score(i, attr) / float(len(lus)) for i in xrange(6) ]) assert max_ratio >= base_ratio base_ratio = mutate_normal(base_ratio, 0, max_ratio, wide=False, random_degree=LevelUpObject.random_degree, return_float=True) remaining = list(lus) while remaining: ratio = mutate_normal(base_ratio, 0, max_ratio, wide=False, random_degree=LevelUpObject.random_degree, return_float=True) max_index = len(remaining) - 1 divider = random.randint(0, max_index) aa = remaining[:divider] bb = remaining[divider:] if len(aa) > len(bb): aa, bb = bb, aa elif len(aa) == len(bb) and random.choice([True, False]): aa, bb = bb, aa if random.choice([True, True, False]): to_set, remaining = aa, bb else: to_set, remaining = bb, aa assert len(to_set + remaining) == max_index + 1 for lu in to_set: value = (random.random() < ratio) lu.set_bit(attr, value)
def randomize_all(cls): meats = {m.meat.old_data['meat'] for m in MonsterObject.every} for meat in sorted(meats): monsters = [m for m in MonsterObject.every if m.meat.old_data['meat'] == meat and m.index <= MonsterObject.MAX_EVOLVE_INDEX] if not monsters: assert meat >= 0xc0 continue assert len(monsters) == 5 assert monsters[-1].level == 0xb assert monsters == sorted(monsters, key=lambda m: m.level) new_levels = set([]) for m in monsters[:4]: new_level = mutate_normal( m.level, minimum=1, maximum=0xa, wide=True, random_degree=MonsterLevelObject.random_degree) new_levels.add(new_level) while len(new_levels) < 4: new_levels.add(random.randint(1, 0xa)) new_levels = sorted(new_levels) for level, monster in zip(new_levels, monsters): assert 1 <= level <= 0xa MonsterLevelObject.get(monster.index).set_level(level) super(MonsterLevelObject, cls).randomize_all()
def mutate(self): item = None if self.contents & 0x8000: value = (self.contents & 0x7FFF) << 1 elif self.contents & 0xFF00: return else: item = ItemObject.get(self.contents & 0xff) if item is None: return if item.rare: new_item = item.get_similar() self.contents = new_item.full_index return elif not item.buyable: return value = item.rank_price if random.randint(1, 20) == 20: value = min(65000, max(0, value)) value = mutate_normal(value, minimum=0, maximum=65000, random_degree=self.random_degree) self.contents = 0x8000 | (value >> 1) return if item is None: item = [i for i in ItemObject.every_buyable if i.rank_price <= value][-1] new_item = item.get_similar() self.contents = new_item.full_index
def randomize(self): num_items = len(self.items) num_items = mutate_normal(num_items, minimum=1, maximum=8, random_degree=self.random_degree, wide=True) old_items = list(self.items) new_items = [] for _ in range(500): if len(new_items) == num_items: break buyable_check = not (random.random() < self.random_degree) chosen_class = random.choice(old_items).shop_item_class chosen_price = random.choice(old_items) candidates = [a for a in AttributeObject.every if a.index <= 0x7f and a.rank >= 0 and a.shop_item_class == chosen_class and a.is_buyable >= buyable_check] candidates = [c for c in candidates if c not in new_items] if not candidates: continue chosen = chosen_price.get_similar( candidates=candidates, random_degree=self.random_degree, override_outsider=True) new_items.append(chosen) else: raise Exception('Unable to populate shop.') for i in new_items: if not i.is_buyable: ItemPriceObject.get(i.index).price = 60000 self.item_indexes = [i.index for i in new_items]
def mutate(self): item = None if self.contents & 0x8000: value = (self.contents & 0x7FFF) << 1 elif self.contents & 0xFF00: return else: item = ItemObject.get(self.contents & 0xff) if item is None: return if item.rare: new_item = item.get_similar() self.contents = new_item.full_index return elif not item.buyable: return value = item.rank_price if random.randint(1, 20) == 20: value = min(65000, max(0, value)) value = mutate_normal(value, minimum=0, maximum=65000, random_degree=self.random_degree) self.contents = 0x8000 | (value >> 1) return if item is None: item = [ i for i in ItemObject.every_buyable if i.rank_price <= value ][-1] new_item = item.get_similar() self.contents = new_item.full_index
def mutate_spell_levels(cls, class_index): lus = [ lu for lu in LevelUpObject.every if lu.class_index == class_index ] indexes = [ i for (i, lu) in enumerate(lus) if lu.get_bit("spell_level") ] if not indexes: return new_indexes = [] for i in indexes: while True: n = mutate_normal(i, 1, 40, random_degree=cls.random_degree) if n not in new_indexes: new_indexes.append(n) break if len(new_indexes) > 1: remaining = 8 - len(new_indexes) highest = max(new_indexes) new_indexes.extend(random.sample(range(highest + 1, 50), remaining)) assert len(new_indexes) == 8 assert len(new_indexes) in [1, 8] for i, lu in enumerate(lus): if i in new_indexes: lu.set_bit("spell_level", True) else: lu.set_bit("spell_level", False)
def mutate(self): if self.misc in [2, 4] and self.contents < 0x10: return partner = random.choice(FF2ChestObject.every) assert self.misc in [1, 2, 4] assert partner.misc in [1, 2, 4] if partner.misc == 1: if self.misc == 1: value = self.contents else: value = ff2_get_price(self.contents) self.misc = 1 value = min(65000, max(0, value)) self.contents = mutate_normal(value, 0, 65000, wide=True, random_degree=self.random_degree) else: if self.misc == 1: item = ff2_get_item_valued(self.contents) self.misc = 2 else: item = self.contents item = ff2_get_similar_item(item, random_degree=self.random_degree) self.contents = item assert self.misc in [1, 2, 4]
def get_similar_all_items(self): candidates = self.sorted_items_and_equipment candidates = [c for c in candidates if c.rank > 0] index = candidates.index(self) max_index = len(candidates) - 1 new_index = mutate_normal(index, 0, max_index, random_degree=self.random_degree) return candidates[new_index]
def mutate(self): super(DropObject, self).mutate() self.item = ItemObject.get(self.item).get_similar().full_index self.charm = ItemObject.get(self.charm).get_similar().full_index if self.monster.get_bit("bosslike"): self.xp = min(self.xp, self.old_data['xp']) self.xp = mutate_normal(self.xp, 0, self.xp, wide=True, random_degree=self.random_degree) else: self.xp = max(self.xp, self.old_data['xp']) self.gp = self.gp >> 1
def mutate(self): for (i, c) in enumerate(self.counts): subcounts = (c >> 4, c & 0xf) final_counts = [] for sc in subcounts: if sc >= 2: sc = mutate_normal(sc, 1, 9, wide=True, random_degree=self.random_degree) final_counts.append(sc) low, high = sorted(final_counts) self.counts[i] = (low << 4) | high
def get_similar(self): if self.rare: result = random.choice(self.every_rare) elif self.buyable: buyables = self.every_buyable index = buyables.index(self) index = mutate_normal(index, minimum=0, maximum=len(buyables)-1, random_degree=self.random_degree) result = buyables[index] else: result = self return result
def mutate(self): value = mutate_normal(self.price, minimum=0, maximum=65000, random_degree=self.random_degree) value = value * 2 power = 0 while value > 100: value /= 10 power += 1 value = (value * (10**power)) / 2 self.price = value if self.equippable and not self.is_weapon: self.equippable = random.randint(1, 127) << 1
def ff2_get_similar_item(item, candidates=None, random_degree=None): candidates = ff2_get_ranked_items(candidates) if random_degree is None: random_degree = FF2ChestObject.random_degree index = candidates.index(item) max_index = len(candidates) - 1 new_index = mutate_normal(index, 0, max_index, wide=True, random_degree=random_degree) return candidates[new_index]
def randomize(self): if not self.intershuffle_valid: return other = self.get_similar() power = mutate_normal(other.power, minimum=0, maximum=0xf, wide=True) if random.random() < (self.random_degree / 2): power = int(round(power * 2 / 3)) count_statuses = bin(self.boost_statuses) while bin(self.boost_statuses).count('1') == count_statuses: new_status = random.choice([0x10, 0x40, 0x80]) self.misc_value |= new_status
def get_similar(self): if self.rare: result = random.choice(self.every_rare) elif self.buyable: buyables = self.every_buyable index = buyables.index(self) index = mutate_normal(index, minimum=0, maximum=len(buyables) - 1, random_degree=self.random_degree) result = buyables[index] else: result = self return result
def new_family_attributes(self): self.family_attributes, self.extended_family_attributes if not hasattr(MonsterObject, '_newfamattr'): MonsterObject._newfamattr = {} if self.family_key in MonsterObject._newfamattr: return MonsterObject._newfamattr[self.family_key] if self.family_key >= 0xc0: MonsterObject._newfamattr[self.family_key] = \ MonsterObject._famattr[self.family_key] return self.new_family_attributes num_attributes = len(self.family_attributes) min_attributes = 8 max_attributes = max( len(v) for (k, v) in MonsterObject._famattr.items() if (k >> 4) <= 0xb) num_attributes = mutate_normal( num_attributes, minimum=min_attributes, maximum=max_attributes, random_degree=self.random_degree, wide=True) new_attributes = [] while len(new_attributes) < num_attributes: old_attribute = random.choice(sorted(self.family_attributes)) if random.random() > self.random_degree: # family attributes candidates = sorted(self.family_attributes) elif random.random() > self.random_degree: # extended family attributes candidates = sorted(self.extended_family_attributes) else: # any attributes candidates = {a for i in MonsterObject._exfamattr for a in MonsterObject._exfamattr[i] if i < 0xc} candidates = sorted(candidates) if len(new_attributes) == 0: candidates = [c for c in candidates if c.get_bit('use_battle')] new_attribute = old_attribute.get_similar( candidates=candidates, random_degree=self.random_degree, override_outsider=True) if new_attribute not in new_attributes: new_attributes.append(new_attribute) assert any([a.get_bit('use_battle') for a in new_attributes]) MonsterObject._newfamattr[self.family_key] = sorted(new_attributes) return self.new_family_attributes
def mutate(self): super(MonsterObject, self).mutate() self.reseed(salt="extra") drop_partner = self.get_similar() if drop_partner.old_data["drop_type"] or not self.is_boss: for attr in ["drop_type", "drop_index", "drop_chance"]: setattr(self, attr, drop_partner.old_data[attr]) if self.drop: new_item = self.drop.get_similar_all_items() self.set_drop(new_item) self.drop_chance = mutate_normal( self.drop_chance, 0, 100, wide=False, random_degree=self.random_degree) self.mutate_magic_bits()
def mutate(self): if self.rank <= 0 and self.contents: return self.reseed(salt="mut") partner = random.choice([ c for c in self.every if c.rank > 0 and c.item_type > 0 and c.contents > 0 ]) value = self.rank if value <= 0: high = random.randint(0, partner.rank) low = random.randint(0, high) value = int( round((high * self.random_degree) + (low * (1 - self.random_degree)))) if not partner.get_bit("contains_item"): value = random.randint(int(round(value**0.9)), value) value = min(value, 65537) value = mutate_normal(value, 1, 65537, random_degree=self.random_degree) value = min(value, 65535) self.set_money_amount(value) else: partner = partner.item candidates = [c for c in partner.ranked if 0 < c.rank <= value] if not candidates: candidates = [c for c in partner.ranked if c.rank > 0] chosen = candidates[0] else: chosen = candidates[-1] chosen = chosen.get_similar( random_degree=ChestObject.random_degree) assert chosen.index > 0 self.set_item(chosen)
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(self): super(MonsterObject, self).mutate() if 1 <= self.level <= 99: new_level = mutate_normal(self.level, minimum=1, maximum=99, random_degree=self.random_degree) old_divisibility = divisibility_rank(self.level) new_divisibility = divisibility_rank(new_level) if new_divisibility < old_divisibility: if not self.is_boss: self.level = new_level else: difference = float(new_level) / self.level if difference > 1: difference = 1 / difference difference = (difference * (1 - self.random_degree)) + ( self.random_degree**2) if random.random() < difference: self.level = new_level elif (not self.is_boss and random.random() < (self.random_degree**0.5)): self.level = new_level
def cleanup(self): growth = CharGrowthObject.get(self.index) for attr in ["power", "stamina", "magic", "hit", "evade", "mdef"]: baseattr = "%s_base" % attr increase = getattr(growth, attr) * (self.level - 1) / 100 initial = getattr(self, baseattr) + increase setattr(self, attr, initial) hpgroup = HPGrowthObject.getgroup(self.index) mpgroup = MPGrowthObject.getgroup(self.index) max_hp = mutate_normal(85, minimum=1, maximum=999, random_degree=self.random_degree) max_mp = mutate_normal(9, minimum=1, maximum=99, random_degree=self.random_degree) temp_level = 1 while temp_level < self.level: temp_level += 1 hpo = [o for o in hpgroup if temp_level <= o.level][-1] mpo = [o for o in mpgroup if temp_level <= o.level][-1] max_hp += hpo.increase max_mp += mpo.increase max_hp = min(999, max(max_hp, 1)) max_mp = min(99, max(max_mp, 1)) self.max_hp, self.hp = max_hp, max_hp self.max_mp, self.mp = max_mp, max_mp if self.level > 1: self.xp = sum([ e.experience for e in ExperienceObject if e.index < (self.level - 1) ]) else: self.xp = 0 self.xpnext = ExperienceObject.get(self.level - 1).experience for attr in ["helmet", "armor"]: current = getattr(self, attr) if self.can_equip(current): continue candidates = [ c for c in ItemObject.every_equippable if getattr(c, "is_%s" % attr) and self.can_equip(c) and c.buyable ] if not candidates: candidates = [ c for c in ItemObject.every_equippable if getattr(c, "is_%s" % attr) and c.buyable ] chosen = min(candidates, key=lambda c: c.rank_price) chosen.make_equippable(self.index) setattr(self, attr, chosen.full_index) current = ItemObject.get(self.accessory) if not self.can_equip(current): candidates = [ c for c in Accessory2Object.every if self.can_equip(c) ] if candidates: chosen = random.choice(candidates) chosen.make_equippable(self.index) self.accessory = chosen.full_index
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]
def rank(self): if hasattr(self, '_rank'): return self._rank by_price = sorted(self.every, key=lambda p: (p.price, p.signature)) by_price = [p for p in by_price if p.price > 0 and p.is_buyable] by_drop_rank = sorted( self.every, key=lambda p: (p.drop_rank, p.signature)) by_drop_rank = [p for p in by_drop_rank if p.drop_rank > 0] by_shop_day = sorted( self.every, key=lambda p: (p.old_shop_availability, p.signature)) by_shop_day = [p for p in by_shop_day if p.is_quest_buyable] for p in self.every: ranks = [] if p in by_price and p.is_buyable: ranks.append(by_price.index(p) / float(len(by_price))-1) if p in by_drop_rank: ranks.append( by_drop_rank.index(p) / float(len(by_drop_rank))-1) if ranks: p._rank = min(ranks) for p in self.every: if hasattr(p, '_rank'): for ev in p.evolves: if ev: p2 = PinObject.get(ev) if not hasattr(p2, '_rank') or p2._rank < p._rank: value = gen_random_normal() ** 4 new_rank = (value * 1.0) + ((1-value) * p._rank) p2._rank = new_rank by_price = sorted(self.every, key=lambda p: (p.price, p.signature)) by_price = [p for p in by_price if p.price > 0] for p in self.every: if hasattr(p, '_rank'): continue if p in by_shop_day: p._rank = by_shop_day.index(p) / float(len(by_shop_day)-1) elif p in by_price: p._rank = by_price.index(p) / float(len(by_price)-1) else: p._rank = random.random() sorted_class = sorted( self.every, key=lambda p: (-p.pin_class, p._rank, p.signature)) sorted_noclass = sorted( self.every, key=lambda p: (p._rank, p.signature)) for p in self.every: if p.pin_class >= 5: rank = sorted_noclass.index(p) else: value = gen_random_normal() rank = ((sorted_class.index(p) * value) + ((1-value) * sorted_noclass.index(p))) p._rank = mutate_normal( rank, minimum=0, maximum=len(self.every), wide=True, random_degree=self.random_degree**2, return_float=True) sorted_noclass = sorted( self.every, key=lambda p: (p._rank, p.signature)) for p in self.every: p._rank = sorted_noclass.index(p) / float(len(sorted_noclass)-1) return self.rank