Ejemplo n.º 1
0
    def load_regions_from_json(self, file_path):
        json_string = ""
        with io.open(file_path, 'r') as file:
            for line in file.readlines():
                json_string += line.split('#')[0].replace('\n', ' ')
        region_json = json.loads(json_string)

        for region in region_json:
            new_region = Region(region['region_name'])
            new_region.world = self
            if 'dungeon' in region:
                new_region.dungeon = region['dungeon']
            if 'locations' in region:
                for location, rule in region['locations'].items():
                    new_location = LocationFactory(location)
                    new_location.parent_region = new_region
                    if self.logic_rules != 'none':
                        new_location.access_rule = parse_rule_string(
                            rule, self)
                    new_location.world = self
                    new_region.locations.append(new_location)
            if 'exits' in region:
                for exit, rule in region['exits'].items():
                    new_exit = Entrance('%s -> %s' % (new_region.name, exit),
                                        new_region)
                    new_exit.connected_region = exit
                    if self.logic_rules != 'none':
                        new_exit.access_rule = parse_rule_string(rule, self)
                    new_region.exits.append(new_exit)
            self.regions.append(new_region)
Ejemplo n.º 2
0
    def load_regions_from_json(self, file_path):
        json_string = ""
        with io.open(file_path, 'r') as file:
            for line in file.readlines():
                json_string += line.split('#')[0].replace('\n', ' ')
        json_string = re.sub(' +', ' ', json_string)
        try:
            region_json = json.loads(json_string)
        except json.JSONDecodeError as error:
            raise Exception("JSON parse error around text:\n" + \
                            json_string[error.pos-35:error.pos+35] + "\n" + \
                            "                                   ^^\n")

        for region in region_json:
            new_region = Region(region['region_name'])
            new_region.world = self
            if 'scene' in region:
                new_region.scene = region['scene']
            if 'hint' in region:
                new_region.hint = region['hint']
            if 'dungeon' in region:
                new_region.dungeon = region['dungeon']
            if 'time_passes' in region:
                new_region.time_passes = region['time_passes']
                new_region.provides_time = TimeOfDay.ALL
            if new_region.name == 'Ganons Castle Grounds':
                new_region.provides_time = TimeOfDay.DAMPE
            if 'locations' in region:
                for location, rule in region['locations'].items():
                    new_location = LocationFactory(location)
                    new_location.parent_region = new_region
                    new_location.rule_string = rule
                    if self.logic_rules != 'none':
                        self.parser.parse_spot_rule(new_location)
                    new_location.world = self
                    new_region.locations.append(new_location)
            if 'events' in region:
                for event, rule in region['events'].items():
                    # Allow duplicate placement of events
                    lname = '%s from %s' % (event, new_region.name)
                    new_location = Location(lname,
                                            type='Event',
                                            parent=new_region)
                    new_location.rule_string = rule
                    if self.logic_rules != 'none':
                        self.parser.parse_spot_rule(new_location)
                    new_location.world = self
                    new_region.locations.append(new_location)
                    MakeEventItem(event, new_location)
            if 'exits' in region:
                for exit, rule in region['exits'].items():
                    new_exit = Entrance('%s -> %s' % (new_region.name, exit),
                                        new_region)
                    new_exit.connected_region = exit
                    new_exit.rule_string = rule
                    if self.logic_rules != 'none':
                        self.parser.parse_spot_rule(new_exit)
                    new_region.exits.append(new_exit)
            self.regions.append(new_region)
