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