Example #1
0
    def create_delayed_rules(self):
        for region_name, node, subrule_name in self.delayed_rules:
            region = self.world.get_region(region_name)
            event = Location(subrule_name,
                             type='Event',
                             parent=region,
                             internal=True)
            event.world = self.world

            self.current_spot = event
            # This could, in theory, create further subrules.
            access_rule = self.make_access_rule(self.visit(node))
            if access_rule is self.rule_cache.get('NameConstant(False)'):
                event.access_rule = None
                event.never = True
                logging.getLogger('').debug(
                    'Dropping unreachable delayed event: %s', event.name)
            else:
                if access_rule is self.rule_cache.get('NameConstant(True)'):
                    event.always = True
                event.set_rule(access_rule)
                region.locations.append(event)

                MakeEventItem(subrule_name, event)
        # Safeguard in case this is called multiple times per world
        self.delayed_rules.clear()
Example #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)
Example #3
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)
Example #4
0
    def create_delayed_rules(self):
        for region_name, node, subrule_name in self.delayed_rules:
            region = self.world.get_region(region_name)
            event = Location(subrule_name, type='Event', parent=region, internal=True)
            event.world = self.world

            self.current_spot = event
            # This could, in theory, create further subrules.
            event.access_rule = self.make_access_rule(self.visit(node))
            event.set_rule(event.access_rule)
            region.locations.append(event)

            MakeEventItem(subrule_name, event)
        # Safeguard in case this is called multiple times per world
        self.delayed_rules.clear()
Example #5
0
def add_hint(spoiler,
             world,
             IDs,
             gossip_text,
             count,
             location=None,
             force_reachable=False):
    random.shuffle(IDs)
    skipped_ids = []
    first = True
    success = True
    while random.random() < count:
        if IDs:
            id = IDs.pop(0)

            if gossipLocations[id].reachable:
                stone_name = gossipLocations[id].location
                stone_location = world.get_location(stone_name)
                if not first or can_reach_stone(spoiler.worlds, stone_location,
                                                location):
                    if first and location:
                        # just name the event item after the gossip stone directly
                        MakeEventItem(stone_name, stone_location)
                        # This mostly guarantees that we don't lock the player out of an item hint
                        # by establishing a (hint -> item) -> hint -> item -> (first hint) loop
                        location.add_rule(
                            world.parser.parse_rule(repr(stone_name)))

                    count -= 1
                    first = False
                    spoiler.hints[world.id][id] = gossip_text
                else:
                    skipped_ids.append(id)
            else:
                if not force_reachable:
                    # The stones are not readable at all in logic, so we ignore any kind of logic here
                    count -= 1
                    spoiler.hints[world.id][id] = gossip_text
                else:
                    # If flagged to guarantee reachable, then skip
                    # If no stones are reachable, then this will place nothing
                    skipped_ids.append(id)
        else:
            success = False
            break
    IDs.extend(skipped_ids)
    return success
Example #6
0
def add_hint(spoiler,
             world,
             IDs,
             gossip_text,
             count,
             location=None,
             force_reachable=False):
    random.shuffle(IDs)
    skipped_ids = []
    duplicates = []
    first = True
    success = True
    # early failure if not enough
    if len(IDs) < int(count):
        return False
    # Randomly round up, if we have enough IDs left
    total = int(random.random() + count) if len(IDs) > count else int(count)
    while total:
        if IDs:
            id = IDs.pop(0)

            if gossipLocations[id].reachable:
                stone_name = gossipLocations[id].location
                stone_location = world.get_location(stone_name)
                if not first or can_reach_stone(spoiler.worlds, stone_location,
                                                location):
                    if first and location:
                        # just name the event item after the gossip stone directly
                        MakeEventItem(stone_name, stone_location)
                        # This mostly guarantees that we don't lock the player out of an item hint
                        # by establishing a (hint -> item) -> hint -> item -> (first hint) loop
                        location.add_rule(
                            world.parser.parse_rule(repr(stone_name)))

                    total -= 1
                    first = False
                    spoiler.hints[world.id][id] = gossip_text
                    # Immediately start choosing duplicates from stones we passed up earlier
                    while duplicates and total:
                        id = duplicates.pop(0)
                        total -= 1
                        spoiler.hints[world.id][id] = gossip_text
                else:
                    # Temporarily skip this stone but consider it for duplicates
                    duplicates.append(id)
            else:
                if not force_reachable:
                    # The stones are not readable at all in logic, so we ignore any kind of logic here
                    if not first:
                        total -= 1
                        spoiler.hints[world.id][id] = gossip_text
                    else:
                        # Temporarily skip this stone but consider it for duplicates
                        duplicates.append(id)
                else:
                    # If flagged to guarantee reachable, then skip
                    # If no stones are reachable, then this will place nothing
                    skipped_ids.append(id)
        else:
            # Out of IDs
            if not force_reachable and len(duplicates) >= total:
                # Didn't find any appropriate stones for this hint, but maybe enough completely unreachable ones.
                # We'd rather not use reachable stones for this.
                unr = [
                    id for id in duplicates
                    if not gossipLocations[id].reachable
                ]
                if len(unr) >= total:
                    duplicates = [
                        id for id in duplicates if id not in unr[:total]
                    ]
                    for id in unr[:total]:
                        spoiler.hints[world.id][id] = gossip_text
                    # Success
                    break
            # Failure
            success = False
            break
    IDs.extend(duplicates)
    IDs.extend(skipped_ids)
    return success