Ejemplo n.º 3
0
    def cloak(self, worlds, location_pools, model_pools):
        for (name, record) in pattern_dict_items(self.locations):
            if record.model is None:
                continue

            player_id = self.id if record.player is None else record.player - 1
            world = worlds[player_id]

            try:
                location = LocationFactory(name)
            except KeyError:
                raise RuntimeError('Unknown location in world %d: %s' %
                                   (world.id + 1, name))
            if location.type == 'Boss':
                continue

            location = pull_item_or_location(location_pools, world, name)
            if location is None:
                raise RuntimeError('Location already cloaked in world %d: %s' %
                                   (self.id + 1, name))
            model = pull_item_or_location(model_pools,
                                          world,
                                          record.model,
                                          remove=False)
            if model is None:
                raise RuntimeError('Unknown model in world %d: %s' %
                                   (self.id + 1, record.model))
            if can_cloak(location.item, model):
                location.item.looks_like_item = model
Ejemplo n.º 4
0
 def fill_bosses(self, world, prize_locs, prizepool):
     count = 0
     for (name, record) in pattern_dict_items(self.locations, prizepool):
         boss = pull_item_or_location([prize_locs], world, name)
         if boss is None:
             try:
                 location = LocationFactory(name)
             except KeyError:
                 raise RuntimeError('Unknown boss in world %d: %s' %
                                    (world.id + 1, name))
             if location.type == 'Boss':
                 raise RuntimeError(
                     'Boss or already placed in world %d: %s' %
                     (world.id + 1, name))
             else:
                 continue
         if record.player is not None and (record.player - 1) != self.id:
             raise RuntimeError(
                 'A boss can only give rewards in its own world')
         reward = pull_item_or_location([prizepool], world, record.item)
         if reward is None:
             if record.item not in item_groups['DungeonReward']:
                 raise RuntimeError(
                     'Cannot place non-dungeon reward %s in world %d on location %s.'
                     % (record.item, self.id + 1, name))
             if IsItem(record.item):
                 raise RuntimeError(
                     'Reward already placed in world %d: %s' %
                     (world.id + 1, record.item))
             else:
                 raise RuntimeError('Reward unknown in world %d: %s' %
                                    (world.id + 1, record.item))
         count += 1
         world.push_item(boss, reward, True)
     return count
Ejemplo n.º 5
0
    def fill(self, window, worlds, location_pools, item_pools):
        world = worlds[self.id]
        for (location_name, record) in pattern_dict_items(self.locations):
            if record.item is None:
                continue

            player_id = self.id if record.player is None else record.player - 1

            location_matcher = lambda loc: loc.world.id == world.id and loc.name == location_name
            location = pull_first_element(location_pools, location_matcher)
            if location is None:
                try:
                    location = LocationFactory(location_name)
                except KeyError:
                    raise RuntimeError('Unknown location in world %d: %s' %
                                       (world.id + 1, name))
                if location.type == 'Boss':
                    continue
                else:
                    raise RuntimeError(
                        'Location already filled in world %d: %s' %
                        (self.id + 1, location_name))

            try:
                item = self.pool_remove_item(item_pools,
                                             record.item,
                                             1,
                                             world_id=player_id)[0]
            except KeyError:
                try:
                    self.pool_remove_item(item_pools,
                                          "#Junk",
                                          1,
                                          world_id=player_id)
                    item_matcher = lambda item: pattern_matcher(record.item)(
                        item.name)
                    item = random.choice(
                        list(ItemIterator(item_matcher, worlds[player_id])))
                except KeyError:
                    raise RuntimeError(
                        'Too many items were added to world %d, and not enough junk is available to be removed.'
                        % (self.id + 1))

            if record.price is not None:
                item.price = record.price
            location.world.push_item(location, item, True)
            if item.advancement:
                playthrough = Playthrough.max_explore(
                    [world.state for world in worlds],
                    itertools.chain.from_iterable(item_pools))
                if not playthrough.can_beat_game(False):
                    raise FillError(
                        '%s in world %d is not reachable without %s in world %d!'
                        %
                        (location.name, self.id + 1, item.name, player_id + 1))
            window.fillcount += 1
            window.update_progress(5 + (
                (window.fillcount / window.locationcount) * 30))
