def get_special_ap(self):
     levels = [e.stats['level'] for e in self.present_enemies if e]
     ap = int(sum(levels) // len(levels))
     low = ap // 2
     ap = low + random.randint(0, low) + random.randint(0, low)
     ap = random.randint(0, ap)
     self.ap = min(100, max(ap, 0))
 def mutate(self, always_break=False):
     global changed_commands
     self.mutate_stats()
     self.mutate_price()
     broken, learned = False, False
     if always_break:
         self.mutate_break_effect(always_break=True)
         broken = True
     for command, itemids in break_unused_dict.items():
         if command in changed_commands and self.itemid in itemids:
             self.mutate_break_effect()
             broken = True
     if self.itemid == 0xE6:
         self.mutate_learning()
         learned = True
     while random.randint(1, 5) == 5:
         x = random.randint(0, 99)
         if x < 10:
             self.mutate_special_action()
         if 10 <= x < 20 and not learned:
             self.mutate_learning()
         if 20 <= x < 50 and not broken:
             self.mutate_break_effect()
             broken = True
         if 50 <= x < 80:
             self.mutate_elements()
         if x >= 80:
             self.mutate_feature()
     if not self.heavy and random.randint(1, 20) == 20:
         self.heavy = True
Exemple #3
0
    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 get_special_mp(self):
     levels = [e.stats['level'] for e in self.present_enemies if e]
     mp = int(sum(levels) // len(levels))
     low = mp // 2
     mp = low + random.randint(0, low) + random.randint(0, low)
     mp = random.randint(0, mp)
     self.mp = min(100, max(mp, 0))
Exemple #5
0
 def mutate(self, always_break=False):
     global changed_commands
     self.mutate_stats()
     self.mutate_price()
     broken, learned = False, False
     if always_break:
         self.mutate_break_effect(always_break=True)
         broken = True
     for command, itemids in break_unused_dict.items():
         if command in changed_commands and self.itemid in itemids:
             self.mutate_break_effect()
             broken = True
     if self.itemid == 0xE6:
         self.mutate_learning()
         learned = True
     while random.randint(1, 5) == 5:
         x = random.randint(0, 99)
         if x < 10:
             self.mutate_special_action()
         if 10 <= x < 20 and not learned:
             self.mutate_learning()
         if 20 <= x < 50 and not broken:
             self.mutate_break_effect()
             broken = True
         if 50 <= x < 80:
             self.mutate_elements()
         if x >= 80:
             self.mutate_feature()
     if not self.heavy and random.randint(1, 20) == 20:
         self.heavy = True
 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
Exemple #7
0
 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
Exemple #8
0
    def mutate_break_effect(self, always_break=False, wild_breaks=False, no_breaks=False, unbreakable=False):
        global effects_used
        if self.is_consumable:
            return

        if no_breaks:
            self.itemtype &= ~0x20
            return

        if unbreakable:
            self.features['otherproperties'] &= ~0x08
            return

        if always_break:
            effects_used = []

        success = False
        max_spellid = 0xFE if wild_breaks else 0x50
        for _ in range(100):
            spell, _ = self.pick_a_spell(custom=lambda x: x.spellid <= max_spellid)
            if spell.spellid not in effects_used:
                effects_used.append(spell.spellid)
                success = True
                break

        if not success:
            return

        # swdtechs, blitzes, superball, and slots don't seem to work
        # correctly with procs, but they work with breaks.
        # (Mostly they just play the wrong animation, but a couple
        # softlock.)
        no_proc_ids = list(range(0x55, 0x66)) + list(range(0x7D, 0x82))
        self.features['breakeffect'] = spell.spellid
        if not self.is_weapon or random.randint(1, 2) == 2 or always_break or spell.spellid in no_proc_ids:
            # always make armors usable in battle; weapons, only sometimes
            self.itemtype = self.itemtype | 0x20

        # flag to break when used as an item
        if random.randint(1, 20) == 20:
            self.features['otherproperties'] &= 0xF7
        else:
            self.features['otherproperties'] |= 0x08

        # flag to set chance to proc a spell
        if self.is_weapon and spell.spellid not in no_proc_ids and (
                not self.itemtype & 0x20 or random.randint(1, 2) == 2):
            self.features['otherproperties'] |= 0x04
            self.mutation_log["Proc"] = get_spell(self.features['breakeffect']).name
        else:
            self.features['otherproperties'] &= 0xFB

        self.features['targeting'] = spell.targeting & 0xef
        self.mutate_name()
Exemple #9
0
        def mutate_power_hitmdef():
            diff = min(self.features['power'], 0xFF-self.features['power'])
            diff = diff / 3
            self.features['power'] = self.features['power'] - diff
            self.features['power'] = self.features['power'] + random.randint(0, diff) + random.randint(0, diff)
            self.features['power'] = int(min(0xFF, max(0, self.features['power'])))

            diff = min(self.features['hitmdef'], 0xFF-self.features['hitmdef'])
            diff = diff / 3
            self.features['hitmdef'] = self.features['hitmdef'] - diff
            self.features['hitmdef'] = self.features['hitmdef'] + random.randint(0, diff) + random.randint(0, diff)
            self.features['hitmdef'] = int(min(0xFF, max(0, self.features['hitmdef'])))
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
Exemple #11
0
    def mutate_special_action(self):
        if self.features['specialaction'] & 0xf0 != 0 or not self.is_weapon:
            return

        new_action = random.randint(1, 0xf)
        if new_action == 0xA:  # make random valiant knife effect rare
            new_action = random.randint(1, 0xf)

        if new_action == 9:  # no random dice effect
            return

        self.features['specialaction'] = (new_action << 4) | (
            self.features['specialaction'] & 0x0f)
Exemple #12
0
 def enrank(r):
     mr = min(maxrank, 0xFF)
     r = max(0, min(r, mr))
     if options_.is_code_active('racecave'):
         half = r // 2
         quarter = half // 2
         r = (half + random.randint(0, quarter) +
              random.randint(0, quarter))
     if r <= 0:
         return 0
     elif r >= mr:
         return 1.0
     ratio = float(r) / mr
     return ratio
 def unlock_chests(self, low, high, monster=False,
                   guarantee_miab_treasure=False, enemy_limit=None):
     if len(self.chests) == 1:
         low = (low + high) / 2
     dist = (high - low) / 2
     for c in self.chests:
         c.set_content_type(0x80)
         c.contents = None
         value = low + random.randint(0, dist) + random.randint(0, dist)
         c.value = value
         c.mutate_contents(monster=monster, enemy_limit=enemy_limit,
                           guarantee_miab_treasure=guarantee_miab_treasure,
                           uniqueness=len(self.chests) != 1)
         if random.randint(1, 5) >= 4:
             c.set_new_id()
        def mutate_power_hitmdef():
            diff = min(self.features['power'], 0xFF-self.features['power'])
            diff = diff / 3
            self.features['power'] = self.features['power'] - diff
            self.features['power'] = self.features['power'] + random.randint(0, diff) + random.randint(0, diff)
            self.features['power'] = int(min(0xFF, max(0, self.features['power'])))

            if "Dice" in self.name:
                return

            diff = min(self.features['hitmdef'], 0xFF-self.features['hitmdef'])
            diff = diff / 3
            self.features['hitmdef'] = self.features['hitmdef'] - diff
            self.features['hitmdef'] = self.features['hitmdef'] + random.randint(0, diff) + random.randint(0, diff)
            self.features['hitmdef'] = int(min(0xFF, max(0, self.features['hitmdef'])))
 def unlock_chests(self, low, high, monster=False,
                   guarantee_miab_treasure=False, enemy_limit=None, crazy_prices=False, uncapped_monsters=False):
     if len(self.chests) == 1:
         low = (low + high) // 2
     dist = (high - low) // 2
     for c in self.chests:
         c.set_content_type(0x80)
         c.contents = None
         value = low + random.randint(0, dist) + random.randint(0, dist)
         c.value = value
         c.mutate_contents(monster=monster, enemy_limit=enemy_limit,
                           guarantee_miab_treasure=guarantee_miab_treasure,
                           uniqueness=len(self.chests) != 1, crazy_prices=crazy_prices,
                           uncapped_monsters=uncapped_monsters)
         if random.randint(1, 5) >= 4:
             c.set_new_id()
Exemple #16
0
    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 mutate_chests(self, guideline=None):
        for c in self.chests:
            if self.fset.setid == 0:
                c.set_rank(None)
            else:
                c.set_rank(self.rank())

        if guideline is None:
            if len(self.chests) > 0:
                values = [
                    c.get_current_value(guideline=100) for c in self.chests
                ]
                average_value = (sum(values) * 100) / len(values)
                guideline = average_value
            else:
                guideline = 100

        if self.locid == 0xb4:
            return
        elif self.locid == 0x147:
            guideline = random.randint(1820, 4500)

        random.shuffle(self.chests)
        for c in self.chests:
            if self.locid in range(0x139, 0x13d) and c.empty:
                c.mutate_contents(monster=True, guideline=guideline)
                continue
            elif self.locid == 0x147:
                pass

            c.mutate_contents(guideline=guideline)
            if guideline is None and hasattr(c, "value") and c.value:
                guideline = value
Exemple #18
0
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 mutate_chests(self, guideline=None):
        for c in self.chests:
            if self.fset.setid == 0:
                c.set_rank(None)
            else:
                c.set_rank(self.rank())

        if guideline is None:
            if len(self.chests) > 0:
                values = [c.get_current_value(guideline=100)
                          for c in self.chests]
                average_value = (sum(values)*100) / len(values)
                guideline = average_value
            else:
                guideline = 100

        if self.locid == 0xb4:
            return
        elif self.locid == 0x147:
            guideline = random.randint(1820, 4500)

        random.shuffle(self.chests)
        for c in self.chests:
            if self.locid in range(0x139, 0x13d) and c.empty:
                c.mutate_contents(monster=True, guideline=guideline)
                continue
            elif self.locid == 0x147:
                pass

            c.mutate_contents(guideline=guideline)
            if guideline is None and hasattr(c, "value") and c.value:
                guideline = value
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)
Exemple #22
0
    def mutate_price(self, undo_priceless=False, crazy_prices=False):
        if crazy_prices:
            if self.itemid == 250:
                self.price = random.randint(250, 500)
            else:
                self.price = random.randint(20, 500)
            return
        if self.price <= 2:
            if undo_priceless:
                self.price = self.rank()
            else:
                return

        normal = self.price // 2
        self.price += random.randint(0, normal) + random.randint(0, normal)
        while random.randint(1, 10) == 10:
            self.price += random.randint(0, normal) + random.randint(0, normal)

        zerocount = 0
        while self.price > 100:
            self.price = self.price // 10
            zerocount += 1

        while zerocount > 0:
            self.price = self.price * 10
            zerocount += -1

        self.price = min(self.price, 65000)
Exemple #23
0
def bit_mutate(byte, op="on", nochange=0x00):
    if op == "on":
        bit = 1 << random.randint(0, 7)
        if bit & nochange:
            return byte
        byte = byte | bit
    elif op == "off":
        bit = 1 << random.randint(0, 7)
        if bit & nochange:
            return byte
        bit = 0xff ^ bit
        byte = byte & bit
    elif op == "invert":
        bit = 1 << random.randint(0, 7)
        if bit & nochange:
            return byte
        byte = byte ^ bit
    return byte
    def mutate_special_action(self):
        if self.features['specialaction'] != 0 or not self.is_weapon:
            return

        new_action = random.randint(1, 0xf)
        if new_action == 8:
            return

        self.features['specialaction'] = new_action
Exemple #25
0
        def mutation(base):
            while True:
                value = max(base // 2, 1)
                if self.beserk:
                    value += 1

                value += random.randint(0, value) + random.randint(0, value)
                while random.randint(1, 10) == 10:
                    value = max(value // 2, 1)
                    value += random.randint(0, value) + random.randint(0, value)
                value = max(1, min(value, 0xFE))

                if not self.beserk:
                    break
                elif value >= base:
                    break

            return value
def bit_mutate(byte, op="on", nochange=0x00):
    if op == "on":
        bit = 1 << random.randint(0, 7)
        if bit & nochange:
            return byte
        byte = byte | bit
    elif op == "off":
        bit = 1 << random.randint(0, 7)
        if bit & nochange:
            return byte
        bit = 0xff ^ bit
        byte = byte & bit
    elif op == "invert":
        bit = 1 << random.randint(0, 7)
        if bit & nochange:
            return byte
        byte = byte ^ bit
    return byte
Exemple #27
0
    def mutate_special_action(self):
        if self.features['specialaction'] != 0 or not self.is_weapon:
            return

        new_action = random.randint(1, 0xf)
        if new_action == 8:
            return

        self.features['specialaction'] = new_action
Exemple #28
0
 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
Exemple #29
0
    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)
Exemple #30
0
 def make_challenge_event(loc, ptr):
     bg = ch_bgs.pop()
     formids = random.sample(challenges[loc.restrank], 2)
     formations = [get_formation(formid) for formid in formids]
     for formid in formids:
         challenges[loc.restrank].remove(formid)
     setcands = [f for f in get_fsets() if f.setid >= 0x100 and f.unused]
     fset = setcands.pop()
     fset.formids = formids
     fset.write_data(fout)
     timer = max(
         [e.stats['hp'] for f in formations for e in f.present_enemies])
     reverse = False
     if timer >= 32768:
         reverse = True
         timer = 65535 - timer
     timer = max(timer, 3600)
     half = None
     while half is None or random.randint(1, 5) == 5:
         half = timer // 2
         timer = half + random.randint(0, half) + random.randint(0, half)
     if reverse:
         timer = 65535 - timer
     timer = int(round(timer / 1800.0))
     timer = max(2, min(timer, 36))
     timer = timer * 1800
     timer = [timer & 0xFF, timer >> 8]
     addr1 = ptr + 10 - 0xa0000
     addr2 = ptr + (len(enemy_template) - 1) - 0xa0000
     addr1 = [addr1 & 0xFF, (addr1 >> 8) & 0xFF, addr1 >> 16]
     addr2 = [addr2 & 0xFF, (addr2 >> 8) & 0xFF, addr2 >> 16]
     bytestring = list(enemy_template)
     bytestring[4:7] = addr1
     bytestring[7:10] = addr2
     bytestring[11:13] = timer
     bytestring[17] = fset.setid & 0xFF
     bytestring[18] = bg
     assert None not in bytestring
     sub = Substitution()
     sub.set_location(ptr)
     sub.bytestring = bytes(bytestring)
     sub.write(fout)
     return ptr + len(enemy_template)
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 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)
Exemple #34
0
 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 mutate_price(self, undo_priceless=False):
        if self.price <= 2:
            if undo_priceless:
                self.price = self.rank()
            else:
                return

        normal = self.price / 2
        self.price += random.randint(0, normal) + random.randint(0, normal)
        while random.randint(1, 10) == 10:
            self.price += random.randint(0, normal) + random.randint(0, normal)

        zerocount = 0
        while self.price > 100:
            self.price = self.price / 10
            zerocount += 1

        while zerocount > 0:
            self.price = self.price * 10
            zerocount += -1

        self.price = min(self.price, 65000)
Exemple #38
0
    def mutate_price(self, undo_priceless=False):
        if self.price <= 2:
            if undo_priceless:
                self.price = self.rank()
            else:
                return

        normal = self.price / 2
        self.price += random.randint(0, normal) + random.randint(0, normal)
        while random.randint(1, 10) == 10:
            self.price += random.randint(0, normal) + random.randint(0, normal)

        zerocount = 0
        while self.price > 100:
            self.price = self.price / 10
            zerocount += 1

        while zerocount > 0:
            self.price = self.price * 10
            zerocount += -1

        self.price = min(self.price, 65000)
Exemple #39
0
    def mutate_break_effect(self, always_break=False):
        global effects_used
        if self.is_consumable:
            return

        if always_break:
            effects_used = []

        success = False
        for _ in xrange(100):
            spell, _ = self.pick_a_spell(custom=lambda x: x.spellid <= 0x3F)
            if spell.spellid not in effects_used:
                effects_used.append(spell.spellid)
                success = True
                break

        if not success:
            return

        self.features['breakeffect'] = spell.spellid
        if not self.is_weapon or random.randint(1, 2) == 2 or always_break:
            # always make armors usable in battle; weapons, only sometimes
            self.itemtype = self.itemtype | 0x20

        # flag to break when used as an item
        if random.randint(1, 20) == 20:
            self.features['breakeffect'] &= 0x7F
        else:
            self.features['breakeffect'] |= 0x80

        # flag to set chance to proc a spell
        if self.is_weapon and (not self.itemtype & 0x20 or random.randint(1, 2) == 2):
            self.features['breakeffect'] |= 0x40
        else:
            self.features['breakeffect'] &= 0xBF

        self.features['targeting'] = spell.targeting & 0xef
    def mutate_break_effect(self, always_break=False):
        global effects_used
        if self.is_consumable:
            return

        if always_break:
            effects_used = []

        success = False
        for _ in xrange(100):
            spell, _ = self.pick_a_spell(custom=lambda x: x.spellid <= 0x3F)
            if spell.spellid not in effects_used:
                effects_used.append(spell.spellid)
                success = True
                break

        if not success:
            return

        self.features['breakeffect'] = spell.spellid
        if not self.is_weapon or random.randint(1, 2) == 2 or always_break:
            # always make armors usable in battle; weapons, only sometimes
            self.itemtype = self.itemtype | 0x20

        # flag to break when used as an item
        if random.randint(1, 20) == 20:
            self.features['breakeffect'] &= 0x7F
        else:
            self.features['breakeffect'] |= 0x80

        # flag to set chance to proc a spell
        if self.is_weapon and (not self.itemtype & 0x20 or random.randint(1, 2) == 2):
            self.features['breakeffect'] |= 0x40
        else:
            self.features['breakeffect'] &= 0xBF

        self.features['targeting'] = spell.targeting & 0xef
    def mutate_chests(self, guideline=None, crazy_prices=False, no_monsters=False, uncapped_monsters=False):
        if not self.chests:
            return

        rank = None
        override = maplocations_override.get(maplocations[self.locid], None)
        if (self.attacks & 0x80 == 0 or override) and self.locid in maplocations:
            location = maplocations[self.locid]
            if override:
                location = override
            fsets = {get_location(l).fset
                     for l in maplocations_reverse[location]
                     if get_location(l).attacks & 0x80 != 0}
            if fsets:
                rank = min(fsets, key=lambda f: f.rank()).rank()
        else:
            rank = self.rank()
        for c in self.chests:
            c.set_rank(rank)

        if guideline is None:
            if not self.chests:
                values = [c.get_current_value(guideline=100)
                          for c in self.chests]
                average_value = (sum(values)*100) // len(values)
                guideline = average_value
            else:
                guideline = 100

        if self.locid == 0xb4:
            return
        if self.locid == 0x147:
            guideline = random.randint(1820, 4500)

        random.shuffle(self.chests)
        for c in self.chests:
            if self.locid in range(0x139, 0x13d) and c.empty:
                c.mutate_contents(monster=True, guideline=guideline, crazy_prices=crazy_prices, uncapped_monsters=uncapped_monsters)
                continue
            elif self.locid == 0x147:
                pass

            # No monster-in-a-box in the ZoneEater falling ceiling room.
            # It causes problems with the ceiling event.
            in_falling_ceiling_room = self.locid == 280 and c.memid in range(232, 235)
            monster = False if in_falling_ceiling_room or no_monsters else None
            c.mutate_contents(guideline=guideline, crazy_prices=crazy_prices, monster=monster, uncapped_monsters=uncapped_monsters)
            if guideline is None and hasattr(c, "value") and c.value:
                guideline = c.value
    def mutate_misc(self):
        self.misc &= 0x0F
        value = random.randint(1, 20)
        if value <= 3:
            self.misc |= 0x20
        elif value <= 9:
            self.misc |= 0x10

        for i in self.items:
            i = get_item(i)
            if i is None:
                continue
            if i.price * price_multipliers[self.discount] > 65535:
                self.misc &= 0x0F
                break
Exemple #43
0
    def mutate_misc(self):
        self.misc &= 0x0F
        value = random.randint(1, 20)
        if value <= 3:
            self.misc |= 0x20
        elif value <= 9:
            self.misc |= 0x10

        for i in self.items:
            i = get_item(i)
            if i is None:
                continue
            if i.price * price_multipliers[self.discount] > 65535:
                self.misc &= 0x0F
                break
    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 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 mutate_contents(self, guideline=None, monster=None,
                        guarantee_miab_treasure=False, enemy_limit=None,
                        uniqueness=False):
        global used_formations, done_items

        if self.do_not_mutate and self.contents is not None:
            return

        if self.value is not None:
            value = self.value
        else:
            value = self.get_current_value(guideline=guideline)

        items = get_ranked_items()
        itemids = [i.itemid for i in items]
        if self.treasure:
            try:
                index = itemids.index(self.contents)
            except ValueError:
                index = 0
            indexed_item = items[index]
        else:
            lowpriced = [i for i in items if i.rank() <= value*100]
            if not lowpriced:
                lowpriced = items[:random.randint(1, 16)]
            index = max(0, len(lowpriced)-1)
            indexed_item = lowpriced[index]

        chance = random.randint(1, 50)
        orphaned_formations = get_orphaned_formations()
        orphaned_formations = [f for f in orphaned_formations
                               if f not in used_formations]
        extra_miabs = get_extra_miabs(0)
        if orphaned_formations or extra_miabs:
            chance -= 2
            chance = max(chance, 1)

        if monster is True:
            chance = 1
        elif monster is False:
            chance += 3
            chance = min(chance, 50)

        formations = get_appropriate_formations()
        formations = [f for f in formations if
                      f.get_guaranteed_drop_value() >= value * 100]
        if 1 <= chance <= 3 and (self.rank or formations):
            # monster
            self.set_content_type(0x20)

            rank = self.rank or min(formations, key=lambda f: f.rank()).rank()
            if guarantee_miab_treasure:
                extra_miabs = []
                orphaned_formations = []
                candidates = []
            else:
                if len(extra_miabs) > 1:
                    extra_miabs = get_extra_miabs(rank)
                if orphaned_formations or extra_miabs:
                    formations = [f for f in formations if f.rank() >= rank]
                    formations = formations[:random.randint(1, 3)]

                candidates = (orphaned_formations + extra_miabs)
            candidates = sorted(set(candidates))
            if len(candidates) != 1:
                candidates += formations
            candidates = [c for c in candidates if c not in used_formations]
            candidates = [c for c in candidates
                          if c.formid not in banned_formids]

            if enemy_limit is not None:
                candidates = [f for f in candidates if f.rank() <= enemy_limit]

            if not candidates:
                candidates = (formations +
                              get_orphaned_formations() + get_extra_miabs(0))
                if enemy_limit is not None:
                    candidates = [f for f in candidates
                                  if f.rank() <= enemy_limit]
                    candidates = sorted(candidates, key=lambda f: f.rank())
                    half = len(candidates) / 2
                    candidates = candidates[half:]
                    index = random.randint(0, half) + random.randint(0, half)
                    index = min(index, len(candidates)-1)
                    candidates = candidates[index:]

            candidates = sorted(candidates, key=lambda f: f.rank())
            if orphaned_formations:
                index = max(
                    0, len([c for c in candidates if c.rank() <= rank])-1)
                index = mutate_index(index, len(candidates), [False, True],
                                     (-3, 2), (-1, 1))
            else:
                index = 0
                index = mutate_index(index, len(candidates), [False, True],
                                     (-1, 4), (-1, 1))

            chosen = candidates[index]
            for m in chosen.present_enemies:
                m.auxloc = "Monster-in-a-Box"

            banned_formids.append(chosen.formid)
            used_formations.append(chosen)
            chosen = get_2pack(chosen)
            # only 2-packs are allowed
            self.contents = chosen.setid & 0xFF
        elif 4 <= chance <= 5:
            # gold
            self.set_content_type(0x80)
            value = value / 2
            value += (random.randint(0, value) + random.randint(0, value))
            self.contents = min(0xFF, max(1, value))
            if self.contents == 0xFF:
                self.contents -= random.randint(0, 20) + random.randint(0, 20)
        else:
            # treasure
            self.set_content_type(0x40)
            if uniqueness and random.randint(1, 7) != 7:
                if len(done_items) >= len(items):
                    done_items = []
                temp = [i for i in items
                        if i == indexed_item or i not in done_items]
                if len(temp) > 1:
                    items = temp
                    index = items.index(indexed_item)
                    if indexed_item in done_items:
                        items.remove(indexed_item)
            index = mutate_index(index, len(items), [False, True],
                                 (-4, 2), (-2, 2))
            self.contents = items[index].itemid
            done_items.append(items[index])

        assert self.contents <= 0xFF
        self.value = value
    def mutate_items(self, fout):
        items = get_ranked_items()
        if self.shoptype == 1:
            valid_items = [c for c in items if c.is_weapon or c.is_tool]
        elif self.shoptype == 2:
            valid_items = [c for c in items if c.is_armor]
        elif self.shoptype == 3:
            valid_items = [c for c in items if not (c.is_weapon or c.is_armor or c.is_relic)]
        elif self.shoptype == 4:
            valid_items = [c for c in items if c.is_relic]
        elif self.shoptype == 5:
            valid_items = list(items)

        old_items = [i for i in self.items if i != 0xFF]
        if not old_items:
            return
        old_items = [get_item(i) for i in old_items]
        old_items = [i for i in old_items if i]

        if len(old_items) == 0:
            average_value = 0
        else:
            average_value = sum([i.rank() for i in old_items]) / len(old_items)
        average_item = len([i for i in valid_items if i.rank() <= average_value])
        average_item += -1
        average_item = valid_items[average_item]

        while random.randint(1, 3) == 3 and len(old_items) < 8:
            old_items.append(average_item)
        new_items = []
        for item in old_items:
            if random.randint(1, 10) == 10:
                candidates = items
            else:
                candidates = valid_items

            try:
                index = candidates.index(item)
            except ValueError:
                continue

            while random.randint(1, 3) < 3:
                index += random.randint(-2, 2)
                index = max(0, min(index, len(candidates)-1))
            new_items.append(candidates[index])

        if not new_items:
            return

        for i in new_items:
            if i.price < 3:
                price = i.rank()
                modifier = price / 2
                price += random.randint(0, modifier)
                while random.randint(1, 4) < 4:
                    price += random.randint(0, modifier)
                price = min(price, 0xFEFE)
                i.price = price

                zerocount = 0
                while i.price > 100:
                    i.price = i.price / 10
                    zerocount += 1

                while zerocount > 0:
                    i.price = i.price * 10
                    zerocount += -1

                i.write_stats(fout)

        self.items = [i.itemid for i in new_items]
        self.items = sorted(set(self.items))
        while len(self.items) < 8:
            self.items.append(0xFF)

        assert len(self.items) == 8
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 mutate_stats(self):
        if self.is_consumable:
            return

        def mutate_power_hitmdef():
            diff = min(self.features['power'], 0xFF-self.features['power'])
            diff = diff / 3
            self.features['power'] = self.features['power'] - diff
            self.features['power'] = self.features['power'] + random.randint(0, diff) + random.randint(0, diff)
            self.features['power'] = int(min(0xFF, max(0, self.features['power'])))

            if "Dice" in self.name:
                return

            diff = min(self.features['hitmdef'], 0xFF-self.features['hitmdef'])
            diff = diff / 3
            self.features['hitmdef'] = self.features['hitmdef'] - diff
            self.features['hitmdef'] = self.features['hitmdef'] + random.randint(0, diff) + random.randint(0, diff)
            self.features['hitmdef'] = int(min(0xFF, max(0, self.features['hitmdef'])))

        mutate_power_hitmdef()
        while random.randint(0, 10) == 10:
            mutate_power_hitmdef()

        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

        self.features['speedvigor'] = mutate_nibble(self.features['speedvigor'])
        self.features['speedvigor'] = mutate_nibble(self.features['speedvigor'], left=True)
        self.features['magstam'] = mutate_nibble(self.features['magstam'])
        self.features['magstam'] = mutate_nibble(self.features['magstam'], left=True)

        evade, mblock = self.evade, self.mblock

        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

        evade = evade_is_screwed_up(evade)
        mblock = evade_is_screwed_up(mblock)
        self.features['mblockevade'] = evade | (mblock << 4)
def reset_equippable(items, characters, numchars=NUM_CHARS):
    global changed_commands
    prevents = filter(lambda i: i.prevent_encounters, items)
    for item in prevents:
        while True:
            test = 1 << random.randint(0, numchars-1)
            if item.itemid == 0xDE or not (CHAR_MASK & item.equippable):
                item.equippable = test
                break

            if test & item.equippable:
                test |= IMP_MASK
                item.equippable &= test
                break

    items = filter(lambda i: not (i.is_consumable or i.is_tool or i.prevent_encounters), items)
    new_weaps = range(numchars)
    random.shuffle(new_weaps)
    new_weaps = dict(zip(range(numchars), new_weaps))

    for item in items:
        if numchars == 14 and random.randint(1, 10) == 10:
            # for umaro's benefit
            item.equippable |= 0x2000

        if item.is_weapon:
            equippable = item.equippable
            item.equippable &= IMP_MASK
            for i in range(numchars):
                if equippable & (1 << i):
                    item.equippable |= (1 << new_weaps[i])
        elif item.is_relic:
            if random.randint(1, 15) == 15:
                item.equippable = 1 << (random.randint(0, numchars-1))
                while random.randint(1, 3) == 3:
                    item.equippable |= (1 << (random.randint(0, numchars-1)))
            else:
                item.equippable = CHAR_MASK

    charequips = []
    valid_items = filter(lambda i: (not i.is_weapon and not i.is_relic
                                    and not i.equippable & 0x4000), items)
    for c in range(numchars):
        myequips = []
        for i in valid_items:
            if i.equippable & (1 << c):
                myequips.append(True)
            else:
                myequips.append(False)
        random.shuffle(myequips)
        charequips.append(myequips)

    for item in valid_items:
        item.equippable &= 0xc000

    random.shuffle(charequips)
    for c in range(numchars):
        assert len(valid_items) == len(charequips[c])
        for equippable, item in zip(charequips[c], valid_items):
            if equippable:
                item.equippable |= (1 << c)

    if random.randint(1, 3) == 3:
        weaponstoo = True
    else:
        weaponstoo = False

    for item in items:
        if item.equippable == 0:
            if not weaponstoo:
                continue
            item.equippable |= (1 << random.randint(0, numchars-1))

    paladin_equippable = None
    for item in items:
        if item.itemid in [0x66, 0x67]:
            if paladin_equippable is not None:
                item.equippable = paladin_equippable
            else:
                paladin_equippable = item.equippable

    if 0x10 not in changed_commands:
        for item in items:
            if item.itemid == 0x1C:
                rage_chars = [c for c in characters if
                              0x10 in c.battle_commands]
                rage_mask = 0
                for c in rage_chars:
                    rage_mask |= (1 << c.id)
                rage_mask |= (1 << 12)  # gogo
                if item.equippable & rage_mask:
                    invert_rage_mask = 0xFFFF ^ rage_mask
                    item.equippable &= invert_rage_mask
                assert not item.equippable & rage_mask

    return items
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()