Example #7
0
def add_hint(spoiler, world, groups, gossip_text, count, location=None, force_reachable=False):
    random.shuffle(groups)
    skipped_groups = []
    duplicates = []
    first = True
    success = True
    # early failure if not enough
    if len(groups) < int(count):
        return False
    # Randomly round up, if we have enough groups left
    total = int(random.random() + count) if len(groups) > count else int(count)
    while total:
        if groups:
            group = groups.pop(0)

            if any(map(lambda id: gossipLocations[id].reachable, group)):
                stone_names = [gossipLocations[id].location for id in group]
                stone_locations = [world.get_location(stone_name) for stone_name in stone_names]
                if not first or any(map(lambda stone_location: can_reach_hint(spoiler.worlds, stone_location, location), stone_locations)):
                    if first and location:
                        # just name the event item after the gossip stone directly
                        event_item = None
                        for i, stone_name in enumerate(stone_names):
                            # place the same event item in each location in the group
                            if event_item is None:
                                event_item = MakeEventItem(stone_name, stone_locations[i], event_item)
                            else:
                                MakeEventItem(stone_name, stone_locations[i], event_item)

                        # This mostly guarantees that we don't lock the player out of an item hint
                        # by establishing a (hint -> item) -> hint -> item -> (first hint) loop
                        location.add_rule(world.parser.parse_rule(repr(event_item.name)))

                    total -= 1
                    first = False
                    for id in group:
                        spoiler.hints[world.id][id] = gossip_text
                    # Immediately start choosing duplicates from stones we passed up earlier
                    while duplicates and total:
                        group = duplicates.pop(0)
                        total -= 1
                        for id in group:
                            spoiler.hints[world.id][id] = gossip_text
                else:
                    # Temporarily skip this stone but consider it for duplicates
                    duplicates.append(group)
            else:
                if not force_reachable:
                    # The stones are not readable at all in logic, so we ignore any kind of logic here
                    if not first:
                        total -= 1
                        for id in group:
                            spoiler.hints[world.id][id] = gossip_text
                    else:
                        # Temporarily skip this stone but consider it for duplicates
                        duplicates.append(group)
                else:
                    # If flagged to guarantee reachable, then skip
                    # If no stones are reachable, then this will place nothing
                    skipped_groups.append(group)
        else:
            # Out of groups
            if not force_reachable and len(duplicates) >= total:
                # Didn't find any appropriate stones for this hint, but maybe enough completely unreachable ones.
                # We'd rather not use reachable stones for this.
                unr = [group for group in duplicates if all(map(lambda id: not gossipLocations[id].reachable, group))]
                if len(unr) >= total:
                    duplicates = [group for group in duplicates if group not in unr[:total]]
                    for group in unr[:total]:
                        for id in group:
                            spoiler.hints[world.id][id] = gossip_text
                    # Success
                    break
            # Failure
            success = False
            break
    groups.extend(duplicates)
    groups.extend(skipped_groups)
    return success
Example #8
0
def stone_reachability(stone_name, stone_location):
    # just name the event item after the gossip stone directly
    MakeEventItem(stone_name, stone_location)

    return lambda state, **kwargs: state.has(stone_name)