Ejemplo n.º 6
0
 def load_regions_from_json(self, file_path):
     region_json = read_json(file_path)
         
     for region in region_json:
         new_region = Region(region['region_name'])
         new_region.world = self
         if 'scene' in region:
             new_region.scene = region['scene']
         if 'hint' in region:
             new_region.hint = region['hint']
         if 'dungeon' in region:
             new_region.dungeon = region['dungeon']
         if 'time_passes' in region:
             new_region.time_passes = region['time_passes']
             new_region.provides_time = TimeOfDay.ALL
         if new_region.name == 'Ganons Castle Grounds':
             new_region.provides_time = TimeOfDay.DAMPE
         if 'locations' in region:
             for location, rule in region['locations'].items():
                 new_location = LocationFactory(location)
                 new_location.parent_region = new_region
                 new_location.rule_string = rule
                 if self.logic_rules != 'none':
                     self.parser.parse_spot_rule(new_location)
                 if new_location.never:
                     # We still need to fill the location even if ALR is off.
                     logging.getLogger('').debug('Unreachable location: %s', new_location.name)
                 new_location.world = self
                 new_region.locations.append(new_location)
         if 'events' in region:
             for event, rule in region['events'].items():
                 # Allow duplicate placement of events
                 lname = '%s from %s' % (event, new_region.name)
                 new_location = Location(lname, type='Event', parent=new_region)
                 new_location.rule_string = rule
                 if self.logic_rules != 'none':
                     self.parser.parse_spot_rule(new_location)
                 if new_location.never:
                     logging.getLogger('').debug('Dropping unreachable event: %s', new_location.name)
                 else:
                     new_location.world = self
                     new_region.locations.append(new_location)
                     MakeEventItem(event, new_location)
         if 'exits' in region:
             for exit, rule in region['exits'].items():
                 new_exit = Entrance('%s -> %s' % (new_region.name, exit), new_region)
                 new_exit.connected_region = exit
                 new_exit.rule_string = rule
                 if self.logic_rules != 'none':
                     self.parser.parse_spot_rule(new_exit)
                 if new_exit.never:
                     logging.getLogger('').debug('Dropping unreachable exit: %s', new_exit.name)
                 else:
                     new_region.exits.append(new_exit)
         self.regions.append(new_region)
Ejemplo n.º 7
0
    def load_regions_from_json(self, file_path):
        json_string = ""
        with io.open(file_path, 'r') as file:
            for line in file.readlines():
                json_string += line.split('#')[0].replace('\n', ' ')
        json_string = re.sub(' +', ' ', json_string)
        try:
            region_json = json.loads(json_string)
        except json.JSONDecodeError as error:
            raise Exception("JSON parse error around text:\n" + \
                            json_string[error.pos-35:error.pos+35] + "\n" + \
                            "                                   ^^\n")

        for region in region_json:
            new_region = Region(region['region_name'])
            new_region.world = self
            if 'scene' in region:
                new_region.scene = region['scene']
            if 'hint' in region:
                new_region.hint = region['hint']
            if 'dungeon' in region:
                new_region.dungeon = region['dungeon']
            if 'time_passes' in region:
                new_region.time_passes = region['time_passes']
            if 'locations' in region:
                for location, rule in region['locations'].items():
                    new_location = LocationFactory(location)
                    new_location.parent_region = new_region
                    if self.logic_rules != 'none':
                        new_location.access_rule = parse_rule_string(
                            rule, self)
                    new_location.world = self
                    new_region.locations.append(new_location)
            if 'exits' in region:
                for exit, rule in region['exits'].items():
                    new_exit = Entrance('%s -> %s' % (new_region.name, exit),
                                        new_region)
                    new_exit.connected_region = exit
                    if self.logic_rules != 'none':
                        new_exit.access_rule = parse_rule_string(rule, self)
                    new_region.exits.append(new_exit)
            self.regions.append(new_region)
Ejemplo n.º 8
0
    def fill(self, window, worlds, location_pools, item_pools):
        world = worlds[self.id]
        locations = {}
        if self.locations:
            locations = {
                loc: self.locations[loc]
                for loc in random.sample(self.locations.keys(),
                                         len(self.locations))
            }
        for starting_item in self.starting_items:
            for _ in range(self.starting_items[starting_item].count):
                try:
                    if starting_item in item_groups['DungeonReward']:
                        continue
                    item = None
                    if starting_item in item_groups['Bottle']:
                        item = self.pool_replace_item(item_pools, "#Bottle",
                                                      self.id, "#Junk", worlds)
                    elif starting_item in item_groups['AdultTrade']:
                        item = self.pool_replace_item(item_pools,
                                                      "#AdultTrade", self.id,
                                                      "#Junk", worlds)
                    elif IsItem(starting_item):
                        try:
                            item = self.pool_replace_item(
                                item_pools, starting_item, self.id, "#Junk",
                                worlds)
                        except KeyError:
                            pass  # If a normal item exceeds the item pool count, continue.
                except KeyError:
                    raise RuntimeError(
                        'Started with too many "%s" in world %d, and not enough "%s" are available in the item pool to be removed.'
                        % (starting_item, self.id + 1, starting_item))

                if starting_item in item_groups['Song']:
                    self.song_as_items = True

                # Update item_pool
                if item is not None:
                    if item not in self.item_pool:
                        self.item_pool[item.name] = ItemPoolRecord({
                            'type': 'set',
                            'count': 1
                        })
                    else:
                        self.item_pool[item.name].count += 1
                    item_pools[5].append(ItemFactory(item.name, world))
        for (location_name,
             record) in pattern_dict_items(locations, world.itempool, []):
            if record.item is None:
                continue

            player_id = self.id if record.player is None else record.player - 1

            location_matcher = lambda loc: loc.world.id == world.id and loc.name == location_name
            location = pull_first_element(location_pools, location_matcher)
            if location is None:
                try:
                    location = LocationFactory(location_name)
                except KeyError:
                    raise RuntimeError('Unknown location in world %d: %s' %
                                       (world.id + 1, location_name))
                if location.type == 'Boss':
                    continue
                elif location.name in world.disabled_locations:
                    continue
                else:
                    raise RuntimeError(
                        'Location already filled in world %d: %s' %
                        (self.id + 1, location_name))

            if record.item in item_groups['DungeonReward']:
                raise RuntimeError(
                    'Cannot place dungeon reward %s in world %d in location %s.'
                    % (record.item, self.id + 1, location_name))

            if record.item == '#Junk' and location.type == 'Song' and not world.shuffle_song_items:
                record.item = '#JunkSong'

            ignore_pools = None
            is_invert = pattern_matcher(record.item)('!')
            if is_invert and location.type != 'Song' and not world.shuffle_song_items:
                ignore_pools = [2]
            if is_invert and location.type == 'Song' and not world.shuffle_song_items:
                ignore_pools = [i for i in range(len(item_pools)) if i != 2]

            try:
                item = self.pool_remove_item(item_pools,
                                             record.item,
                                             1,
                                             world_id=player_id,
                                             ignore_pools=ignore_pools)[0]
            except KeyError:
                if location.type == 'Shop' and "Buy" in record.item:
                    try:
                        self.pool_remove_item([item_pools[0]],
                                              "Buy *",
                                              1,
                                              world_id=player_id)
                        item = ItemFactory([record.item], world=world)[0]
                    except KeyError:
                        raise RuntimeError(
                            'Too many shop buy items were added to world %d, and not enough shop buy items are available in the item pool to be removed.'
                            % (self.id + 1))
                elif record.item in item_groups['Bottle']:
                    try:
                        item = self.pool_replace_item(item_pools, "#Bottle",
                                                      player_id, record.item,
                                                      worlds)
                    except KeyError:
                        raise RuntimeError(
                            'Too many bottles were added to world %d, and not enough bottles are available in the item pool to be removed.'
                            % (self.id + 1))
                elif record.item in item_groups['AdultTrade']:
                    try:
                        item = self.pool_replace_item(item_pools,
                                                      "#AdultTrade", player_id,
                                                      record.item, worlds)
                    except KeyError:
                        raise RuntimeError(
                            'Too many adult trade items were added to world %d, and not enough adult trade items are available in the item pool to be removed.'
                            % (self.id + 1))
                else:
                    try:
                        item = self.pool_replace_item(item_pools, "#Junk",
                                                      player_id, record.item,
                                                      worlds)
                    except KeyError:
                        raise RuntimeError(
                            'Too many items were added to world %d, and not enough junk is available to be removed.'
                            % (self.id + 1))
                # Update item_pool
                if item.name not in self.item_pool:
                    self.item_pool[item.name] = ItemPoolRecord({
                        'type': 'set',
                        'count': 1
                    })
                else:
                    self.item_pool[item.name].count += 1
            except IndexError:
                raise RuntimeError(
                    'Unknown item %s being placed on location %s in world %d.'
                    % (record.item, location, self.id + 1))

            if record.price is not None and item.type != 'Shop':
                location.price = record.price
                world.shop_prices[location.name] = record.price

            if location.type == 'Song' and item.type != 'Song':
                self.song_as_items = True
            location.world.push_item(location, item, True)

            if item.advancement:
                search = Search.max_explore(
                    [world.state for world in worlds],
                    itertools.chain.from_iterable(item_pools))
                if not search.can_beat_game(False):
                    raise FillError(
                        '%s in world %d is not reachable without %s in world %d!'
                        %
                        (location.name, self.id + 1, item.name, player_id + 1))
            window.fillcount += 1
            window.update_progress(5 + (
                (window.fillcount / window.locationcount) * 30))
Ejemplo n.º 9
0
    def fill(self, window, worlds, location_pools, item_pools):
        """Fills the world with restrictions defined in a plandomizer JSON file.

        :param window:
        :param worlds: A list of the world objects that define the rules of each game world.
        :param location_pools: A list containing all of the location pools.
            0: Shop Locations
            1: Song Locations
            2: Fill locations
        :param item_pools: A list containing all of the item pools.
            0: Shop Items
            1: Dungeon Items
            2: Songs
            3: Progression Items
            4: Priority Items
            5: The rest of the Item pool
        """
        world = worlds[self.id]
        locations = {}
        if self.locations:
            locations = {loc: self.locations[loc] for loc in random.sample(self.locations.keys(), len(self.locations))}
        used_items = []
        for (location_name, record) in pattern_dict_items(locations, world.itempool, used_items):
            if record.item is None:
                continue

            player_id = self.id if record.player is None else record.player - 1

            location_matcher = lambda loc: loc.world.id == world.id and loc.name.lower() == location_name.lower()
            location = pull_first_element(location_pools, location_matcher)
            if location is None:
                try:
                    location = LocationFactory(location_name)
                except KeyError:
                    raise RuntimeError('Unknown location in world %d: %s' % (world.id + 1, location_name))
                if location.type == 'Boss':
                    continue
                elif location.name in world.disabled_locations:
                    continue
                else:
                    raise RuntimeError('Location already filled in world %d: %s' % (self.id + 1, location_name))

            if record.item in item_groups['DungeonReward']:
                raise RuntimeError('Cannot place dungeon reward %s in world %d in location %s.' % (record.item, self.id + 1, location_name))

            if record.item == '#Junk' and location.type == 'Song' and not world.shuffle_song_items:
                record.item = '#JunkSong'

            ignore_pools = None
            is_invert = pattern_matcher(record.item)('!')
            if is_invert and location.type != 'Song' and not world.shuffle_song_items:
                ignore_pools = [2]
            if is_invert and location.type == 'Song' and not world.shuffle_song_items:
                ignore_pools = [i for i in range(len(item_pools)) if i != 2]

            try:
                if record.item == "#Bottle":
                    try:
                        item = self.pool_replace_item(item_pools, "#Bottle", player_id, record.item, worlds)
                        # Update item_pool
                        if item.name not in self.item_pool:
                            self.item_pool[item.name] = ItemPoolRecord()
                        else:
                            self.item_pool[item.name].count += 1
                    except KeyError:
                        raise RuntimeError(
                            'Too many bottles were added to world %d, and not enough bottles are available in the item pool to be removed.' % (
                                        self.id + 1))
                elif record.item == "#AdultTrade":
                    try:
                        item = self.pool_replace_item(item_pools, "#AdultTrade", player_id, record.item, worlds)
                        # Update item_pool
                        if item.name not in self.item_pool:
                            self.item_pool[item.name] = ItemPoolRecord()
                        else:
                            self.item_pool[item.name].count += 1
                    except KeyError:
                        raise RuntimeError(
                            'Too many adult trade items were added to world %d, and not enough adult trade items are available in the item pool to be removed.' % (
                                        self.id + 1))
                else:
                    item = self.pool_remove_item(item_pools, record.item, 1, world_id=player_id, ignore_pools=ignore_pools)[0]
            except KeyError:
                if location.type == 'Shop' and "Buy" in record.item:
                    try:
                        self.pool_remove_item([item_pools[0]], "Buy *", 1, world_id=player_id)
                        item = ItemFactory([record.item], world=world)[0]
                    except KeyError:
                        raise RuntimeError('Too many shop buy items were added to world %d, and not enough shop buy items are available in the item pool to be removed.' % (self.id + 1))
                elif record.item in item_groups['Bottle']:
                    try:
                        item = self.pool_replace_item(item_pools, "#Bottle", player_id, record.item, worlds)
                    except KeyError:
                        raise RuntimeError('Too many bottles were added to world %d, and not enough bottles are available in the item pool to be removed.' % (self.id + 1))
                elif record.item in item_groups['AdultTrade']:
                    try:
                        item = self.pool_replace_item(item_pools, "#AdultTrade", player_id, record.item, worlds)
                    except KeyError:
                        raise RuntimeError('Too many adult trade items were added to world %d, and not enough adult trade items are available in the item pool to be removed.' % (self.id + 1))
                elif record.item == "Weird Egg":
                    # If Letter has not been shown to guard before obtaining a second weird egg a softlock can occur
                    # if there are important items at deku theater or an important location locked behind the gate
                    # or if Keaton Mask gets overwritten before giving it to the guard.
                    try:
                        item = self.pool_replace_item(item_pools, "Weird Egg", player_id, record.item, worlds)
                    except KeyError:
                        raise RuntimeError('Weird Egg already placed in World %d.' % (self.id + 1))
                else:
                    try:
                        item = self.pool_replace_item(item_pools, "#Junk", player_id, record.item, worlds)
                    except KeyError:
                        raise RuntimeError('Too many items were added to world %d, and not enough junk is available to be removed.' % (self.id + 1))
                # Update item_pool
                if item.name not in self.item_pool:
                    self.item_pool[item.name] = ItemPoolRecord()
                else:
                    self.item_pool[item.name].count += 1
            except IndexError:
                raise RuntimeError('Unknown item %s being placed on location %s in world %d.' % (record.item, location, self.id + 1))

            if record.price is not None and item.type != 'Shop':
                location.price = record.price
                world.shop_prices[location.name] = record.price

            if location.type == 'Song' and item.type != 'Song':
                self.song_as_items = True
            location.world.push_item(location, item, True)

            if item.advancement:
                search = Search.max_explore([world.state for world in worlds], itertools.chain.from_iterable(item_pools))
                if not search.can_beat_game(False):
                    raise FillError('%s in world %d is not reachable without %s in world %d!' % (location.name, self.id + 1, item.name, player_id + 1))
            window.fillcount += 1
            window.update_progress(5 + ((window.fillcount / window.locationcount) * 30))