Core: rename `world` to `multiworld` (#931)

* rename references to `Multiworld` in core to `multiworld` instead of `world`

* fix smz3

* fix oot

* fix low hanging fruit

* revert mysteriously broken spacing in world api.md

* fix more randomly broken spacing

* hate

* that better be all of it

* begrudgingly move over smw

* ._.

* missed some worlds

* this is getting tedious now

* Missed some self.world definitions

Co-authored-by: espeon65536 <espeon65536@gmail.com>
Co-authored-by: Zach Parks <zach@alliware.com>
This commit is contained in:
alwaysintreble 2022-10-31 21:41:21 -05:00 committed by GitHub
parent 87f4a97f1e
commit 2af510328e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 1623 additions and 1621 deletions

View File

@ -312,7 +312,7 @@ class MultiWorld():
def initialize_regions(self, regions=None): def initialize_regions(self, regions=None):
for region in regions if regions else self.regions: for region in regions if regions else self.regions:
region.world = self region.multiworld = self
self._region_cache[region.player][region.name] = region self._region_cache[region.player][region.name] = region
@functools.cached_property @functools.cached_property
@ -602,7 +602,7 @@ PathValue = Tuple[str, Optional["PathValue"]]
class CollectionState(): class CollectionState():
prog_items: typing.Counter[Tuple[str, int]] prog_items: typing.Counter[Tuple[str, int]]
world: MultiWorld multiworld: MultiWorld
reachable_regions: Dict[int, Set[Region]] reachable_regions: Dict[int, Set[Region]]
blocked_connections: Dict[int, Set[Entrance]] blocked_connections: Dict[int, Set[Entrance]]
events: Set[Location] events: Set[Location]
@ -614,7 +614,7 @@ class CollectionState():
def __init__(self, parent: MultiWorld): def __init__(self, parent: MultiWorld):
self.prog_items = Counter() self.prog_items = Counter()
self.world = parent self.multiworld = parent
self.reachable_regions = {player: set() for player in parent.get_all_ids()} self.reachable_regions = {player: set() for player in parent.get_all_ids()}
self.blocked_connections = {player: set() for player in parent.get_all_ids()} self.blocked_connections = {player: set() for player in parent.get_all_ids()}
self.events = set() self.events = set()
@ -632,7 +632,7 @@ class CollectionState():
rrp = self.reachable_regions[player] rrp = self.reachable_regions[player]
bc = self.blocked_connections[player] bc = self.blocked_connections[player]
queue = deque(self.blocked_connections[player]) queue = deque(self.blocked_connections[player])
start = self.world.get_region('Menu', player) start = self.multiworld.get_region('Menu', player)
# init on first call - this can't be done on construction since the regions don't exist yet # init on first call - this can't be done on construction since the regions don't exist yet
if start not in rrp: if start not in rrp:
@ -655,12 +655,12 @@ class CollectionState():
self.path[new_region] = (new_region.name, self.path.get(connection, None)) self.path[new_region] = (new_region.name, self.path.get(connection, None))
# Retry connections if the new region can unblock them # Retry connections if the new region can unblock them
for new_entrance in self.world.indirect_connections.get(new_region, set()): for new_entrance in self.multiworld.indirect_connections.get(new_region, set()):
if new_entrance in bc and new_entrance not in queue: if new_entrance in bc and new_entrance not in queue:
queue.append(new_entrance) queue.append(new_entrance)
def copy(self) -> CollectionState: def copy(self) -> CollectionState:
ret = CollectionState(self.world) ret = CollectionState(self.multiworld)
ret.prog_items = self.prog_items.copy() ret.prog_items = self.prog_items.copy()
ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in
self.reachable_regions} self.reachable_regions}
@ -681,17 +681,17 @@ class CollectionState():
assert isinstance(player, int), "can_reach: player is required if spot is str" assert isinstance(player, int), "can_reach: player is required if spot is str"
# try to resolve a name # try to resolve a name
if resolution_hint == 'Location': if resolution_hint == 'Location':
spot = self.world.get_location(spot, player) spot = self.multiworld.get_location(spot, player)
elif resolution_hint == 'Entrance': elif resolution_hint == 'Entrance':
spot = self.world.get_entrance(spot, player) spot = self.multiworld.get_entrance(spot, player)
else: else:
# default to Region # default to Region
spot = self.world.get_region(spot, player) spot = self.multiworld.get_region(spot, player)
return spot.can_reach(self) return spot.can_reach(self)
def sweep_for_events(self, key_only: bool = False, locations: Optional[Iterable[Location]] = None) -> None: def sweep_for_events(self, key_only: bool = False, locations: Optional[Iterable[Location]] = None) -> None:
if locations is None: if locations is None:
locations = self.world.get_filled_locations() locations = self.multiworld.get_filled_locations()
reachable_events = True reachable_events = True
# since the loop has a good chance to run more than once, only filter the events once # since the loop has a good chance to run more than once, only filter the events once
locations = {location for location in locations if location.event and location not in self.events and locations = {location for location in locations if location.event and location not in self.events and
@ -718,7 +718,7 @@ class CollectionState():
def has_group(self, item_name_group: str, player: int, count: int = 1) -> bool: def has_group(self, item_name_group: str, player: int, count: int = 1) -> bool:
found: int = 0 found: int = 0
for item_name in self.world.worlds[player].item_name_groups[item_name_group]: for item_name in self.multiworld.worlds[player].item_name_groups[item_name_group]:
found += self.prog_items[item_name, player] found += self.prog_items[item_name, player]
if found >= count: if found >= count:
return True return True
@ -726,17 +726,17 @@ class CollectionState():
def count_group(self, item_name_group: str, player: int) -> int: def count_group(self, item_name_group: str, player: int) -> int:
found: int = 0 found: int = 0
for item_name in self.world.worlds[player].item_name_groups[item_name_group]: for item_name in self.multiworld.worlds[player].item_name_groups[item_name_group]:
found += self.prog_items[item_name, player] found += self.prog_items[item_name, player]
return found return found
def can_buy_unlimited(self, item: str, player: int) -> bool: def can_buy_unlimited(self, item: str, player: int) -> bool:
return any(shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self) for return any(shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self) for
shop in self.world.shops) shop in self.multiworld.shops)
def can_buy(self, item: str, player: int) -> bool: def can_buy(self, item: str, player: int) -> bool:
return any(shop.region.player == player and shop.has(item) and shop.region.can_reach(self) for return any(shop.region.player == player and shop.has(item) and shop.region.can_reach(self) for
shop in self.world.shops) shop in self.multiworld.shops)
def item_count(self, item: str, player: int) -> int: def item_count(self, item: str, player: int) -> int:
return self.prog_items[item, player] return self.prog_items[item, player]
@ -756,7 +756,7 @@ class CollectionState():
return self.has('Power Glove', player) or self.has('Titans Mitts', player) return self.has('Power Glove', player) or self.has('Titans Mitts', player)
def bottle_count(self, player: int) -> int: def bottle_count(self, player: int) -> int:
return min(self.world.difficulty_requirements[player].progressive_bottle_limit, return min(self.multiworld.difficulty_requirements[player].progressive_bottle_limit,
self.count_group("Bottles", player)) self.count_group("Bottles", player))
def has_hearts(self, player: int, count: int) -> int: def has_hearts(self, player: int, count: int) -> int:
@ -765,7 +765,7 @@ class CollectionState():
def heart_count(self, player: int) -> int: def heart_count(self, player: int) -> int:
# Warning: This only considers items that are marked as advancement items # Warning: This only considers items that are marked as advancement items
diff = self.world.difficulty_requirements[player] diff = self.multiworld.difficulty_requirements[player]
return min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) \ return min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) \
+ self.item_count('Sanctuary Heart Container', player) \ + self.item_count('Sanctuary Heart Container', player) \
+ min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 \ + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 \
@ -782,9 +782,9 @@ class CollectionState():
elif self.has('Magic Upgrade (1/2)', player): elif self.has('Magic Upgrade (1/2)', player):
basemagic = 16 basemagic = 16
if self.can_buy_unlimited('Green Potion', player) or self.can_buy_unlimited('Blue Potion', player): if self.can_buy_unlimited('Green Potion', player) or self.can_buy_unlimited('Blue Potion', player):
if self.world.item_functionality[player] == 'hard' and not fullrefill: if self.multiworld.item_functionality[player] == 'hard' and not fullrefill:
basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count(player)) basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count(player))
elif self.world.item_functionality[player] == 'expert' and not fullrefill: elif self.multiworld.item_functionality[player] == 'expert' and not fullrefill:
basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count(player)) basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count(player))
else: else:
basemagic = basemagic + basemagic * self.bottle_count(player) basemagic = basemagic + basemagic * self.bottle_count(player)
@ -799,12 +799,12 @@ class CollectionState():
or (self.has('Bombs (10)', player) and enemies < 6)) or (self.has('Bombs (10)', player) and enemies < 6))
def can_shoot_arrows(self, player: int) -> bool: def can_shoot_arrows(self, player: int) -> bool:
if self.world.retro_bow[player]: if self.multiworld.retro_bow[player]:
return (self.has('Bow', player) or self.has('Silver Bow', player)) and self.can_buy('Single Arrow', player) return (self.has('Bow', player) or self.has('Silver Bow', player)) and self.can_buy('Single Arrow', player)
return self.has('Bow', player) or self.has('Silver Bow', player) return self.has('Bow', player) or self.has('Silver Bow', player)
def can_get_good_bee(self, player: int) -> bool: def can_get_good_bee(self, player: int) -> bool:
cave = self.world.get_region('Good Bee Cave', player) cave = self.multiworld.get_region('Good Bee Cave', player)
return ( return (
self.has_group("Bottles", player) and self.has_group("Bottles", player) and
self.has('Bug Catching Net', player) and self.has('Bug Catching Net', player) and
@ -815,7 +815,7 @@ class CollectionState():
def can_retrieve_tablet(self, player: int) -> bool: def can_retrieve_tablet(self, player: int) -> bool:
return self.has('Book of Mudora', player) and (self.has_beam_sword(player) or return self.has('Book of Mudora', player) and (self.has_beam_sword(player) or
(self.world.swordless[player] and (self.multiworld.swordless[player] and
self.has("Hammer", player))) self.has("Hammer", player)))
def has_sword(self, player: int) -> bool: def has_sword(self, player: int) -> bool:
@ -837,7 +837,7 @@ class CollectionState():
def can_melt_things(self, player: int) -> bool: def can_melt_things(self, player: int) -> bool:
return self.has('Fire Rod', player) or \ return self.has('Fire Rod', player) or \
(self.has('Bombos', player) and (self.has('Bombos', player) and
(self.world.swordless[player] or (self.multiworld.swordless[player] or
self.has_sword(player))) self.has_sword(player)))
def can_avoid_lasers(self, player: int) -> bool: def can_avoid_lasers(self, player: int) -> bool:
@ -847,7 +847,7 @@ class CollectionState():
if self.has('Moon Pearl', player): if self.has('Moon Pearl', player):
return True return True
return region.is_light_world if self.world.mode[player] != 'inverted' else region.is_dark_world return region.is_light_world if self.multiworld.mode[player] != 'inverted' else region.is_dark_world
def can_reach_light_world(self, player: int) -> bool: def can_reach_light_world(self, player: int) -> bool:
if True in [i.is_light_world for i in self.reachable_regions[player]]: if True in [i.is_light_world for i in self.reachable_regions[player]]:
@ -860,24 +860,24 @@ class CollectionState():
return False return False
def has_misery_mire_medallion(self, player: int) -> bool: def has_misery_mire_medallion(self, player: int) -> bool:
return self.has(self.world.required_medallions[player][0], player) return self.has(self.multiworld.required_medallions[player][0], player)
def has_turtle_rock_medallion(self, player: int) -> bool: def has_turtle_rock_medallion(self, player: int) -> bool:
return self.has(self.world.required_medallions[player][1], player) return self.has(self.multiworld.required_medallions[player][1], player)
def can_boots_clip_lw(self, player: int) -> bool: def can_boots_clip_lw(self, player: int) -> bool:
if self.world.mode[player] == 'inverted': if self.multiworld.mode[player] == 'inverted':
return self.has('Pegasus Boots', player) and self.has('Moon Pearl', player) return self.has('Pegasus Boots', player) and self.has('Moon Pearl', player)
return self.has('Pegasus Boots', player) return self.has('Pegasus Boots', player)
def can_boots_clip_dw(self, player: int) -> bool: def can_boots_clip_dw(self, player: int) -> bool:
if self.world.mode[player] != 'inverted': if self.multiworld.mode[player] != 'inverted':
return self.has('Pegasus Boots', player) and self.has('Moon Pearl', player) return self.has('Pegasus Boots', player) and self.has('Moon Pearl', player)
return self.has('Pegasus Boots', player) return self.has('Pegasus Boots', player)
def can_get_glitched_speed_lw(self, player: int) -> bool: def can_get_glitched_speed_lw(self, player: int) -> bool:
rules = [self.has('Pegasus Boots', player), any([self.has('Hookshot', player), self.has_sword(player)])] rules = [self.has('Pegasus Boots', player), any([self.has('Hookshot', player), self.has_sword(player)])]
if self.world.mode[player] == 'inverted': if self.multiworld.mode[player] == 'inverted':
rules.append(self.has('Moon Pearl', player)) rules.append(self.has('Moon Pearl', player))
return all(rules) return all(rules)
@ -886,7 +886,7 @@ class CollectionState():
def can_get_glitched_speed_dw(self, player: int) -> bool: def can_get_glitched_speed_dw(self, player: int) -> bool:
rules = [self.has('Pegasus Boots', player), any([self.has('Hookshot', player), self.has_sword(player)])] rules = [self.has('Pegasus Boots', player), any([self.has('Hookshot', player), self.has_sword(player)])]
if self.world.mode[player] != 'inverted': if self.multiworld.mode[player] != 'inverted':
rules.append(self.has('Moon Pearl', player)) rules.append(self.has('Moon Pearl', player))
return all(rules) return all(rules)
@ -897,7 +897,7 @@ class CollectionState():
if location: if location:
self.locations_checked.add(location) self.locations_checked.add(location)
changed = self.world.worlds[item.player].collect(self, item) changed = self.multiworld.worlds[item.player].collect(self, item)
if not changed and event: if not changed and event:
self.prog_items[item.name, item.player] += 1 self.prog_items[item.name, item.player] += 1
@ -911,7 +911,7 @@ class CollectionState():
return changed return changed
def remove(self, item: Item): def remove(self, item: Item):
changed = self.world.worlds[item.player].remove(self, item) changed = self.multiworld.worlds[item.player].remove(self, item)
if changed: if changed:
# invalidate caches, nothing can be trusted anymore now # invalidate caches, nothing can be trusted anymore now
self.reachable_regions[item.player] = set() self.reachable_regions[item.player] = set()
@ -938,7 +938,7 @@ class Region:
type: RegionType type: RegionType
hint_text: str hint_text: str
player: int player: int
world: Optional[MultiWorld] multiworld: Optional[MultiWorld]
entrances: List[Entrance] entrances: List[Entrance]
exits: List[Entrance] exits: List[Entrance]
locations: List[Location] locations: List[Location]
@ -956,7 +956,7 @@ class Region:
self.entrances = [] self.entrances = []
self.exits = [] self.exits = []
self.locations = [] self.locations = []
self.world = world self.multiworld = world
self.hint_text = hint self.hint_text = hint
self.player = player self.player = player
@ -984,7 +984,7 @@ class Region:
return self.__str__() return self.__str__()
def __str__(self): def __str__(self):
return self.world.get_name_string_for_object(self) if self.world else f'{self.name} (Player {self.player})' return self.multiworld.get_name_string_for_object(self) if self.multiworld else f'{self.name} (Player {self.player})'
class Entrance: class Entrance:
@ -1021,7 +1021,7 @@ class Entrance:
return self.__str__() return self.__str__()
def __str__(self): def __str__(self):
world = self.parent_region.world if self.parent_region else None world = self.parent_region.multiworld if self.parent_region else None
return world.get_name_string_for_object(self) if world else f'{self.name} (Player {self.player})' return world.get_name_string_for_object(self) if world else f'{self.name} (Player {self.player})'
@ -1035,7 +1035,7 @@ class Dungeon(object):
self.dungeon_items = dungeon_items self.dungeon_items = dungeon_items
self.bosses = dict() self.bosses = dict()
self.player = player self.player = player
self.world = None self.multiworld = None
@property @property
def boss(self) -> Optional[Boss]: def boss(self) -> Optional[Boss]:
@ -1065,7 +1065,7 @@ class Dungeon(object):
return self.__str__() return self.__str__()
def __str__(self): def __str__(self):
return self.world.get_name_string_for_object(self) if self.world else f'{self.name} (Player {self.player})' return self.multiworld.get_name_string_for_object(self) if self.multiworld else f'{self.name} (Player {self.player})'
class Boss(): class Boss():
@ -1130,7 +1130,7 @@ class Location:
return self.__str__() return self.__str__()
def __str__(self): def __str__(self):
world = self.parent_region.world if self.parent_region and self.parent_region.world else None world = self.parent_region.multiworld if self.parent_region and self.parent_region.multiworld else None
return world.get_name_string_for_object(self) if world else f'{self.name} (Player {self.player})' return world.get_name_string_for_object(self) if world else f'{self.name} (Player {self.player})'
def __hash__(self): def __hash__(self):
@ -1227,17 +1227,17 @@ class Item:
return self.__str__() return self.__str__()
def __str__(self) -> str: def __str__(self) -> str:
if self.location and self.location.parent_region and self.location.parent_region.world: if self.location and self.location.parent_region and self.location.parent_region.multiworld:
return self.location.parent_region.world.get_name_string_for_object(self) return self.location.parent_region.multiworld.get_name_string_for_object(self)
return f"{self.name} (Player {self.player})" return f"{self.name} (Player {self.player})"
class Spoiler(): class Spoiler():
world: MultiWorld multiworld: MultiWorld
unreachables: Set[Location] unreachables: Set[Location]
def __init__(self, world): def __init__(self, world):
self.world = world self.multiworld = world
self.hashes = {} self.hashes = {}
self.entrances = OrderedDict() self.entrances = OrderedDict()
self.medallions = {} self.medallions = {}
@ -1249,7 +1249,7 @@ class Spoiler():
self.bosses = OrderedDict() self.bosses = OrderedDict()
def set_entrance(self, entrance: str, exit_: str, direction: str, player: int): def set_entrance(self, entrance: str, exit_: str, direction: str, player: int):
if self.world.players == 1: if self.multiworld.players == 1:
self.entrances[(entrance, direction, player)] = OrderedDict( self.entrances[(entrance, direction, player)] = OrderedDict(
[('entrance', entrance), ('exit', exit_), ('direction', direction)]) [('entrance', entrance), ('exit', exit_), ('direction', direction)])
else: else:
@ -1258,45 +1258,45 @@ class Spoiler():
def parse_data(self): def parse_data(self):
self.medallions = OrderedDict() self.medallions = OrderedDict()
for player in self.world.get_game_players("A Link to the Past"): for player in self.multiworld.get_game_players("A Link to the Past"):
self.medallions[f'Misery Mire ({self.world.get_player_name(player)})'] = \ self.medallions[f'Misery Mire ({self.multiworld.get_player_name(player)})'] = \
self.world.required_medallions[player][0] self.multiworld.required_medallions[player][0]
self.medallions[f'Turtle Rock ({self.world.get_player_name(player)})'] = \ self.medallions[f'Turtle Rock ({self.multiworld.get_player_name(player)})'] = \
self.world.required_medallions[player][1] self.multiworld.required_medallions[player][1]
self.locations = OrderedDict() self.locations = OrderedDict()
listed_locations = set() listed_locations = set()
lw_locations = [loc for loc in self.world.get_locations() if lw_locations = [loc for loc in self.multiworld.get_locations() if
loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.LightWorld and loc.show_in_spoiler] loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.LightWorld and loc.show_in_spoiler]
self.locations['Light World'] = OrderedDict( self.locations['Light World'] = OrderedDict(
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in [(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
lw_locations]) lw_locations])
listed_locations.update(lw_locations) listed_locations.update(lw_locations)
dw_locations = [loc for loc in self.world.get_locations() if dw_locations = [loc for loc in self.multiworld.get_locations() if
loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.DarkWorld and loc.show_in_spoiler] loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.DarkWorld and loc.show_in_spoiler]
self.locations['Dark World'] = OrderedDict( self.locations['Dark World'] = OrderedDict(
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in [(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
dw_locations]) dw_locations])
listed_locations.update(dw_locations) listed_locations.update(dw_locations)
cave_locations = [loc for loc in self.world.get_locations() if cave_locations = [loc for loc in self.multiworld.get_locations() if
loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.Cave and loc.show_in_spoiler] loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.Cave and loc.show_in_spoiler]
self.locations['Caves'] = OrderedDict( self.locations['Caves'] = OrderedDict(
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in [(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
cave_locations]) cave_locations])
listed_locations.update(cave_locations) listed_locations.update(cave_locations)
for dungeon in self.world.dungeons.values(): for dungeon in self.multiworld.dungeons.values():
dungeon_locations = [loc for loc in self.world.get_locations() if dungeon_locations = [loc for loc in self.multiworld.get_locations() if
loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon and loc.show_in_spoiler] loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon and loc.show_in_spoiler]
self.locations[str(dungeon)] = OrderedDict( self.locations[str(dungeon)] = OrderedDict(
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in [(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
dungeon_locations]) dungeon_locations])
listed_locations.update(dungeon_locations) listed_locations.update(dungeon_locations)
other_locations = [loc for loc in self.world.get_locations() if other_locations = [loc for loc in self.multiworld.get_locations() if
loc not in listed_locations and loc.show_in_spoiler] loc not in listed_locations and loc.show_in_spoiler]
if other_locations: if other_locations:
self.locations['Other Locations'] = OrderedDict( self.locations['Other Locations'] = OrderedDict(
@ -1306,7 +1306,7 @@ class Spoiler():
self.shops = [] self.shops = []
from worlds.alttp.Shops import ShopType, price_type_display_name, price_rate_display from worlds.alttp.Shops import ShopType, price_type_display_name, price_rate_display
for shop in self.world.shops: for shop in self.multiworld.shops:
if not shop.custom: if not shop.custom:
continue continue
shopdata = { shopdata = {
@ -1335,34 +1335,34 @@ class Spoiler():
index)] += f", {item['replacement']} - {item['replacement_price']} {price_type_display_name[item['replacement_price_type']]}" index)] += f", {item['replacement']} - {item['replacement_price']} {price_type_display_name[item['replacement_price_type']]}"
self.shops.append(shopdata) self.shops.append(shopdata)
for player in self.world.get_game_players("A Link to the Past"): for player in self.multiworld.get_game_players("A Link to the Past"):
self.bosses[str(player)] = OrderedDict() self.bosses[str(player)] = OrderedDict()
self.bosses[str(player)]["Eastern Palace"] = self.world.get_dungeon("Eastern Palace", player).boss.name self.bosses[str(player)]["Eastern Palace"] = self.multiworld.get_dungeon("Eastern Palace", player).boss.name
self.bosses[str(player)]["Desert Palace"] = self.world.get_dungeon("Desert Palace", player).boss.name self.bosses[str(player)]["Desert Palace"] = self.multiworld.get_dungeon("Desert Palace", player).boss.name
self.bosses[str(player)]["Tower Of Hera"] = self.world.get_dungeon("Tower of Hera", player).boss.name self.bosses[str(player)]["Tower Of Hera"] = self.multiworld.get_dungeon("Tower of Hera", player).boss.name
self.bosses[str(player)]["Hyrule Castle"] = "Agahnim" self.bosses[str(player)]["Hyrule Castle"] = "Agahnim"
self.bosses[str(player)]["Palace Of Darkness"] = self.world.get_dungeon("Palace of Darkness", self.bosses[str(player)]["Palace Of Darkness"] = self.multiworld.get_dungeon("Palace of Darkness",
player).boss.name player).boss.name
self.bosses[str(player)]["Swamp Palace"] = self.world.get_dungeon("Swamp Palace", player).boss.name self.bosses[str(player)]["Swamp Palace"] = self.multiworld.get_dungeon("Swamp Palace", player).boss.name
self.bosses[str(player)]["Skull Woods"] = self.world.get_dungeon("Skull Woods", player).boss.name self.bosses[str(player)]["Skull Woods"] = self.multiworld.get_dungeon("Skull Woods", player).boss.name
self.bosses[str(player)]["Thieves Town"] = self.world.get_dungeon("Thieves Town", player).boss.name self.bosses[str(player)]["Thieves Town"] = self.multiworld.get_dungeon("Thieves Town", player).boss.name
self.bosses[str(player)]["Ice Palace"] = self.world.get_dungeon("Ice Palace", player).boss.name self.bosses[str(player)]["Ice Palace"] = self.multiworld.get_dungeon("Ice Palace", player).boss.name
self.bosses[str(player)]["Misery Mire"] = self.world.get_dungeon("Misery Mire", player).boss.name self.bosses[str(player)]["Misery Mire"] = self.multiworld.get_dungeon("Misery Mire", player).boss.name
self.bosses[str(player)]["Turtle Rock"] = self.world.get_dungeon("Turtle Rock", player).boss.name self.bosses[str(player)]["Turtle Rock"] = self.multiworld.get_dungeon("Turtle Rock", player).boss.name
if self.world.mode[player] != 'inverted': if self.multiworld.mode[player] != 'inverted':
self.bosses[str(player)]["Ganons Tower Basement"] = \ self.bosses[str(player)]["Ganons Tower Basement"] = \
self.world.get_dungeon('Ganons Tower', player).bosses['bottom'].name self.multiworld.get_dungeon('Ganons Tower', player).bosses['bottom'].name
self.bosses[str(player)]["Ganons Tower Middle"] = self.world.get_dungeon('Ganons Tower', player).bosses[ self.bosses[str(player)]["Ganons Tower Middle"] = self.multiworld.get_dungeon('Ganons Tower', player).bosses[
'middle'].name 'middle'].name
self.bosses[str(player)]["Ganons Tower Top"] = self.world.get_dungeon('Ganons Tower', player).bosses[ self.bosses[str(player)]["Ganons Tower Top"] = self.multiworld.get_dungeon('Ganons Tower', player).bosses[
'top'].name 'top'].name
else: else:
self.bosses[str(player)]["Ganons Tower Basement"] = \ self.bosses[str(player)]["Ganons Tower Basement"] = \
self.world.get_dungeon('Inverted Ganons Tower', player).bosses['bottom'].name self.multiworld.get_dungeon('Inverted Ganons Tower', player).bosses['bottom'].name
self.bosses[str(player)]["Ganons Tower Middle"] = \ self.bosses[str(player)]["Ganons Tower Middle"] = \
self.world.get_dungeon('Inverted Ganons Tower', player).bosses['middle'].name self.multiworld.get_dungeon('Inverted Ganons Tower', player).bosses['middle'].name
self.bosses[str(player)]["Ganons Tower Top"] = \ self.bosses[str(player)]["Ganons Tower Top"] = \
self.world.get_dungeon('Inverted Ganons Tower', player).bosses['top'].name self.multiworld.get_dungeon('Inverted Ganons Tower', player).bosses['top'].name
self.bosses[str(player)]["Ganons Tower"] = "Agahnim 2" self.bosses[str(player)]["Ganons Tower"] = "Agahnim 2"
self.bosses[str(player)]["Ganon"] = "Ganon" self.bosses[str(player)]["Ganon"] = "Ganon"
@ -1392,7 +1392,7 @@ class Spoiler():
return 'Yes' if variable else 'No' return 'Yes' if variable else 'No'
def write_option(option_key: str, option_obj: type(Options.Option)): def write_option(option_key: str, option_obj: type(Options.Option)):
res = getattr(self.world, option_key)[player] res = getattr(self.multiworld, option_key)[player]
display_name = getattr(option_obj, "display_name", option_key) display_name = getattr(option_obj, "display_name", option_key)
try: try:
outfile.write(f'{display_name + ":":33}{res.get_current_option_name()}\n') outfile.write(f'{display_name + ":":33}{res.get_current_option_name()}\n')
@ -1402,59 +1402,59 @@ class Spoiler():
with open(filename, 'w', encoding="utf-8-sig") as outfile: with open(filename, 'w', encoding="utf-8-sig") as outfile:
outfile.write( outfile.write(
'Archipelago Version %s - Seed: %s\n\n' % ( 'Archipelago Version %s - Seed: %s\n\n' % (
Utils.__version__, self.world.seed)) Utils.__version__, self.multiworld.seed))
outfile.write('Filling Algorithm: %s\n' % self.world.algorithm) outfile.write('Filling Algorithm: %s\n' % self.multiworld.algorithm)
outfile.write('Players: %d\n' % self.world.players) outfile.write('Players: %d\n' % self.multiworld.players)
AutoWorld.call_stage(self.world, "write_spoiler_header", outfile) AutoWorld.call_stage(self.multiworld, "write_spoiler_header", outfile)
for player in range(1, self.world.players + 1): for player in range(1, self.multiworld.players + 1):
if self.world.players > 1: if self.multiworld.players > 1:
outfile.write('\nPlayer %d: %s\n' % (player, self.world.get_player_name(player))) outfile.write('\nPlayer %d: %s\n' % (player, self.multiworld.get_player_name(player)))
outfile.write('Game: %s\n' % self.world.game[player]) outfile.write('Game: %s\n' % self.multiworld.game[player])
for f_option, option in Options.per_game_common_options.items(): for f_option, option in Options.per_game_common_options.items():
write_option(f_option, option) write_option(f_option, option)
options = self.world.worlds[player].option_definitions options = self.multiworld.worlds[player].option_definitions
if options: if options:
for f_option, option in options.items(): for f_option, option in options.items():
write_option(f_option, option) write_option(f_option, option)
AutoWorld.call_single(self.world, "write_spoiler_header", player, outfile) AutoWorld.call_single(self.multiworld, "write_spoiler_header", player, outfile)
if player in self.world.get_game_players("A Link to the Past"): if player in self.multiworld.get_game_players("A Link to the Past"):
outfile.write('%s%s\n' % ('Hash: ', self.hashes[player])) outfile.write('%s%s\n' % ('Hash: ', self.hashes[player]))
outfile.write('Logic: %s\n' % self.world.logic[player]) outfile.write('Logic: %s\n' % self.multiworld.logic[player])
outfile.write('Dark Room Logic: %s\n' % self.world.dark_room_logic[player]) outfile.write('Dark Room Logic: %s\n' % self.multiworld.dark_room_logic[player])
outfile.write('Mode: %s\n' % self.world.mode[player]) outfile.write('Mode: %s\n' % self.multiworld.mode[player])
outfile.write('Goal: %s\n' % self.world.goal[player]) outfile.write('Goal: %s\n' % self.multiworld.goal[player])
if "triforce" in self.world.goal[player]: # triforce hunt if "triforce" in self.multiworld.goal[player]: # triforce hunt
outfile.write("Pieces available for Triforce: %s\n" % outfile.write("Pieces available for Triforce: %s\n" %
self.world.triforce_pieces_available[player]) self.multiworld.triforce_pieces_available[player])
outfile.write("Pieces required for Triforce: %s\n" % outfile.write("Pieces required for Triforce: %s\n" %
self.world.triforce_pieces_required[player]) self.multiworld.triforce_pieces_required[player])
outfile.write('Difficulty: %s\n' % self.world.difficulty[player]) outfile.write('Difficulty: %s\n' % self.multiworld.difficulty[player])
outfile.write('Item Functionality: %s\n' % self.world.item_functionality[player]) outfile.write('Item Functionality: %s\n' % self.multiworld.item_functionality[player])
outfile.write('Entrance Shuffle: %s\n' % self.world.shuffle[player]) outfile.write('Entrance Shuffle: %s\n' % self.multiworld.shuffle[player])
if self.world.shuffle[player] != "vanilla": if self.multiworld.shuffle[player] != "vanilla":
outfile.write('Entrance Shuffle Seed %s\n' % self.world.worlds[player].er_seed) outfile.write('Entrance Shuffle Seed %s\n' % self.multiworld.worlds[player].er_seed)
outfile.write('Shop inventory shuffle: %s\n' % outfile.write('Shop inventory shuffle: %s\n' %
bool_to_text("i" in self.world.shop_shuffle[player])) bool_to_text("i" in self.multiworld.shop_shuffle[player]))
outfile.write('Shop price shuffle: %s\n' % outfile.write('Shop price shuffle: %s\n' %
bool_to_text("p" in self.world.shop_shuffle[player])) bool_to_text("p" in self.multiworld.shop_shuffle[player]))
outfile.write('Shop upgrade shuffle: %s\n' % outfile.write('Shop upgrade shuffle: %s\n' %
bool_to_text("u" in self.world.shop_shuffle[player])) bool_to_text("u" in self.multiworld.shop_shuffle[player]))
outfile.write('New Shop inventory: %s\n' % outfile.write('New Shop inventory: %s\n' %
bool_to_text("g" in self.world.shop_shuffle[player] or bool_to_text("g" in self.multiworld.shop_shuffle[player] or
"f" in self.world.shop_shuffle[player])) "f" in self.multiworld.shop_shuffle[player]))
outfile.write('Custom Potion Shop: %s\n' % outfile.write('Custom Potion Shop: %s\n' %
bool_to_text("w" in self.world.shop_shuffle[player])) bool_to_text("w" in self.multiworld.shop_shuffle[player]))
outfile.write('Enemy health: %s\n' % self.world.enemy_health[player]) outfile.write('Enemy health: %s\n' % self.multiworld.enemy_health[player])
outfile.write('Enemy damage: %s\n' % self.world.enemy_damage[player]) outfile.write('Enemy damage: %s\n' % self.multiworld.enemy_damage[player])
outfile.write('Prize shuffle %s\n' % outfile.write('Prize shuffle %s\n' %
self.world.shuffle_prizes[player]) self.multiworld.shuffle_prizes[player])
if self.entrances: if self.entrances:
outfile.write('\n\nEntrances:\n\n') outfile.write('\n\nEntrances:\n\n')
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_name(entry["player"])}: ' outfile.write('\n'.join(['%s%s %s %s' % (f'{self.multiworld.get_player_name(entry["player"])}: '
if self.world.players > 1 else '', entry['entrance'], if self.multiworld.players > 1 else '', entry['entrance'],
'<=>' if entry['direction'] == 'both' else '<=>' if entry['direction'] == 'both' else
'<=' if entry['direction'] == 'exit' else '=>', '<=' if entry['direction'] == 'exit' else '=>',
entry['exit']) for entry in self.entrances.values()])) entry['exit']) for entry in self.entrances.values()]))
@ -1464,7 +1464,7 @@ class Spoiler():
for dungeon, medallion in self.medallions.items(): for dungeon, medallion in self.medallions.items():
outfile.write(f'\n{dungeon}: {medallion}') outfile.write(f'\n{dungeon}: {medallion}')
AutoWorld.call_all(self.world, "write_spoiler", outfile) AutoWorld.call_all(self.multiworld, "write_spoiler", outfile)
outfile.write('\n\nLocations:\n\n') outfile.write('\n\nLocations:\n\n')
outfile.write('\n'.join( outfile.write('\n'.join(
@ -1477,11 +1477,11 @@ class Spoiler():
item for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if
item)) for shop in self.shops)) item)) for shop in self.shops))
for player in self.world.get_game_players("A Link to the Past"): for player in self.multiworld.get_game_players("A Link to the Past"):
if self.world.boss_shuffle[player] != 'none': if self.multiworld.boss_shuffle[player] != 'none':
bossmap = self.bosses[str(player)] if self.world.players > 1 else self.bosses bossmap = self.bosses[str(player)] if self.multiworld.players > 1 else self.bosses
outfile.write( outfile.write(
f'\n\nBosses{(f" ({self.world.get_player_name(player)})" if self.world.players > 1 else "")}:\n') f'\n\nBosses{(f" ({self.multiworld.get_player_name(player)})" if self.multiworld.players > 1 else "")}:\n')
outfile.write(' ' + '\n '.join([f'{x}: {y}' for x, y in bossmap.items()])) outfile.write(' ' + '\n '.join([f'{x}: {y}' for x, y in bossmap.items()]))
outfile.write('\n\nPlaythrough:\n\n') outfile.write('\n\nPlaythrough:\n\n')
outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join( outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join(
@ -1505,7 +1505,7 @@ class Spoiler():
path_listings.append("{}\n {}".format(location, "\n => ".join(path_lines))) path_listings.append("{}\n {}".format(location, "\n => ".join(path_lines)))
outfile.write('\n'.join(path_listings)) outfile.write('\n'.join(path_listings))
AutoWorld.call_all(self.world, "write_spoiler_end", outfile) AutoWorld.call_all(self.multiworld, "write_spoiler_end", outfile)
class Tutorial(NamedTuple): class Tutorial(NamedTuple):

View File

@ -455,7 +455,7 @@ In addition, the following methods can be implemented and attributes can be set
```python ```python
def generate_early(self) -> None: def generate_early(self) -> None:
# read player settings to world instance # read player settings to world instance
self.final_boss_hp = self.world.final_boss_hp[self.player].value self.final_boss_hp = self.multiworld.final_boss_hp[self.player].value
``` ```
#### create_item #### create_item
@ -490,19 +490,19 @@ def create_items(self) -> None:
# If an item can't have duplicates it has to be excluded manually. # If an item can't have duplicates it has to be excluded manually.
# List of items to exclude, as a copy since it will be destroyed below # List of items to exclude, as a copy since it will be destroyed below
exclude = [item for item in self.world.precollected_items[self.player]] exclude = [item for item in self.multiworld.precollected_items[self.player]]
for item in map(self.create_item, mygame_items): for item in map(self.create_item, mygame_items):
if item in exclude: if item in exclude:
exclude.remove(item) # this is destructive. create unique list above exclude.remove(item) # this is destructive. create unique list above
self.world.itempool.append(self.create_item("nothing")) self.multiworld.itempool.append(self.create_item("nothing"))
else: else:
self.world.itempool.append(item) self.multiworld.itempool.append(item)
# itempool and number of locations should match up. # itempool and number of locations should match up.
# If this is not the case we want to fill the itempool with junk. # If this is not the case we want to fill the itempool with junk.
junk = 0 # calculate this based on player settings junk = 0 # calculate this based on player settings
self.world.itempool += [self.create_item("nothing") for _ in range(junk)] self.multiworld.itempool += [self.create_item("nothing") for _ in range(junk)]
``` ```
#### create_regions #### create_regions
@ -511,30 +511,30 @@ def create_items(self) -> None:
def create_regions(self) -> None: def create_regions(self) -> None:
# Add regions to the multiworld. "Menu" is the required starting point. # Add regions to the multiworld. "Menu" is the required starting point.
# Arguments to Region() are name, type, human_readable_name, player, world # Arguments to Region() are name, type, human_readable_name, player, world
r = Region("Menu", RegionType.Generic, "Menu", self.player, self.world) r = Region("Menu", RegionType.Generic, "Menu", self.player, self.multiworld)
# Set Region.exits to a list of entrances that are reachable from region # Set Region.exits to a list of entrances that are reachable from region
r.exits = [Entrance(self.player, "New game", r)] # or use r.exits.append r.exits = [Entrance(self.player, "New game", r)] # or use r.exits.append
# Append region to MultiWorld's regions # Append region to MultiWorld's regions
self.world.regions.append(r) # or use += [r...] self.multiworld.regions.append(r) # or use += [r...]
r = Region("Main Area", RegionType.Generic, "Main Area", self.player, self.world) r = Region("Main Area", RegionType.Generic, "Main Area", self.player, self.multiworld)
# Add main area's locations to main area (all but final boss) # Add main area's locations to main area (all but final boss)
r.locations = [MyGameLocation(self.player, location.name, r.locations = [MyGameLocation(self.player, location.name,
self.location_name_to_id[location.name], r)] self.location_name_to_id[location.name], r)]
r.exits = [Entrance(self.player, "Boss Door", r)] r.exits = [Entrance(self.player, "Boss Door", r)]
self.world.regions.append(r) self.multiworld.regions.append(r)
r = Region("Boss Room", RegionType.Generic, "Boss Room", self.player, self.world) r = Region("Boss Room", RegionType.Generic, "Boss Room", self.player, self.multiworld)
# add event to Boss Room # add event to Boss Room
r.locations = [MyGameLocation(self.player, "Final Boss", None, r)] r.locations = [MyGameLocation(self.player, "Final Boss", None, r)]
self.world.regions.append(r) self.multiworld.regions.append(r)
# If entrances are not randomized, they should be connected here, otherwise # If entrances are not randomized, they should be connected here, otherwise
# they can also be connected at a later stage. # they can also be connected at a later stage.
self.world.get_entrance("New Game", self.player)\ self.multiworld.get_entrance("New Game", self.player)
.connect(self.world.get_region("Main Area", self.player)) .connect(self.multiworld.get_region("Main Area", self.player))
self.world.get_entrance("Boss Door", self.player)\ self.multiworld.get_entrance("Boss Door", self.player)
.connect(self.world.get_region("Boss Room", self.player)) .connect(self.multiworld.get_region("Boss Room", self.player))
# If setting location access rules from data is easier here, set_rules can # If setting location access rules from data is easier here, set_rules can
# possibly omitted. # possibly omitted.
@ -545,14 +545,14 @@ def create_regions(self) -> None:
```python ```python
def generate_basic(self) -> None: def generate_basic(self) -> None:
# place "Victory" at "Final Boss" and set collection as win condition # place "Victory" at "Final Boss" and set collection as win condition
self.world.get_location("Final Boss", self.player)\ self.multiworld.get_location("Final Boss", self.player)
.place_locked_item(self.create_event("Victory")) .place_locked_item(self.create_event("Victory"))
self.world.completion_condition[self.player] = \ self.multiworld.completion_condition[self.player] =
lambda state: state.has("Victory", self.player) lambda state: state.has("Victory", self.player)
# place item Herb into location Chest1 for some reason # place item Herb into location Chest1 for some reason
item = self.create_item("Herb") item = self.create_item("Herb")
self.world.get_location("Chest1", self.player).place_locked_item(item) self.multiworld.get_location("Chest1", self.player).place_locked_item(item)
# in most cases it's better to do this at the same time the itempool is # in most cases it's better to do this at the same time the itempool is
# filled to avoid accidental duplicates: # filled to avoid accidental duplicates:
# manually placed and still in the itempool # manually placed and still in the itempool
@ -564,41 +564,42 @@ def generate_basic(self) -> None:
from worlds.generic.Rules import add_rule, set_rule, forbid_item from worlds.generic.Rules import add_rule, set_rule, forbid_item
from Items import get_item_type from Items import get_item_type
def set_rules(self) -> None: def set_rules(self) -> None:
# For some worlds this step can be omitted if either a Logic mixin # For some worlds this step can be omitted if either a Logic mixin
# (see below) is used, it's easier to apply the rules from data during # (see below) is used, it's easier to apply the rules from data during
# location generation or everything is in generate_basic # location generation or everything is in generate_basic
# set a simple rule for an region # set a simple rule for an region
set_rule(self.world.get_entrance("Boss Door", self.player), set_rule(self.multiworld.get_entrance("Boss Door", self.player),
lambda state: state.has("Boss Key", self.player)) lambda state: state.has("Boss Key", self.player))
# combine rules to require two items # combine rules to require two items
add_rule(self.world.get_location("Chest2", self.player), add_rule(self.multiworld.get_location("Chest2", self.player),
lambda state: state.has("Sword", self.player)) lambda state: state.has("Sword", self.player))
add_rule(self.world.get_location("Chest2", self.player), add_rule(self.multiworld.get_location("Chest2", self.player),
lambda state: state.has("Shield", self.player)) lambda state: state.has("Shield", self.player))
# or simply combine yourself # or simply combine yourself
set_rule(self.world.get_location("Chest2", self.player), set_rule(self.multiworld.get_location("Chest2", self.player),
lambda state: state.has("Sword", self.player) and lambda state: state.has("Sword", self.player) and
state.has("Shield", self.player)) state.has("Shield", self.player))
# require two of an item # require two of an item
set_rule(self.world.get_location("Chest3", self.player), set_rule(self.multiworld.get_location("Chest3", self.player),
lambda state: state.has("Key", self.player, 2)) lambda state: state.has("Key", self.player, 2))
# require one item from an item group # require one item from an item group
add_rule(self.world.get_location("Chest3", self.player), add_rule(self.multiworld.get_location("Chest3", self.player),
lambda state: state.has_group("weapons", self.player)) lambda state: state.has_group("weapons", self.player))
# state also has .item_count() for items, .has_any() and.has_all() for sets # state also has .item_count() for items, .has_any() and.has_all() for sets
# and .count_group() for groups # and .count_group() for groups
# set_rule is likely to be a bit faster than add_rule # set_rule is likely to be a bit faster than add_rule
# disallow placing a specific local item at a specific location # disallow placing a specific local item at a specific location
forbid_item(self.world.get_location("Chest4", self.player), "Sword") forbid_item(self.multiworld.get_location("Chest4", self.player), "Sword")
# disallow placing items with a specific property # disallow placing items with a specific property
add_item_rule(self.world.get_location("Chest5", self.player), add_item_rule(self.multiworld.get_location("Chest5", self.player),
lambda item: get_item_type(item) == "weapon") lambda item: get_item_type(item) == "weapon")
# get_item_type needs to take player/world into account # get_item_type needs to take player/world into account
# if MyGameItem has a type property, a more direct implementation would be # if MyGameItem has a type property, a more direct implementation would be
add_item_rule(self.world.get_location("Chest5", self.player), add_item_rule(self.multiworld.get_location("Chest5", self.player),
lambda item: item.player != self.player or\ lambda item: item.player != self.player or\
item.my_type == "weapon") item.my_type == "weapon")
# location.item_rule = ... is likely to be a bit faster # location.item_rule = ... is likely to be a bit faster
@ -659,32 +660,33 @@ class MyGameWorld(World):
```python ```python
from .Mod import generate_mod from .Mod import generate_mod
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
# How to generate the mod or ROM highly depends on the game # How to generate the mod or ROM highly depends on the game
# if the mod is written in Lua, Jinja can be used to fill a template # if the mod is written in Lua, Jinja can be used to fill a template
# if the mod reads a json file, `json.dump()` can be used to generate that # if the mod reads a json file, `json.dump()` can be used to generate that
# code below is a dummy # code below is a dummy
data = { data = {
"seed": self.world.seed_name, # to verify the server's multiworld "seed": self.multiworld.seed_name, # to verify the server's multiworld
"slot": self.world.player_name[self.player], # to connect to server "slot": self.multiworld.player_name[self.player], # to connect to server
"items": {location.name: location.item.name "items": {location.name: location.item.name
if location.item.player == self.player else "Remote" if location.item.player == self.player else "Remote"
for location in self.world.get_filled_locations(self.player)}, for location in self.multiworld.get_filled_locations(self.player)},
# store start_inventory from player's .yaml # store start_inventory from player's .yaml
"starter_items": [item.name for item "starter_items": [item.name for item
in self.world.precollected_items[self.player]], in self.multiworld.precollected_items[self.player]],
"final_boss_hp": self.final_boss_hp, "final_boss_hp": self.final_boss_hp,
# store option name "easy", "normal" or "hard" for difficuly # store option name "easy", "normal" or "hard" for difficuly
"difficulty": self.world.difficulty[self.player].current_key, "difficulty": self.multiworld.difficulty[self.player].current_key,
# store option value True or False for fixing a glitch # store option value True or False for fixing a glitch
"fix_xyz_glitch": self.world.fix_xyz_glitch[self.player].value "fix_xyz_glitch": self.multiworld.fix_xyz_glitch[self.player].value
} }
# point to a ROM specified by the installation # point to a ROM specified by the installation
src = Utils.get_options()["mygame_options"]["rom_file"] src = Utils.get_options()["mygame_options"]["rom_file"]
# or point to worlds/mygame/data/mod_template # or point to worlds/mygame/data/mod_template
src = os.path.join(os.path.dirname(__file__), "data", "mod_template") src = os.path.join(os.path.dirname(__file__), "data", "mod_template")
# generate output path # generate output path
mod_name = f"AP-{self.world.seed_name}-P{self.player}-{self.world.player_name[self.player]}" mod_name = f"AP-{self.multiworld.seed_name}-P{self.player}-{self.multiworld.player_name[self.player]}"
out_file = os.path.join(output_directory, mod_name + ".zip") out_file = os.path.join(output_directory, mod_name + ".zip")
# generate the file # generate the file
generate_mod(src, out_file, data) generate_mod(src, out_file, data)

View File

@ -11,18 +11,18 @@ from worlds.alttp.Items import ItemFactory
class TestBase(unittest.TestCase): class TestBase(unittest.TestCase):
world: MultiWorld multiworld: MultiWorld
_state_cache = {} _state_cache = {}
def get_state(self, items): def get_state(self, items):
if (self.world, tuple(items)) in self._state_cache: if (self.multiworld, tuple(items)) in self._state_cache:
return self._state_cache[self.world, tuple(items)] return self._state_cache[self.multiworld, tuple(items)]
state = CollectionState(self.world) state = CollectionState(self.multiworld)
for item in items: for item in items:
item.classification = ItemClassification.progression item.classification = ItemClassification.progression
state.collect(item) state.collect(item)
state.sweep_for_events() state.sweep_for_events()
self._state_cache[self.world, tuple(items)] = state self._state_cache[self.multiworld, tuple(items)] = state
return state return state
def get_path(self, state, region): def get_path(self, state, region):
@ -44,11 +44,11 @@ class TestBase(unittest.TestCase):
items = item_pool[0] items = item_pool[0]
all_except = item_pool[1] if len(item_pool) > 1 else None all_except = item_pool[1] if len(item_pool) > 1 else None
state = self._get_items(item_pool, all_except) state = self._get_items(item_pool, all_except)
path = self.get_path(state, self.world.get_location(location, 1).parent_region) path = self.get_path(state, self.multiworld.get_location(location, 1).parent_region)
with self.subTest(msg="Reach Location", location=location, access=access, items=items, with self.subTest(msg="Reach Location", location=location, access=access, items=items,
all_except=all_except, path=path, entry=i): all_except=all_except, path=path, entry=i):
self.assertEqual(self.world.get_location(location, 1).can_reach(state), access) self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), access)
# check for partial solution # check for partial solution
if not all_except and access: # we are not supposed to be able to reach location with partial inventory if not all_except and access: # we are not supposed to be able to reach location with partial inventory
@ -56,18 +56,18 @@ class TestBase(unittest.TestCase):
with self.subTest(msg="Location reachable without required item", location=location, with self.subTest(msg="Location reachable without required item", location=location,
items=item_pool[0], missing_item=missing_item, entry=i): items=item_pool[0], missing_item=missing_item, entry=i):
state = self._get_items_partial(item_pool, missing_item) state = self._get_items_partial(item_pool, missing_item)
self.assertEqual(self.world.get_location(location, 1).can_reach(state), False) self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), False)
def run_entrance_tests(self, access_pool): def run_entrance_tests(self, access_pool):
for i, (entrance, access, *item_pool) in enumerate(access_pool): for i, (entrance, access, *item_pool) in enumerate(access_pool):
items = item_pool[0] items = item_pool[0]
all_except = item_pool[1] if len(item_pool) > 1 else None all_except = item_pool[1] if len(item_pool) > 1 else None
state = self._get_items(item_pool, all_except) state = self._get_items(item_pool, all_except)
path = self.get_path(state, self.world.get_entrance(entrance, 1).parent_region) path = self.get_path(state, self.multiworld.get_entrance(entrance, 1).parent_region)
with self.subTest(msg="Reach Entrance", entrance=entrance, access=access, items=items, with self.subTest(msg="Reach Entrance", entrance=entrance, access=access, items=items,
all_except=all_except, path=path, entry=i): all_except=all_except, path=path, entry=i):
self.assertEqual(self.world.get_entrance(entrance, 1).can_reach(state), access) self.assertEqual(self.multiworld.get_entrance(entrance, 1).can_reach(state), access)
# check for partial solution # check for partial solution
if not all_except and access: # we are not supposed to be able to reach location with partial inventory if not all_except and access: # we are not supposed to be able to reach location with partial inventory
@ -75,11 +75,11 @@ class TestBase(unittest.TestCase):
with self.subTest(msg="Entrance reachable without required item", entrance=entrance, with self.subTest(msg="Entrance reachable without required item", entrance=entrance,
items=item_pool[0], missing_item=missing_item, entry=i): items=item_pool[0], missing_item=missing_item, entry=i):
state = self._get_items_partial(item_pool, missing_item) state = self._get_items_partial(item_pool, missing_item)
self.assertEqual(self.world.get_entrance(entrance, 1).can_reach(state), False) self.assertEqual(self.multiworld.get_entrance(entrance, 1).can_reach(state), False)
def _get_items(self, item_pool, all_except): def _get_items(self, item_pool, all_except):
if all_except and len(all_except) > 0: if all_except and len(all_except) > 0:
items = self.world.itempool[:] items = self.multiworld.itempool[:]
items = [item for item in items if items = [item for item in items if
item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)] item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)]
items.extend(ItemFactory(item_pool[0], 1)) items.extend(ItemFactory(item_pool[0], 1))

View File

@ -14,46 +14,46 @@ from worlds import AutoWorld
class TestDungeon(unittest.TestCase): class TestDungeon(unittest.TestCase):
def setUp(self): def setUp(self):
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})
self.world.set_options(args) self.multiworld.set_options(args)
self.world.set_default_common_options() self.multiworld.set_default_common_options()
self.starting_regions = [] # Where to start exploring self.starting_regions = [] # Where to start exploring
self.remove_exits = [] # Block dungeon exits self.remove_exits = [] # Block dungeon exits
self.world.difficulty_requirements[1] = difficulties['normal'] self.multiworld.difficulty_requirements[1] = difficulties['normal']
create_regions(self.world, 1) create_regions(self.multiworld, 1)
create_dungeons(self.world, 1) create_dungeons(self.multiworld, 1)
create_shops(self.world, 1) create_shops(self.multiworld, 1)
for exitname, regionname in mandatory_connections: for exitname, regionname in mandatory_connections:
connect_simple(self.world, exitname, regionname, 1) connect_simple(self.multiworld, exitname, regionname, 1)
connect_simple(self.world, 'Big Bomb Shop', 'Big Bomb Shop', 1) connect_simple(self.multiworld, 'Big Bomb Shop', 'Big Bomb Shop', 1)
self.world.get_region('Menu', 1).exits = [] self.multiworld.get_region('Menu', 1).exits = []
self.world.swamp_patch_required[1] = True self.multiworld.swamp_patch_required[1] = True
self.world.worlds[1].set_rules() self.multiworld.worlds[1].set_rules()
self.world.worlds[1].create_items() self.multiworld.worlds[1].create_items()
self.world.itempool.extend(get_dungeon_item_pool(self.world)) self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
self.world.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1))
def run_tests(self, access_pool): def run_tests(self, access_pool):
for exit in self.remove_exits: for exit in self.remove_exits:
self.world.get_entrance(exit, 1).connected_region = self.world.get_region('Menu', 1) self.multiworld.get_entrance(exit, 1).connected_region = self.multiworld.get_region('Menu', 1)
for location, access, *item_pool in access_pool: for location, access, *item_pool in access_pool:
items = item_pool[0] items = item_pool[0]
all_except = item_pool[1] if len(item_pool) > 1 else None all_except = item_pool[1] if len(item_pool) > 1 else None
with self.subTest(location=location, access=access, items=items, all_except=all_except): with self.subTest(location=location, access=access, items=items, all_except=all_except):
if all_except and len(all_except) > 0: if all_except and len(all_except) > 0:
items = self.world.itempool[:] items = self.multiworld.itempool[:]
items = [item for item in items if item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)] items = [item for item in items if item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)]
items.extend(ItemFactory(item_pool[0], 1)) items.extend(ItemFactory(item_pool[0], 1))
else: else:
items = ItemFactory(items, 1) items = ItemFactory(items, 1)
state = CollectionState(self.world) state = CollectionState(self.multiworld)
state.reachable_regions[1].add(self.world.get_region('Menu', 1)) state.reachable_regions[1].add(self.multiworld.get_region('Menu', 1))
for region_name in self.starting_regions: for region_name in self.starting_regions:
region = self.world.get_region(region_name, 1) region = self.multiworld.get_region(region_name, 1)
state.reachable_regions[1].add(region) state.reachable_regions[1].add(region)
for exit in region.exits: for exit in region.exits:
if exit.connected_region is not None: if exit.connected_region is not None:
@ -63,4 +63,4 @@ class TestDungeon(unittest.TestCase):
item.classification = ItemClassification.progression item.classification = ItemClassification.progression
state.collect(item) state.collect(item)
self.assertEqual(self.world.get_location(location, 1).can_reach(state), access) self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), access)

View File

@ -27,7 +27,7 @@ def generate_multi_world(players: int = 1) -> MultiWorld:
class PlayerDefinition(object): class PlayerDefinition(object):
world: MultiWorld multiworld: MultiWorld
id: int id: int
menu: Region menu: Region
locations: List[Location] locations: List[Location]
@ -36,7 +36,7 @@ class PlayerDefinition(object):
regions: List[Region] regions: List[Region]
def __init__(self, world: MultiWorld, id: int, menu: Region, locations: List[Location] = [], prog_items: List[Item] = [], basic_items: List[Item] = []): def __init__(self, world: MultiWorld, id: int, menu: Region, locations: List[Location] = [], prog_items: List[Item] = [], basic_items: List[Item] = []):
self.world = world self.multiworld = world
self.id = id self.id = id
self.menu = menu self.menu = menu
self.locations = locations self.locations = locations
@ -48,7 +48,7 @@ class PlayerDefinition(object):
region_tag = "_region" + str(len(self.regions)) region_tag = "_region" + str(len(self.regions))
region_name = "player" + str(self.id) + region_tag region_name = "player" + str(self.id) + region_tag
region = Region("player" + str(self.id) + region_tag, RegionType.Generic, region = Region("player" + str(self.id) + region_tag, RegionType.Generic,
"Region Hint", self.id, self.world) "Region Hint", self.id, self.multiworld)
self.locations += generate_locations(size, self.id, None, region, region_tag) self.locations += generate_locations(size, self.id, None, region, region_tag)
entrance = Entrance(self.id, region_name + "_entrance", parent) entrance = Entrance(self.id, region_name + "_entrance", parent)
@ -57,7 +57,7 @@ class PlayerDefinition(object):
entrance.access_rule = access_rule entrance.access_rule = access_rule
self.regions.append(region) self.regions.append(region)
self.world.regions.append(region) self.multiworld.regions.append(region)
return region return region

View File

@ -7,15 +7,15 @@ gen_steps = ["generate_early", "create_regions", "create_items", "set_rules", "g
def setup_default_world(world_type) -> MultiWorld: def setup_default_world(world_type) -> MultiWorld:
world = MultiWorld(1) multiworld = MultiWorld(1)
world.game[1] = world_type.game multiworld.game[1] = world_type.game
world.player_name = {1: "Tester"} multiworld.player_name = {1: "Tester"}
world.set_seed() multiworld.set_seed()
args = Namespace() args = Namespace()
for name, option in world_type.option_definitions.items(): for name, option in world_type.option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})
world.set_options(args) multiworld.set_options(args)
world.set_default_common_options() multiworld.set_default_common_options()
for step in gen_steps: for step in gen_steps:
call_all(world, step) call_all(multiworld, step)
return world return multiworld

View File

@ -14,23 +14,23 @@ from worlds import AutoWorld
class TestInverted(TestBase): class TestInverted(TestBase):
def setUp(self): def setUp(self):
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})
self.world.set_options(args) self.multiworld.set_options(args)
self.world.set_default_common_options() self.multiworld.set_default_common_options()
self.world.difficulty_requirements[1] = difficulties['normal'] self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.world.mode[1] = "inverted" self.multiworld.mode[1] = "inverted"
create_inverted_regions(self.world, 1) create_inverted_regions(self.multiworld, 1)
create_dungeons(self.world, 1) create_dungeons(self.multiworld, 1)
create_shops(self.world, 1) create_shops(self.multiworld, 1)
link_inverted_entrances(self.world, 1) link_inverted_entrances(self.multiworld, 1)
self.world.worlds[1].create_items() self.multiworld.worlds[1].create_items()
self.world.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.required_medallions[1] = ['Ether', 'Quake']
self.world.itempool.extend(get_dungeon_item_pool(self.world)) self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
self.world.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1))
self.world.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 1', 1).item = None
self.world.get_location('Agahnim 2', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None
mark_light_world_regions(self.world, 1) mark_light_world_regions(self.multiworld, 1)
self.world.worlds[1].set_rules() self.multiworld.worlds[1].set_rules()

View File

@ -14,16 +14,16 @@ from worlds import AutoWorld
class TestInvertedBombRules(unittest.TestCase): class TestInvertedBombRules(unittest.TestCase):
def setUp(self): def setUp(self):
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
self.world.mode[1] = "inverted" self.multiworld.mode[1] = "inverted"
args = Namespace args = Namespace
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})
self.world.set_options(args) self.multiworld.set_options(args)
self.world.set_default_common_options() self.multiworld.set_default_common_options()
self.world.difficulty_requirements[1] = difficulties['normal'] self.multiworld.difficulty_requirements[1] = difficulties['normal']
create_inverted_regions(self.world, 1) create_inverted_regions(self.multiworld, 1)
create_dungeons(self.world, 1) create_dungeons(self.multiworld, 1)
#TODO: Just making sure I haven't missed an entrance. It would be good to test the rules make sense as well. #TODO: Just making sure I haven't missed an entrance. It would be good to test the rules make sense as well.
def testInvertedBombRulesAreComplete(self): def testInvertedBombRulesAreComplete(self):
@ -31,9 +31,9 @@ class TestInvertedBombRules(unittest.TestCase):
must_exits = list(Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit) must_exits = list(Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit)
for entrance_name in (entrances + must_exits): for entrance_name in (entrances + must_exits):
if entrance_name not in ['Desert Palace Entrance (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)']: if entrance_name not in ['Desert Palace Entrance (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)']:
entrance = self.world.get_entrance(entrance_name, 1) entrance = self.multiworld.get_entrance(entrance_name, 1)
connect_entrance(self.world, entrance_name, 'Inverted Big Bomb Shop', 1) connect_entrance(self.multiworld, entrance_name, 'Inverted Big Bomb Shop', 1)
set_inverted_big_bomb_rules(self.world, 1) set_inverted_big_bomb_rules(self.multiworld, 1)
entrance.connected_region.entrances.remove(entrance) entrance.connected_region.entrances.remove(entrance)
entrance.connected_region = None entrance.connected_region = None
@ -45,9 +45,9 @@ class TestInvertedBombRules(unittest.TestCase):
def testInvalidEntrances(self): def testInvalidEntrances(self):
for entrance_name in ['Desert Palace Entrance (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)']: for entrance_name in ['Desert Palace Entrance (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)']:
entrance = self.world.get_entrance(entrance_name, 1) entrance = self.multiworld.get_entrance(entrance_name, 1)
connect_entrance(self.world, entrance_name, 'Inverted Big Bomb Shop', 1) connect_entrance(self.multiworld, entrance_name, 'Inverted Big Bomb Shop', 1)
with self.assertRaises(Exception): with self.assertRaises(Exception):
set_inverted_big_bomb_rules(self.world, 1) set_inverted_big_bomb_rules(self.multiworld, 1)
entrance.connected_region.entrances.remove(entrance) entrance.connected_region.entrances.remove(entrance)
entrance.connected_region = None entrance.connected_region = None

View File

@ -15,24 +15,24 @@ from worlds import AutoWorld
class TestInvertedMinor(TestBase): class TestInvertedMinor(TestBase):
def setUp(self): def setUp(self):
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})
self.world.set_options(args) self.multiworld.set_options(args)
self.world.set_default_common_options() self.multiworld.set_default_common_options()
self.world.mode[1] = "inverted" self.multiworld.mode[1] = "inverted"
self.world.logic[1] = "minorglitches" self.multiworld.logic[1] = "minorglitches"
self.world.difficulty_requirements[1] = difficulties['normal'] self.multiworld.difficulty_requirements[1] = difficulties['normal']
create_inverted_regions(self.world, 1) create_inverted_regions(self.multiworld, 1)
create_dungeons(self.world, 1) create_dungeons(self.multiworld, 1)
create_shops(self.world, 1) create_shops(self.multiworld, 1)
link_inverted_entrances(self.world, 1) link_inverted_entrances(self.multiworld, 1)
self.world.worlds[1].create_items() self.multiworld.worlds[1].create_items()
self.world.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.required_medallions[1] = ['Ether', 'Quake']
self.world.itempool.extend(get_dungeon_item_pool(self.world)) self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
self.world.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1))
self.world.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 1', 1).item = None
self.world.get_location('Agahnim 2', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None
mark_light_world_regions(self.world, 1) mark_light_world_regions(self.multiworld, 1)
self.world.worlds[1].set_rules() self.multiworld.worlds[1].set_rules()

View File

@ -16,26 +16,26 @@ from worlds import AutoWorld
class TestInvertedOWG(TestBase): class TestInvertedOWG(TestBase):
def setUp(self): def setUp(self):
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})
self.world.set_options(args) self.multiworld.set_options(args)
self.world.set_default_common_options() self.multiworld.set_default_common_options()
self.world.logic[1] = "owglitches" self.multiworld.logic[1] = "owglitches"
self.world.mode[1] = "inverted" self.multiworld.mode[1] = "inverted"
self.world.difficulty_requirements[1] = difficulties['normal'] self.multiworld.difficulty_requirements[1] = difficulties['normal']
create_inverted_regions(self.world, 1) create_inverted_regions(self.multiworld, 1)
create_dungeons(self.world, 1) create_dungeons(self.multiworld, 1)
create_shops(self.world, 1) create_shops(self.multiworld, 1)
link_inverted_entrances(self.world, 1) link_inverted_entrances(self.multiworld, 1)
self.world.worlds[1].create_items() self.multiworld.worlds[1].create_items()
self.world.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.required_medallions[1] = ['Ether', 'Quake']
self.world.itempool.extend(get_dungeon_item_pool(self.world)) self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
self.world.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1))
self.world.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 1', 1).item = None
self.world.get_location('Agahnim 2', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None
self.world.precollected_items[1].clear() self.multiworld.precollected_items[1].clear()
self.world.itempool.append(ItemFactory('Pegasus Boots', 1)) self.multiworld.itempool.append(ItemFactory('Pegasus Boots', 1))
mark_light_world_regions(self.world, 1) mark_light_world_regions(self.multiworld, 1)
self.world.worlds[1].set_rules() self.multiworld.worlds[1].set_rules()

View File

@ -30,28 +30,28 @@ def MCItemFactory(items, player: int):
class TestMinecraft(TestBase): class TestMinecraft(TestBase):
def setUp(self): def setUp(self):
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
self.world.game[1] = "Minecraft" self.multiworld.game[1] = "Minecraft"
self.world.worlds[1] = MinecraftWorld(self.world, 1) self.multiworld.worlds[1] = MinecraftWorld(self.multiworld, 1)
exclusion_pools = ['hard', 'unreasonable', 'postgame'] exclusion_pools = ['hard', 'unreasonable', 'postgame']
for pool in exclusion_pools: for pool in exclusion_pools:
setattr(self.world, f"include_{pool}_advancements", {1: False}) setattr(self.multiworld, f"include_{pool}_advancements", {1: False})
setattr(self.world, "advancement_goal", {1: AdvancementGoal(30)}) setattr(self.multiworld, "advancement_goal", {1: AdvancementGoal(30)})
setattr(self.world, "egg_shards_required", {1: EggShardsRequired(0)}) setattr(self.multiworld, "egg_shards_required", {1: EggShardsRequired(0)})
setattr(self.world, "egg_shards_available", {1: EggShardsAvailable(0)}) setattr(self.multiworld, "egg_shards_available", {1: EggShardsAvailable(0)})
setattr(self.world, "required_bosses", {1: BossGoal(1)}) # ender dragon setattr(self.multiworld, "required_bosses", {1: BossGoal(1)}) # ender dragon
setattr(self.world, "shuffle_structures", {1: ShuffleStructures(False)}) setattr(self.multiworld, "shuffle_structures", {1: ShuffleStructures(False)})
setattr(self.world, "bee_traps", {1: BeeTraps(0)}) setattr(self.multiworld, "bee_traps", {1: BeeTraps(0)})
setattr(self.world, "combat_difficulty", {1: CombatDifficulty(1)}) # normal setattr(self.multiworld, "combat_difficulty", {1: CombatDifficulty(1)}) # normal
setattr(self.world, "structure_compasses", {1: Toggle(False)}) setattr(self.multiworld, "structure_compasses", {1: Toggle(False)})
setattr(self.world, "death_link", {1: Toggle(False)}) setattr(self.multiworld, "death_link", {1: Toggle(False)})
AutoWorld.call_single(self.world, "create_regions", 1) AutoWorld.call_single(self.multiworld, "create_regions", 1)
AutoWorld.call_single(self.world, "generate_basic", 1) AutoWorld.call_single(self.multiworld, "generate_basic", 1)
AutoWorld.call_single(self.world, "set_rules", 1) AutoWorld.call_single(self.multiworld, "set_rules", 1)
def _get_items(self, item_pool, all_except): def _get_items(self, item_pool, all_except):
if all_except and len(all_except) > 0: if all_except and len(all_except) > 0:
items = self.world.itempool[:] items = self.multiworld.itempool[:]
items = [item for item in items if items = [item for item in items if
item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)] item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)]
items.extend(MCItemFactory(item_pool[0], 1)) items.extend(MCItemFactory(item_pool[0], 1))

View File

@ -15,23 +15,23 @@ from worlds import AutoWorld
class TestMinor(TestBase): class TestMinor(TestBase):
def setUp(self): def setUp(self):
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})
self.world.set_options(args) self.multiworld.set_options(args)
self.world.set_default_common_options() self.multiworld.set_default_common_options()
self.world.logic[1] = "minorglitches" self.multiworld.logic[1] = "minorglitches"
self.world.difficulty_requirements[1] = difficulties['normal'] self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.world.worlds[1].er_seed = 0 self.multiworld.worlds[1].er_seed = 0
self.world.worlds[1].create_regions() self.multiworld.worlds[1].create_regions()
self.world.worlds[1].create_items() self.multiworld.worlds[1].create_items()
self.world.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.required_medallions[1] = ['Ether', 'Quake']
self.world.itempool.extend(get_dungeon_item_pool(self.world)) self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
self.world.itempool.extend(ItemFactory( self.multiworld.itempool.extend(ItemFactory(
['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', ['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1',
'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1))
self.world.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 1', 1).item = None
self.world.get_location('Agahnim 2', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None
mark_dark_world_regions(self.world, 1) mark_dark_world_regions(self.multiworld, 1)
self.world.worlds[1].set_rules() self.multiworld.worlds[1].set_rules()

View File

@ -16,23 +16,23 @@ from worlds import AutoWorld
class TestVanillaOWG(TestBase): class TestVanillaOWG(TestBase):
def setUp(self): def setUp(self):
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})
self.world.set_options(args) self.multiworld.set_options(args)
self.world.set_default_common_options() self.multiworld.set_default_common_options()
self.world.difficulty_requirements[1] = difficulties['normal'] self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.world.logic[1] = "owglitches" self.multiworld.logic[1] = "owglitches"
self.world.worlds[1].er_seed = 0 self.multiworld.worlds[1].er_seed = 0
self.world.worlds[1].create_regions() self.multiworld.worlds[1].create_regions()
self.world.worlds[1].create_items() self.multiworld.worlds[1].create_items()
self.world.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.required_medallions[1] = ['Ether', 'Quake']
self.world.itempool.extend(get_dungeon_item_pool(self.world)) self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
self.world.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1))
self.world.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 1', 1).item = None
self.world.get_location('Agahnim 2', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None
self.world.precollected_items[1].clear() self.multiworld.precollected_items[1].clear()
self.world.itempool.append(ItemFactory('Pegasus Boots', 1)) self.multiworld.itempool.append(ItemFactory('Pegasus Boots', 1))
mark_dark_world_regions(self.world, 1) mark_dark_world_regions(self.multiworld, 1)
self.world.worlds[1].set_rules() self.multiworld.worlds[1].set_rules()

View File

@ -14,21 +14,21 @@ from worlds import AutoWorld
class TestVanilla(TestBase): class TestVanilla(TestBase):
def setUp(self): def setUp(self):
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})
self.world.set_options(args) self.multiworld.set_options(args)
self.world.set_default_common_options() self.multiworld.set_default_common_options()
self.world.logic[1] = "noglitches" self.multiworld.logic[1] = "noglitches"
self.world.difficulty_requirements[1] = difficulties['normal'] self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.world.worlds[1].er_seed = 0 self.multiworld.worlds[1].er_seed = 0
self.world.worlds[1].create_regions() self.multiworld.worlds[1].create_regions()
self.world.worlds[1].create_items() self.multiworld.worlds[1].create_items()
self.world.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.required_medallions[1] = ['Ether', 'Quake']
self.world.itempool.extend(get_dungeon_item_pool(self.world)) self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
self.world.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1))
self.world.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 1', 1).item = None
self.world.get_location('Agahnim 2', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None
mark_dark_world_regions(self.world, 1) mark_dark_world_regions(self.multiworld, 1)
self.world.worlds[1].set_rules() self.multiworld.worlds[1].set_rules()

View File

@ -22,29 +22,29 @@ class WorldTestBase(unittest.TestCase):
def world_setup(self, seed: typing.Optional[int] = None) -> None: def world_setup(self, seed: typing.Optional[int] = None) -> None:
if not hasattr(self, "game"): if not hasattr(self, "game"):
raise NotImplementedError("didn't define game name") raise NotImplementedError("didn't define game name")
self.world = MultiWorld(1) self.multiworld = MultiWorld(1)
self.world.game[1] = self.game self.multiworld.game[1] = self.game
self.world.player_name = {1: "Tester"} self.multiworld.player_name = {1: "Tester"}
self.world.set_seed(seed) self.multiworld.set_seed(seed)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types[self.game].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types[self.game].option_definitions.items():
setattr(args, name, { setattr(args, name, {
1: option.from_any(self.options.get(name, getattr(option, "default"))) 1: option.from_any(self.options.get(name, getattr(option, "default")))
}) })
self.world.set_options(args) self.multiworld.set_options(args)
self.world.set_default_common_options() self.multiworld.set_default_common_options()
for step in gen_steps: for step in gen_steps:
call_all(self.world, step) call_all(self.multiworld, step)
def collect_all_but(self, item_names: typing.Union[str, typing.Iterable[str]]) -> None: def collect_all_but(self, item_names: typing.Union[str, typing.Iterable[str]]) -> None:
if isinstance(item_names, str): if isinstance(item_names, str):
item_names = (item_names,) item_names = (item_names,)
for item in self.world.get_items(): for item in self.multiworld.get_items():
if item.name not in item_names: if item.name not in item_names:
self.world.state.collect(item) self.multiworld.state.collect(item)
def get_item_by_name(self, item_name: str) -> Item: def get_item_by_name(self, item_name: str) -> Item:
for item in self.world.get_items(): for item in self.multiworld.get_items():
if item.name == item_name: if item.name == item_name:
return item return item
raise ValueError("No such item") raise ValueError("No such item")
@ -52,7 +52,7 @@ class WorldTestBase(unittest.TestCase):
def get_items_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]: def get_items_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]:
if isinstance(item_names, str): if isinstance(item_names, str):
item_names = (item_names,) item_names = (item_names,)
return [item for item in self.world.itempool if item.name in item_names] return [item for item in self.multiworld.itempool if item.name in item_names]
def collect_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]: def collect_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]:
""" collect all of the items in the item pool that have the given names """ """ collect all of the items in the item pool that have the given names """
@ -64,21 +64,21 @@ class WorldTestBase(unittest.TestCase):
if isinstance(items, Item): if isinstance(items, Item):
items = (items,) items = (items,)
for item in items: for item in items:
self.world.state.collect(item) self.multiworld.state.collect(item)
def remove(self, items: typing.Union[Item, typing.Iterable[Item]]) -> None: def remove(self, items: typing.Union[Item, typing.Iterable[Item]]) -> None:
if isinstance(items, Item): if isinstance(items, Item):
items = (items,) items = (items,)
for item in items: for item in items:
if item.location and item.location.event and item.location in self.world.state.events: if item.location and item.location.event and item.location in self.multiworld.state.events:
self.world.state.events.remove(item.location) self.multiworld.state.events.remove(item.location)
self.world.state.remove(item) self.multiworld.state.remove(item)
def can_reach_location(self, location: str) -> bool: def can_reach_location(self, location: str) -> bool:
return self.world.state.can_reach(location, "Location", 1) return self.multiworld.state.can_reach(location, "Location", 1)
def count(self, item_name: str) -> int: def count(self, item_name: str) -> int:
return self.world.state.count(item_name, 1) return self.multiworld.state.count(item_name, 1)
def assertAccessDependency(self, def assertAccessDependency(self,
locations: typing.List[str], locations: typing.List[str],
@ -86,8 +86,8 @@ class WorldTestBase(unittest.TestCase):
all_items = [item_name for item_names in possible_items for item_name in item_names] all_items = [item_name for item_names in possible_items for item_name in item_names]
self.collect_all_but(all_items) self.collect_all_but(all_items)
for location in self.world.get_locations(): for location in self.multiworld.get_locations():
self.assertEqual(self.world.state.can_reach(location), location.name not in locations) self.assertEqual(self.multiworld.state.can_reach(location), location.name not in locations)
for item_names in possible_items: for item_names in possible_items:
items = self.collect_by_name(item_names) items = self.collect_by_name(item_names)
for location in locations: for location in locations:
@ -95,4 +95,4 @@ class WorldTestBase(unittest.TestCase):
self.remove(items) self.remove(items)
def assertBeatable(self, beatable: bool): def assertBeatable(self, beatable: bool):
self.assertEqual(self.world.can_beat_game(self.world.state), beatable) self.assertEqual(self.multiworld.can_beat_game(self.multiworld.state), beatable)

View File

@ -9,7 +9,7 @@ class OptionsTest(ZillionTestBase):
def test_validate_default(self) -> None: def test_validate_default(self) -> None:
self.world_setup() self.world_setup()
validate(self.world, 1) validate(self.multiworld, 1)
def test_vblr_ap_to_zz(self) -> None: def test_vblr_ap_to_zz(self) -> None:
""" all of the valid values for the AP options map to valid values for ZZ options """ """ all of the valid values for the AP options map to valid values for ZZ options """
@ -20,7 +20,7 @@ class OptionsTest(ZillionTestBase):
for value in vblr_class.name_lookup.values(): for value in vblr_class.name_lookup.values():
self.options = {option_name: value} self.options = {option_name: value}
self.world_setup() self.world_setup()
zz_options, _item_counts = validate(self.world, 1) zz_options, _item_counts = validate(self.multiworld, 1)
assert getattr(zz_options, option_name) in VBLR_CHOICES assert getattr(zz_options, option_name) in VBLR_CHOICES
# TODO: test validate with invalid combinations of options # TODO: test validate with invalid combinations of options

View File

@ -13,7 +13,7 @@ class ZillionTestBase(WorldTestBase):
This makes sure that gun 3 is required by making all the canisters This makes sure that gun 3 is required by making all the canisters
in O-7 (including key word canisters) require gun 3. in O-7 (including key word canisters) require gun 3.
""" """
zz_world = cast(ZillionWorld, self.world.worlds[1]) zz_world = cast(ZillionWorld, self.multiworld.worlds[1])
assert zz_world.zz_system.randomizer assert zz_world.zz_system.randomizer
for zz_loc_name, zz_loc in zz_world.zz_system.randomizer.locations.items(): for zz_loc_name, zz_loc in zz_world.zz_system.randomizer.locations.items():
if zz_loc_name.startswith("r15c6"): if zz_loc_name.startswith("r15c6"):

View File

@ -182,7 +182,7 @@ class World(metaclass=AutoWorldRegister):
web: WebWorld = WebWorld() web: WebWorld = WebWorld()
# autoset on creation: # autoset on creation:
world: "MultiWorld" multiworld: "MultiWorld"
player: int player: int
# automatically generated # automatically generated
@ -196,7 +196,7 @@ class World(metaclass=AutoWorldRegister):
__file__: str # path it was loaded from __file__: str # path it was loaded from
def __init__(self, world: "MultiWorld", player: int): def __init__(self, world: "MultiWorld", player: int):
self.world = world self.multiworld = world
self.player = player self.player = player
# overridable methods that get called by Main.py, sorted by execution order # overridable methods that get called by Main.py, sorted by execution order
@ -287,7 +287,7 @@ class World(metaclass=AutoWorldRegister):
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
"""Called when the item pool needs to be filled with additional items to match location count.""" """Called when the item pool needs to be filled with additional items to match location count."""
logging.warning(f"World {self} is generating a filler item without custom filler pool.") logging.warning(f"World {self} is generating a filler item without custom filler pool.")
return self.world.random.choice(tuple(self.item_name_to_id.keys())) return self.multiworld.random.choice(tuple(self.item_name_to_id.keys()))
# decent place to implement progressive items, in most cases can stay as-is # decent place to implement progressive items, in most cases can stay as-is
def collect_item(self, state: "CollectionState", item: "Item", remove: bool = False) -> Optional[str]: def collect_item(self, state: "CollectionState", item: "Item", remove: bool = False) -> Optional[str]:

View File

@ -81,7 +81,7 @@ def KholdstareDefeatRule(state, player: int) -> bool:
state.has('Fire Rod', player) or state.has('Fire Rod', player) or
( (
state.has('Bombos', player) and state.has('Bombos', player) and
(state.has_sword(player) or state.world.swordless[player]) (state.has_sword(player) or state.multiworld.swordless[player])
) )
) and ) and
( (
@ -90,7 +90,7 @@ def KholdstareDefeatRule(state, player: int) -> bool:
( (
state.has('Fire Rod', player) and state.has('Fire Rod', player) and
state.has('Bombos', player) and state.has('Bombos', player) and
state.world.swordless[player] and state.multiworld.swordless[player] and
state.can_extend_magic(player, 16) state.can_extend_magic(player, 16)
) )
) )
@ -114,7 +114,7 @@ def AgahnimDefeatRule(state, player: int) -> bool:
def GanonDefeatRule(state, player: int) -> bool: def GanonDefeatRule(state, player: int) -> bool:
if state.world.swordless[player]: if state.multiworld.swordless[player]:
return state.has('Hammer', player) and \ return state.has('Hammer', player) and \
state.has_fire_source(player) and \ state.has_fire_source(player) and \
state.has('Silver Bow', player) and \ state.has('Silver Bow', player) and \
@ -123,7 +123,7 @@ def GanonDefeatRule(state, player: int) -> bool:
can_hurt = state.has_beam_sword(player) can_hurt = state.has_beam_sword(player)
common = can_hurt and state.has_fire_source(player) common = can_hurt and state.has_fire_source(player)
# silverless ganon may be needed in anything higher than no glitches # silverless ganon may be needed in anything higher than no glitches
if state.world.logic[player] != 'noglitches': if state.multiworld.logic[player] != 'noglitches':
# need to light torch a sufficient amount of times # need to light torch a sufficient amount of times
return common and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or ( return common and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (
state.has('Silver Bow', player) and state.can_shoot_arrows(player)) or state.has('Silver Bow', player) and state.can_shoot_arrows(player)) or

View File

@ -18,7 +18,7 @@ def create_dungeons(world, player):
dungeon.boss = BossFactory(default_boss, player) if default_boss else None dungeon.boss = BossFactory(default_boss, player) if default_boss else None
for region in dungeon.regions: for region in dungeon.regions:
world.get_region(region, player).dungeon = dungeon world.get_region(region, player).dungeon = dungeon
dungeon.world = world dungeon.multiworld = world
return dungeon return dungeon
ES = make_dungeon('Hyrule Castle', None, ['Hyrule Castle', 'Sewers', 'Sewer Drop', 'Sewers (Dark)', 'Sanctuary'], ES = make_dungeon('Hyrule Castle', None, ['Hyrule Castle', 'Sewers', 'Sewer Drop', 'Sewers (Dark)', 'Sanctuary'],

View File

@ -224,7 +224,7 @@ for diff in {'easy', 'normal', 'hard', 'expert'}:
def generate_itempool(world): def generate_itempool(world):
player = world.player player = world.player
world = world.world world = world.multiworld
if world.difficulty[player] not in difficulties: if world.difficulty[player] not in difficulties:
raise NotImplementedError(f"Diffulty {world.difficulty[player]}") raise NotImplementedError(f"Diffulty {world.difficulty[player]}")
@ -286,7 +286,7 @@ def generate_itempool(world):
region = world.get_region('Light World', player) region = world.get_region('Light World', player)
loc = ALttPLocation(player, "Murahdahla", parent=region) loc = ALttPLocation(player, "Murahdahla", parent=region)
loc.access_rule = lambda state: state.has_triforce_pieces(state.world.treasure_hunt_count[player], player) loc.access_rule = lambda state: state.has_triforce_pieces(state.multiworld.treasure_hunt_count[player], player)
region.locations.append(loc) region.locations.append(loc)
world.clear_location_cache() world.clear_location_cache()

View File

@ -13,7 +13,7 @@ from worlds.alttp.Options import smallkey_shuffle
def set_rules(world): def set_rules(world):
player = world.player player = world.player
world = world.world world = world.multiworld
if world.logic[player] == 'nologic': if world.logic[player] == 'nologic':
if player == next(player_id for player_id in world.get_game_players("A Link to the Past") if player == next(player_id for player_id in world.get_game_players("A Link to the Past")
if world.logic[player_id] == 'nologic'): # only warn one time if world.logic[player_id] == 'nologic'): # only warn one time
@ -81,7 +81,7 @@ def set_rules(world):
set_big_bomb_rules(world, player) set_big_bomb_rules(world, player)
if world.logic[player] in {'owglitches', 'hybridglitches', 'nologic'} and world.shuffle[player] not in {'insanity', 'insanity_legacy', 'madness'}: if world.logic[player] in {'owglitches', 'hybridglitches', 'nologic'} and world.shuffle[player] not in {'insanity', 'insanity_legacy', 'madness'}:
path_to_courtyard = mirrorless_path_to_castle_courtyard(world, player) path_to_courtyard = mirrorless_path_to_castle_courtyard(world, player)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.world.get_entrance('Dark Death Mountain Offset Mirror', player).can_reach(state) and all(rule(state) for rule in path_to_courtyard), 'or') add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.multiworld.get_entrance('Dark Death Mountain Offset Mirror', player).can_reach(state) and all(rule(state) for rule in path_to_courtyard), 'or')
else: else:
set_inverted_big_bomb_rules(world, player) set_inverted_big_bomb_rules(world, player)
@ -97,9 +97,9 @@ def set_rules(world):
set_trock_key_rules(world, player) set_trock_key_rules(world, player)
set_rule(ganons_tower, lambda state: state.has_crystals(state.world.crystals_needed_for_gt[player], player)) set_rule(ganons_tower, lambda state: state.has_crystals(state.multiworld.crystals_needed_for_gt[player], player))
if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']: if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']:
add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.world.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or') add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.multiworld.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or')
set_bunny_rules(world, player, world.mode[player] == 'inverted') set_bunny_rules(world, player, world.mode[player] == 'inverted')
@ -204,7 +204,7 @@ def global_rules(world, player):
((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or ((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or
(state.has('Cane of Byrna', player) and (state.has('Cane of Byrna', player) and
(state.can_extend_magic(player, 12, True) or (state.can_extend_magic(player, 12, True) or
(state.world.can_take_damage[player] and (state.has('Pegasus Boots', player) or state.has_hearts(player, 4)))))) (state.multiworld.can_take_damage[player] and (state.has('Pegasus Boots', player) or state.has_hearts(player, 4))))))
) )
set_rule(world.get_location('Hookshot Cave - Top Right', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_location('Hookshot Cave - Top Right', player), lambda state: state.has('Hookshot', player))
@ -242,12 +242,12 @@ def global_rules(world, player):
set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player)) set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player))
set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has('Pegasus Boots', player)) set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has('Pegasus Boots', player))
set_rule(world.get_entrance('Desert Palace East Wing', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player)) set_rule(world.get_entrance('Desert Palace East Wing', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player))
set_rule(world.get_location('Desert Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and state.world.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state)) set_rule(world.get_location('Desert Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and state.multiworld.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state))
set_rule(world.get_location('Desert Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and state.world.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state)) set_rule(world.get_location('Desert Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and state.multiworld.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state))
# logic patch to prevent placing a crystal in Desert that's required to reach the required keys # logic patch to prevent placing a crystal in Desert that's required to reach the required keys
if not (world.smallkey_shuffle[player] and world.bigkey_shuffle[player]): if not (world.smallkey_shuffle[player] and world.bigkey_shuffle[player]):
add_rule(world.get_location('Desert Palace - Prize', player), lambda state: state.world.get_region('Desert Palace Main (Outer)', player).can_reach(state)) add_rule(world.get_location('Desert Palace - Prize', player), lambda state: state.multiworld.get_region('Desert Palace Main (Outer)', player).can_reach(state))
set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Tower of Hera)', player) or item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player)) set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Tower of Hera)', player) or item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player))
set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player)) set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player))
@ -286,12 +286,12 @@ def global_rules(world, player):
set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player)) set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player))
set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state._lttp_has_key('Small Key (Ice Palace)', player, 2) or (state.has('Cane of Somaria', player) and state._lttp_has_key('Small Key (Ice Palace)', player, 1)))) set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state._lttp_has_key('Small Key (Ice Palace)', player, 2) or (state.has('Cane of Somaria', player) and state._lttp_has_key('Small Key (Ice Palace)', player, 1))))
set_rule(world.get_entrance('Ice Palace (East)', player), lambda state: (state.has('Hookshot', player) or ( set_rule(world.get_entrance('Ice Palace (East)', player), lambda state: (state.has('Hookshot', player) or (
item_in_locations(state, 'Big Key (Ice Palace)', player, [('Ice Palace - Spike Room', player), ('Ice Palace - Big Key Chest', player), ('Ice Palace - Map Chest', player)]) and state._lttp_has_key('Small Key (Ice Palace)', player))) and (state.world.can_take_damage[player] or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player))) item_in_locations(state, 'Big Key (Ice Palace)', player, [('Ice Palace - Spike Room', player), ('Ice Palace - Big Key Chest', player), ('Ice Palace - Map Chest', player)]) and state._lttp_has_key('Small Key (Ice Palace)', player))) and (state.multiworld.can_take_damage[player] or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player)))
set_rule(world.get_entrance('Ice Palace (East Top)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) set_rule(world.get_entrance('Ice Palace (East Top)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
set_rule(world.get_entrance('Misery Mire Entrance Gap', player), lambda state: (state.has('Pegasus Boots', player) or state.has('Hookshot', player)) and (state.has_sword(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player))) # need to defeat wizzrobes, bombs don't work ... set_rule(world.get_entrance('Misery Mire Entrance Gap', player), lambda state: (state.has('Pegasus Boots', player) or state.has('Hookshot', player)) and (state.has_sword(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player))) # need to defeat wizzrobes, bombs don't work ...
set_rule(world.get_location('Misery Mire - Big Chest', player), lambda state: state.has('Big Key (Misery Mire)', player)) set_rule(world.get_location('Misery Mire - Big Chest', player), lambda state: state.has('Big Key (Misery Mire)', player))
set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage[player] and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.multiworld.can_take_damage[player] and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player))
set_rule(world.get_entrance('Misery Mire Big Key Door', player), lambda state: state.has('Big Key (Misery Mire)', player)) set_rule(world.get_entrance('Misery Mire Big Key Door', player), lambda state: state.has('Big Key (Misery Mire)', player))
# you can squander the free small key from the pot by opening the south door to the north west switch room, locking you out of accessing a color switch ... # you can squander the free small key from the pot by opening the south door to the north west switch room, locking you out of accessing a color switch ...
# big key gives backdoor access to that from the teleporter in the north west # big key gives backdoor access to that from the teleporter in the north west
@ -377,11 +377,11 @@ def global_rules(world, player):
set_rule(world.get_location('Ganons Tower - Big Chest', player), lambda state: state.has('Big Key (Ganons Tower)', player)) set_rule(world.get_location('Ganons Tower - Big Chest', player), lambda state: state.has('Big Key (Ganons Tower)', player))
set_rule(world.get_location('Ganons Tower - Big Key Room - Left', player), set_rule(world.get_location('Ganons Tower - Big Key Room - Left', player),
lambda state: state.world.get_location('Ganons Tower - Big Key Room - Left', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) lambda state: state.multiworld.get_location('Ganons Tower - Big Key Room - Left', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
set_rule(world.get_location('Ganons Tower - Big Key Chest', player), set_rule(world.get_location('Ganons Tower - Big Key Chest', player),
lambda state: state.world.get_location('Ganons Tower - Big Key Chest', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) lambda state: state.multiworld.get_location('Ganons Tower - Big Key Chest', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
set_rule(world.get_location('Ganons Tower - Big Key Room - Right', player), set_rule(world.get_location('Ganons Tower - Big Key Room - Right', player),
lambda state: state.world.get_location('Ganons Tower - Big Key Room - Right', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) lambda state: state.multiworld.get_location('Ganons Tower - Big Key Room - Right', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
if world.enemy_shuffle[player]: if world.enemy_shuffle[player]:
set_rule(world.get_entrance('Ganons Tower Big Key Door', player), set_rule(world.get_entrance('Ganons Tower Big Key Door', player),
lambda state: state.has('Big Key (Ganons Tower)', player)) lambda state: state.has('Big Key (Ganons Tower)', player))
@ -389,22 +389,22 @@ def global_rules(world, player):
set_rule(world.get_entrance('Ganons Tower Big Key Door', player), set_rule(world.get_entrance('Ganons Tower Big Key Door', player),
lambda state: state.has('Big Key (Ganons Tower)', player) and state.can_shoot_arrows(player)) lambda state: state.has('Big Key (Ganons Tower)', player) and state.can_shoot_arrows(player))
set_rule(world.get_entrance('Ganons Tower Torch Rooms', player), set_rule(world.get_entrance('Ganons Tower Torch Rooms', player),
lambda state: state.has_fire_source(player) and state.world.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state)) lambda state: state.has_fire_source(player) and state.multiworld.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state))
set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest', player), set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest', player),
lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 3)) lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 3))
set_rule(world.get_entrance('Ganons Tower Moldorm Door', player), set_rule(world.get_entrance('Ganons Tower Moldorm Door', player),
lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 4)) lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 4))
set_rule(world.get_entrance('Ganons Tower Moldorm Gap', player), set_rule(world.get_entrance('Ganons Tower Moldorm Gap', player),
lambda state: state.has('Hookshot', player) and state.world.get_entrance('Ganons Tower Moldorm Gap', player).parent_region.dungeon.bosses['top'].can_defeat(state)) lambda state: state.has('Hookshot', player) and state.multiworld.get_entrance('Ganons Tower Moldorm Gap', player).parent_region.dungeon.bosses['top'].can_defeat(state))
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player)) set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player))
ganon = world.get_location('Ganon', player) ganon = world.get_location('Ganon', player)
set_rule(ganon, lambda state: GanonDefeatRule(state, player)) set_rule(ganon, lambda state: GanonDefeatRule(state, player))
if world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']: if world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
add_rule(ganon, lambda state: state.has_triforce_pieces(state.world.treasure_hunt_count[player], player)) add_rule(ganon, lambda state: state.has_triforce_pieces(state.multiworld.treasure_hunt_count[player], player))
elif world.goal[player] == 'ganonpedestal': elif world.goal[player] == 'ganonpedestal':
add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player)) add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player))
else: else:
add_rule(ganon, lambda state: state.has_crystals(state.world.crystals_needed_for_ganon[player], player)) add_rule(ganon, lambda state: state.has_crystals(state.multiworld.crystals_needed_for_ganon[player], player))
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
set_rule(world.get_location('Flute Activation Spot', player), lambda state: state.has('Flute', player)) set_rule(world.get_location('Flute Activation Spot', player), lambda state: state.has('Flute', player))
@ -942,7 +942,7 @@ def set_trock_key_rules(world, player):
if world.accessibility[player] != 'locations': if world.accessibility[player] != 'locations':
set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player
and state.can_reach(state.world.get_region('Turtle Rock (Second Section)', player))) and state.can_reach(state.multiworld.get_region('Turtle Rock (Second Section)', player)))
def set_big_bomb_rules(world, player): def set_big_bomb_rules(world, player):

View File

@ -163,7 +163,7 @@ class ALTTPWorld(World):
check_enemizer(self.enemizer_path) check_enemizer(self.enemizer_path)
player = self.player player = self.player
world = self.world world = self.multiworld
# system for sharing ER layouts # system for sharing ER layouts
self.er_seed = str(world.random.randint(0, 2 ** 64)) self.er_seed = str(world.random.randint(0, 2 ** 64))
@ -195,7 +195,7 @@ class ALTTPWorld(World):
def create_regions(self): def create_regions(self):
player = self.player player = self.player
world = self.world world = self.multiworld
world.triforce_pieces_available[player] = max(world.triforce_pieces_available[player], world.triforce_pieces_available[player] = max(world.triforce_pieces_available[player],
world.triforce_pieces_required[player]) world.triforce_pieces_required[player])
@ -219,21 +219,21 @@ class ALTTPWorld(World):
link_entrances(world, player) link_entrances(world, player)
mark_light_world_regions(world, player) mark_light_world_regions(world, player)
for region_name, entrance_name in indirect_connections_not_inverted.items(): for region_name, entrance_name in indirect_connections_not_inverted.items():
world.register_indirect_condition(self.world.get_region(region_name, player), world.register_indirect_condition(world.get_region(region_name, player),
self.world.get_entrance(entrance_name, player)) world.get_entrance(entrance_name, player))
else: else:
link_inverted_entrances(world, player) link_inverted_entrances(world, player)
mark_dark_world_regions(world, player) mark_dark_world_regions(world, player)
for region_name, entrance_name in indirect_connections_inverted.items(): for region_name, entrance_name in indirect_connections_inverted.items():
world.register_indirect_condition(self.world.get_region(region_name, player), world.register_indirect_condition(world.get_region(region_name, player),
self.world.get_entrance(entrance_name, player)) world.get_entrance(entrance_name, player))
world.random = old_random world.random = old_random
plando_connect(world, player) plando_connect(world, player)
for region_name, entrance_name in indirect_connections.items(): for region_name, entrance_name in indirect_connections.items():
world.register_indirect_condition(self.world.get_region(region_name, player), world.register_indirect_condition(world.get_region(region_name, player),
self.world.get_entrance(entrance_name, player)) world.get_entrance(entrance_name, player))
def collect_item(self, state: CollectionState, item: Item, remove=False): def collect_item(self, state: CollectionState, item: Item, remove=False):
@ -278,15 +278,15 @@ class ALTTPWorld(World):
if 'Sword' in item_name: if 'Sword' in item_name:
if state.has('Golden Sword', item.player): if state.has('Golden Sword', item.player):
pass pass
elif state.has('Tempered Sword', item.player) and self.world.difficulty_requirements[ elif state.has('Tempered Sword', item.player) and self.multiworld.difficulty_requirements[
item.player].progressive_sword_limit >= 4: item.player].progressive_sword_limit >= 4:
return 'Golden Sword' return 'Golden Sword'
elif state.has('Master Sword', item.player) and self.world.difficulty_requirements[ elif state.has('Master Sword', item.player) and self.multiworld.difficulty_requirements[
item.player].progressive_sword_limit >= 3: item.player].progressive_sword_limit >= 3:
return 'Tempered Sword' return 'Tempered Sword'
elif state.has('Fighter Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 2: elif state.has('Fighter Sword', item.player) and self.multiworld.difficulty_requirements[item.player].progressive_sword_limit >= 2:
return 'Master Sword' return 'Master Sword'
elif self.world.difficulty_requirements[item.player].progressive_sword_limit >= 1: elif self.multiworld.difficulty_requirements[item.player].progressive_sword_limit >= 1:
return 'Fighter Sword' return 'Fighter Sword'
elif 'Glove' in item_name: elif 'Glove' in item_name:
if state.has('Titans Mitts', item.player): if state.has('Titans Mitts', item.player):
@ -298,20 +298,20 @@ class ALTTPWorld(World):
elif 'Shield' in item_name: elif 'Shield' in item_name:
if state.has('Mirror Shield', item.player): if state.has('Mirror Shield', item.player):
return return
elif state.has('Red Shield', item.player) and self.world.difficulty_requirements[item.player].progressive_shield_limit >= 3: elif state.has('Red Shield', item.player) and self.multiworld.difficulty_requirements[item.player].progressive_shield_limit >= 3:
return 'Mirror Shield' return 'Mirror Shield'
elif state.has('Blue Shield', item.player) and self.world.difficulty_requirements[item.player].progressive_shield_limit >= 2: elif state.has('Blue Shield', item.player) and self.multiworld.difficulty_requirements[item.player].progressive_shield_limit >= 2:
return 'Red Shield' return 'Red Shield'
elif self.world.difficulty_requirements[item.player].progressive_shield_limit >= 1: elif self.multiworld.difficulty_requirements[item.player].progressive_shield_limit >= 1:
return 'Blue Shield' return 'Blue Shield'
elif 'Bow' in item_name: elif 'Bow' in item_name:
if state.has('Silver Bow', item.player): if state.has('Silver Bow', item.player):
return return
elif state.has('Bow', item.player) and (self.world.difficulty_requirements[item.player].progressive_bow_limit >= 2 elif state.has('Bow', item.player) and (self.multiworld.difficulty_requirements[item.player].progressive_bow_limit >= 2
or self.world.logic[item.player] == 'noglitches' or self.multiworld.logic[item.player] == 'noglitches'
or self.world.swordless[item.player]): # modes where silver bow is always required for ganon or self.multiworld.swordless[item.player]): # modes where silver bow is always required for ganon
return 'Silver Bow' return 'Silver Bow'
elif self.world.difficulty_requirements[item.player].progressive_bow_limit >= 1: elif self.multiworld.difficulty_requirements[item.player].progressive_bow_limit >= 1:
return 'Bow' return 'Bow'
elif item.advancement: elif item.advancement:
return item_name return item_name
@ -319,7 +319,7 @@ class ALTTPWorld(World):
def pre_fill(self): def pre_fill(self):
from Fill import fill_restrictive, FillError from Fill import fill_restrictive, FillError
attempts = 5 attempts = 5
world = self.world world = self.multiworld
player = self.player player = self.player
all_state = world.get_all_state(use_cache=True) all_state = world.get_all_state(use_cache=True)
crystals = [self.create_item(name) for name in ['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6']] crystals = [self.create_item(name) for name in ['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6']]
@ -362,7 +362,7 @@ class ALTTPWorld(World):
ShopSlotFill(world) ShopSlotFill(world)
def use_enemizer(self): def use_enemizer(self):
world = self.world world = self.multiworld
player = self.player player = self.player
return (world.boss_shuffle[player] or world.enemy_shuffle[player] return (world.boss_shuffle[player] or world.enemy_shuffle[player]
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
@ -370,7 +370,7 @@ class ALTTPWorld(World):
or world.killable_thieves[player]) or world.killable_thieves[player])
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
world = self.world world = self.multiworld
player = self.player player = self.player
try: try:
use_enemizer = self.use_enemizer() use_enemizer = self.use_enemizer()
@ -409,7 +409,7 @@ class ALTTPWorld(World):
deathlink=world.death_link[player], deathlink=world.death_link[player],
allowcollect=world.allow_collect[player]) allowcollect=world.allow_collect[player])
rompath = os.path.join(output_directory, f"{self.world.get_out_file_name_base(self.player)}.sfc") rompath = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.sfc")
rom.write_to_file(rompath) rom.write_to_file(rompath)
patch = LttPDeltaPatch(os.path.splitext(rompath)[0]+LttPDeltaPatch.patch_file_ending, player=player, patch = LttPDeltaPatch(os.path.splitext(rompath)[0]+LttPDeltaPatch.patch_file_ending, player=player,
player_name=world.player_name[player], patched_path=rompath) player_name=world.player_name[player], patched_path=rompath)
@ -443,7 +443,7 @@ class ALTTPWorld(World):
# we skip in case of error, so that the original error in the output thread is the one that gets raised # we skip in case of error, so that the original error in the output thread is the one that gets raised
if rom_name: if rom_name:
new_name = base64.b64encode(bytes(self.rom_name)).decode() new_name = base64.b64encode(bytes(self.rom_name)).decode()
multidata["connect_names"][new_name] = multidata["connect_names"][self.world.player_name[self.player]] multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
return ALttPItem(name, self.player, **item_init_table[name]) return ALttPItem(name, self.player, **item_init_table[name])
@ -510,16 +510,16 @@ class ALTTPWorld(World):
logging.warning(f"Could not trash fill Ganon's Tower for player {player}.") logging.warning(f"Could not trash fill Ganon's Tower for player {player}.")
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
if self.world.goal[self.player] == "icerodhunt": if self.multiworld.goal[self.player] == "icerodhunt":
item = "Nothing" item = "Nothing"
else: else:
item = self.world.random.choice(extras_list) item = self.multiworld.random.choice(extras_list)
return GetBeemizerItem(self.world, self.player, item) return GetBeemizerItem(self.multiworld, self.player, item)
def get_pre_fill_items(self): def get_pre_fill_items(self):
res = [] res = []
if self.dungeon_local_item_names: if self.dungeon_local_item_names:
for (name, player), dungeon in self.world.dungeons.items(): for (name, player), dungeon in self.multiworld.dungeons.items():
if player == self.player: if player == self.player:
for item in dungeon.all_items: for item in dungeon.all_items:
if item.name in self.dungeon_local_item_names: if item.name in self.dungeon_local_item_names:
@ -538,8 +538,8 @@ def get_same_seed(world, seed_def: tuple) -> str:
class ALttPLogic(LogicMixin): class ALttPLogic(LogicMixin):
def _lttp_has_key(self, item, player, count: int = 1): def _lttp_has_key(self, item, player, count: int = 1):
if self.world.logic[player] == 'nologic': if self.multiworld.logic[player] == 'nologic':
return True return True
if self.world.smallkey_shuffle[player] == smallkey_shuffle.option_universal: if self.multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal:
return self.can_buy_unlimited('Small Key (Universal)', player) return self.can_buy_unlimited('Small Key (Universal)', player)
return self.prog_items[item, player] >= count return self.prog_items[item, player] >= count

View File

@ -43,7 +43,7 @@ class ArchipIDLEWorld(World):
def generate_basic(self): def generate_basic(self):
item_table_copy = list(item_table) item_table_copy = list(item_table)
self.world.random.shuffle(item_table_copy) self.multiworld.random.shuffle(item_table_copy)
item_pool = [] item_pool = []
for i in range(100): for i in range(100):
@ -55,31 +55,31 @@ class ArchipIDLEWorld(World):
) )
item_pool.append(item) item_pool.append(item)
self.world.itempool += item_pool self.multiworld.itempool += item_pool
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
return Item(name, ItemClassification.progression, self.item_name_to_id[name], self.player) return Item(name, ItemClassification.progression, self.item_name_to_id[name], self.player)
def create_regions(self): def create_regions(self):
self.world.regions += [ self.multiworld.regions += [
create_region(self.world, self.player, 'Menu', None, ['Entrance to IDLE Zone']), create_region(self.multiworld, self.player, 'Menu', None, ['Entrance to IDLE Zone']),
create_region(self.world, self.player, 'IDLE Zone', self.location_name_to_id) create_region(self.multiworld, self.player, 'IDLE Zone', self.location_name_to_id)
] ]
# link up our region with the entrance we just made # link up our region with the entrance we just made
self.world.get_entrance('Entrance to IDLE Zone', self.player)\ self.multiworld.get_entrance('Entrance to IDLE Zone', self.player)\
.connect(self.world.get_region('IDLE Zone', self.player)) .connect(self.multiworld.get_region('IDLE Zone', self.player))
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return self.world.random.choice(item_table) return self.multiworld.random.choice(item_table)
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
region = Region(name, RegionType.Generic, name, player) region = Region(name, RegionType.Generic, name, player)
region.world = world region.multiworld = world
if locations: if locations:
for location_name in locations.keys(): for location_name in locations.keys():
location = ArchipIDLELocation(player, location_name, locations[location_name], region) location = ArchipIDLELocation(player, location_name, locations[location_name], region)

View File

@ -38,12 +38,12 @@ class ChecksFinderWorld(World):
def _get_checksfinder_data(self): def _get_checksfinder_data(self):
return { return {
'world_seed': self.world.slot_seeds[self.player].getrandbits(32), 'world_seed': self.multiworld.slot_seeds[self.player].getrandbits(32),
'seed_name': self.world.seed_name, 'seed_name': self.multiworld.seed_name,
'player_name': self.world.get_player_name(self.player), 'player_name': self.multiworld.get_player_name(self.player),
'player_id': self.player, 'player_id': self.player,
'client_version': client_version, 'client_version': client_version,
'race': self.world.is_race, 'race': self.multiworld.is_race,
} }
def generate_basic(self): def generate_basic(self):
@ -61,15 +61,15 @@ class ChecksFinderWorld(World):
# Convert itempool into real items # Convert itempool into real items
itempool = [item for item in map(lambda name: self.create_item(name), itempool)] itempool = [item for item in map(lambda name: self.create_item(name), itempool)]
self.world.itempool += itempool self.multiworld.itempool += itempool
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)
set_completion_rules(self.world, self.player) set_completion_rules(self.multiworld, self.player)
def create_regions(self): def create_regions(self):
def ChecksFinderRegion(region_name: str, exits=[]): def ChecksFinderRegion(region_name: str, exits=[]):
ret = Region(region_name, RegionType.Generic, region_name, self.player, self.world) ret = Region(region_name, RegionType.Generic, region_name, self.player, self.multiworld)
ret.locations = [ChecksFinderAdvancement(self.player, loc_name, loc_data.id, ret) ret.locations = [ChecksFinderAdvancement(self.player, loc_name, loc_data.id, ret)
for loc_name, loc_data in advancement_table.items() for loc_name, loc_data in advancement_table.items()
if loc_data.region == region_name] if loc_data.region == region_name]
@ -77,13 +77,13 @@ class ChecksFinderWorld(World):
ret.exits.append(Entrance(self.player, exit, ret)) ret.exits.append(Entrance(self.player, exit, ret))
return ret return ret
self.world.regions += [ChecksFinderRegion(*r) for r in checksfinder_regions] self.multiworld.regions += [ChecksFinderRegion(*r) for r in checksfinder_regions]
link_checksfinder_structures(self.world, self.player) link_checksfinder_structures(self.multiworld, self.player)
def fill_slot_data(self): def fill_slot_data(self):
slot_data = self._get_checksfinder_data() slot_data = self._get_checksfinder_data()
for option_name in checksfinder_options: for option_name in checksfinder_options:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}: if slot_data.get(option_name, None) is None and type(option.value) in {str, int}:
slot_data[option_name] = int(option.value) slot_data[option_name] = int(option.value)
return slot_data return slot_data

View File

@ -79,7 +79,7 @@ class DarkSouls3World(World):
def create_regions(self): def create_regions(self):
menu_region = Region("Menu", RegionType.Generic, "Menu", self.player) menu_region = Region("Menu", RegionType.Generic, "Menu", self.player)
self.world.regions.append(menu_region) self.multiworld.regions.append(menu_region)
# Create all Vanilla regions of Dark Souls III # Create all Vanilla regions of Dark Souls III
firelink_shrine_region = self.create_region("Firelink Shrine", fire_link_shrine_table) firelink_shrine_region = self.create_region("Firelink Shrine", fire_link_shrine_table)
@ -106,71 +106,71 @@ class DarkSouls3World(World):
# Create the entrance to connect those regions # Create the entrance to connect those regions
menu_region.exits.append(Entrance(self.player, "New Game", menu_region)) menu_region.exits.append(Entrance(self.player, "New Game", menu_region))
self.world.get_entrance("New Game", self.player).connect(firelink_shrine_region) self.multiworld.get_entrance("New Game", self.player).connect(firelink_shrine_region)
firelink_shrine_region.exits.append(Entrance(self.player, "Goto High Wall of Lothric", firelink_shrine_region.exits.append(Entrance(self.player, "Goto High Wall of Lothric",
firelink_shrine_region)) firelink_shrine_region))
firelink_shrine_region.exits.append(Entrance(self.player, "Goto Kiln Of The First Flame", firelink_shrine_region.exits.append(Entrance(self.player, "Goto Kiln Of The First Flame",
firelink_shrine_region)) firelink_shrine_region))
firelink_shrine_region.exits.append(Entrance(self.player, "Goto Bell Tower", firelink_shrine_region.exits.append(Entrance(self.player, "Goto Bell Tower",
firelink_shrine_region)) firelink_shrine_region))
self.world.get_entrance("Goto High Wall of Lothric", self.player).connect(high_wall_of_lothric_region) self.multiworld.get_entrance("Goto High Wall of Lothric", self.player).connect(high_wall_of_lothric_region)
self.world.get_entrance("Goto Kiln Of The First Flame", self.player).connect(kiln_of_the_first_flame_region) self.multiworld.get_entrance("Goto Kiln Of The First Flame", self.player).connect(kiln_of_the_first_flame_region)
self.world.get_entrance("Goto Bell Tower", self.player).connect(firelink_shrine_bell_tower_region) self.multiworld.get_entrance("Goto Bell Tower", self.player).connect(firelink_shrine_bell_tower_region)
high_wall_of_lothric_region.exits.append(Entrance(self.player, "Goto Undead Settlement", high_wall_of_lothric_region.exits.append(Entrance(self.player, "Goto Undead Settlement",
high_wall_of_lothric_region)) high_wall_of_lothric_region))
high_wall_of_lothric_region.exits.append(Entrance(self.player, "Goto Lothric Castle", high_wall_of_lothric_region.exits.append(Entrance(self.player, "Goto Lothric Castle",
high_wall_of_lothric_region)) high_wall_of_lothric_region))
self.world.get_entrance("Goto Undead Settlement", self.player).connect(undead_settlement_region) self.multiworld.get_entrance("Goto Undead Settlement", self.player).connect(undead_settlement_region)
self.world.get_entrance("Goto Lothric Castle", self.player).connect(lothric_castle_region) self.multiworld.get_entrance("Goto Lothric Castle", self.player).connect(lothric_castle_region)
undead_settlement_region.exits.append(Entrance(self.player, "Goto Road Of Sacrifices", undead_settlement_region.exits.append(Entrance(self.player, "Goto Road Of Sacrifices",
undead_settlement_region)) undead_settlement_region))
self.world.get_entrance("Goto Road Of Sacrifices", self.player).connect(road_of_sacrifices_region) self.multiworld.get_entrance("Goto Road Of Sacrifices", self.player).connect(road_of_sacrifices_region)
road_of_sacrifices_region.exits.append(Entrance(self.player, "Goto Cathedral", road_of_sacrifices_region)) road_of_sacrifices_region.exits.append(Entrance(self.player, "Goto Cathedral", road_of_sacrifices_region))
road_of_sacrifices_region.exits.append(Entrance(self.player, "Goto Farron keep", road_of_sacrifices_region)) road_of_sacrifices_region.exits.append(Entrance(self.player, "Goto Farron keep", road_of_sacrifices_region))
self.world.get_entrance("Goto Cathedral", self.player).connect(cathedral_of_the_deep_region) self.multiworld.get_entrance("Goto Cathedral", self.player).connect(cathedral_of_the_deep_region)
self.world.get_entrance("Goto Farron keep", self.player).connect(farron_keep_region) self.multiworld.get_entrance("Goto Farron keep", self.player).connect(farron_keep_region)
farron_keep_region.exits.append(Entrance(self.player, "Goto Carthus catacombs", farron_keep_region)) farron_keep_region.exits.append(Entrance(self.player, "Goto Carthus catacombs", farron_keep_region))
self.world.get_entrance("Goto Carthus catacombs", self.player).connect(catacombs_of_carthus_region) self.multiworld.get_entrance("Goto Carthus catacombs", self.player).connect(catacombs_of_carthus_region)
catacombs_of_carthus_region.exits.append(Entrance(self.player, "Goto Irithyll of the boreal", catacombs_of_carthus_region.exits.append(Entrance(self.player, "Goto Irithyll of the boreal",
catacombs_of_carthus_region)) catacombs_of_carthus_region))
catacombs_of_carthus_region.exits.append(Entrance(self.player, "Goto Smouldering Lake", catacombs_of_carthus_region.exits.append(Entrance(self.player, "Goto Smouldering Lake",
catacombs_of_carthus_region)) catacombs_of_carthus_region))
self.world.get_entrance("Goto Irithyll of the boreal", self.player).\ self.multiworld.get_entrance("Goto Irithyll of the boreal", self.player).\
connect(irithyll_of_the_boreal_valley_region) connect(irithyll_of_the_boreal_valley_region)
self.world.get_entrance("Goto Smouldering Lake", self.player).connect(smouldering_lake_region) self.multiworld.get_entrance("Goto Smouldering Lake", self.player).connect(smouldering_lake_region)
irithyll_of_the_boreal_valley_region.exits.append(Entrance(self.player, "Goto Irithyll dungeon", irithyll_of_the_boreal_valley_region.exits.append(Entrance(self.player, "Goto Irithyll dungeon",
irithyll_of_the_boreal_valley_region)) irithyll_of_the_boreal_valley_region))
irithyll_of_the_boreal_valley_region.exits.append(Entrance(self.player, "Goto Anor Londo", irithyll_of_the_boreal_valley_region.exits.append(Entrance(self.player, "Goto Anor Londo",
irithyll_of_the_boreal_valley_region)) irithyll_of_the_boreal_valley_region))
self.world.get_entrance("Goto Irithyll dungeon", self.player).connect(irithyll_dungeon_region) self.multiworld.get_entrance("Goto Irithyll dungeon", self.player).connect(irithyll_dungeon_region)
self.world.get_entrance("Goto Anor Londo", self.player).connect(anor_londo_region) self.multiworld.get_entrance("Goto Anor Londo", self.player).connect(anor_londo_region)
irithyll_dungeon_region.exits.append(Entrance(self.player, "Goto Archdragon peak", irithyll_dungeon_region)) irithyll_dungeon_region.exits.append(Entrance(self.player, "Goto Archdragon peak", irithyll_dungeon_region))
irithyll_dungeon_region.exits.append(Entrance(self.player, "Goto Profaned capital", irithyll_dungeon_region)) irithyll_dungeon_region.exits.append(Entrance(self.player, "Goto Profaned capital", irithyll_dungeon_region))
self.world.get_entrance("Goto Archdragon peak", self.player).connect(archdragon_peak_region) self.multiworld.get_entrance("Goto Archdragon peak", self.player).connect(archdragon_peak_region)
self.world.get_entrance("Goto Profaned capital", self.player).connect(profaned_capital_region) self.multiworld.get_entrance("Goto Profaned capital", self.player).connect(profaned_capital_region)
lothric_castle_region.exits.append(Entrance(self.player, "Goto Consumed King Garden", lothric_castle_region)) lothric_castle_region.exits.append(Entrance(self.player, "Goto Consumed King Garden", lothric_castle_region))
lothric_castle_region.exits.append(Entrance(self.player, "Goto Grand Archives", lothric_castle_region)) lothric_castle_region.exits.append(Entrance(self.player, "Goto Grand Archives", lothric_castle_region))
self.world.get_entrance("Goto Consumed King Garden", self.player).connect(consumed_king_garden_region) self.multiworld.get_entrance("Goto Consumed King Garden", self.player).connect(consumed_king_garden_region)
self.world.get_entrance("Goto Grand Archives", self.player).connect(grand_archives_region) self.multiworld.get_entrance("Goto Grand Archives", self.player).connect(grand_archives_region)
consumed_king_garden_region.exits.append(Entrance(self.player, "Goto Untended Graves", consumed_king_garden_region.exits.append(Entrance(self.player, "Goto Untended Graves",
consumed_king_garden_region)) consumed_king_garden_region))
self.world.get_entrance("Goto Untended Graves", self.player).connect(untended_graves_region) self.multiworld.get_entrance("Goto Untended Graves", self.player).connect(untended_graves_region)
# For each region, add the associated locations retrieved from the corresponding location_table # For each region, add the associated locations retrieved from the corresponding location_table
def create_region(self, region_name, location_table) -> Region: def create_region(self, region_name, location_table) -> Region:
new_region = Region(region_name, RegionType.Generic, region_name, self.player, self.world) new_region = Region(region_name, RegionType.Generic, region_name, self.player, self.multiworld)
if location_table: if location_table:
for name, address in location_table.items(): for name, address in location_table.items():
location = DarkSouls3Location(self.player, name, self.location_name_to_id[name], new_region) location = DarkSouls3Location(self.player, name, self.location_name_to_id[name], new_region)
new_region.locations.append(location) new_region.locations.append(location)
self.world.regions.append(new_region) self.multiworld.regions.append(new_region)
return new_region return new_region
def create_items(self): def create_items(self):
for name, address in self.item_name_to_id.items(): for name, address in self.item_name_to_id.items():
# Specific items will be included in the item pool under certain conditions. See generate_basic # Specific items will be included in the item pool under certain conditions. See generate_basic
if name != "Basin of Vows": if name != "Basin of Vows":
self.world.itempool += [self.create_item(name)] self.multiworld.itempool += [self.create_item(name)]
def generate_early(self): def generate_early(self):
pass pass
@ -178,46 +178,46 @@ class DarkSouls3World(World):
def set_rules(self) -> None: def set_rules(self) -> None:
# Define the access rules to the entrances # Define the access rules to the entrances
set_rule(self.world.get_entrance("Goto Bell Tower", self.player), set_rule(self.multiworld.get_entrance("Goto Bell Tower", self.player),
lambda state: state.has("Tower Key", self.player)) lambda state: state.has("Tower Key", self.player))
set_rule(self.world.get_entrance("Goto Undead Settlement", self.player), set_rule(self.multiworld.get_entrance("Goto Undead Settlement", self.player),
lambda state: state.has("Small Lothric Banner", self.player)) lambda state: state.has("Small Lothric Banner", self.player))
set_rule(self.world.get_entrance("Goto Lothric Castle", self.player), set_rule(self.multiworld.get_entrance("Goto Lothric Castle", self.player),
lambda state: state.has("Basin of Vows", self.player)) lambda state: state.has("Basin of Vows", self.player))
set_rule(self.world.get_entrance("Goto Irithyll of the boreal", self.player), set_rule(self.multiworld.get_entrance("Goto Irithyll of the boreal", self.player),
lambda state: state.has("Small Doll", self.player)) lambda state: state.has("Small Doll", self.player))
set_rule(self.world.get_entrance("Goto Archdragon peak", self.player), set_rule(self.multiworld.get_entrance("Goto Archdragon peak", self.player),
lambda state: state.can_reach("CKG: Soul of Consumed Oceiros", "Location", self.player)) lambda state: state.can_reach("CKG: Soul of Consumed Oceiros", "Location", self.player))
set_rule(self.world.get_entrance("Goto Profaned capital", self.player), set_rule(self.multiworld.get_entrance("Goto Profaned capital", self.player),
lambda state: state.has("Storm Ruler", self.player)) lambda state: state.has("Storm Ruler", self.player))
set_rule(self.world.get_entrance("Goto Grand Archives", self.player), set_rule(self.multiworld.get_entrance("Goto Grand Archives", self.player),
lambda state: state.has("Grand Archives Key", self.player)) lambda state: state.has("Grand Archives Key", self.player))
set_rule(self.world.get_entrance("Goto Kiln Of The First Flame", self.player), set_rule(self.multiworld.get_entrance("Goto Kiln Of The First Flame", self.player),
lambda state: state.has("Cinders of a Lord - Abyss Watcher", self.player) and lambda state: state.has("Cinders of a Lord - Abyss Watcher", self.player) and
state.has("Cinders of a Lord - Yhorm the Giant", self.player) and state.has("Cinders of a Lord - Yhorm the Giant", self.player) and
state.has("Cinders of a Lord - Aldrich", self.player) and state.has("Cinders of a Lord - Aldrich", self.player) and
state.has("Cinders of a Lord - Lothric Prince", self.player)) state.has("Cinders of a Lord - Lothric Prince", self.player))
# Define the access rules to some specific locations # Define the access rules to some specific locations
set_rule(self.world.get_location("HWL: Soul of the Dancer", self.player), set_rule(self.multiworld.get_location("HWL: Soul of the Dancer", self.player),
lambda state: state.has("Basin of Vows", self.player)) lambda state: state.has("Basin of Vows", self.player))
set_rule(self.world.get_location("HWL: Greirat's Ashes", self.player), set_rule(self.multiworld.get_location("HWL: Greirat's Ashes", self.player),
lambda state: state.has("Cell Key", self.player)) lambda state: state.has("Cell Key", self.player))
set_rule(self.world.get_location("ID: Bellowing Dragoncrest Ring", self.player), set_rule(self.multiworld.get_location("ID: Bellowing Dragoncrest Ring", self.player),
lambda state: state.has("Jailbreaker's Key", self.player)) lambda state: state.has("Jailbreaker's Key", self.player))
set_rule(self.world.get_location("ID: Prisoner Chief's Ashes", self.player), set_rule(self.multiworld.get_location("ID: Prisoner Chief's Ashes", self.player),
lambda state: state.has("Jailer's Key Ring", self.player)) lambda state: state.has("Jailer's Key Ring", self.player))
set_rule(self.world.get_location("ID: Covetous Gold Serpent Ring", self.player), set_rule(self.multiworld.get_location("ID: Covetous Gold Serpent Ring", self.player),
lambda state: state.has("Old Cell Key", self.player)) lambda state: state.has("Old Cell Key", self.player))
set_rule(self.world.get_location("ID: Karla's Ashes", self.player), set_rule(self.multiworld.get_location("ID: Karla's Ashes", self.player),
lambda state: state.has("Jailer's Key Ring", self.player)) lambda state: state.has("Jailer's Key Ring", self.player))
black_hand_gotthard_corpse_rule = lambda state: \ black_hand_gotthard_corpse_rule = lambda state: \
(state.can_reach("AL: Cinders of a Lord - Aldrich", "Location", self.player) and (state.can_reach("AL: Cinders of a Lord - Aldrich", "Location", self.player) and
state.can_reach("PC: Cinders of a Lord - Yhorm the Giant", "Location", self.player)) state.can_reach("PC: Cinders of a Lord - Yhorm the Giant", "Location", self.player))
set_rule(self.world.get_location("LC: Grand Archives Key", self.player), black_hand_gotthard_corpse_rule) set_rule(self.multiworld.get_location("LC: Grand Archives Key", self.player), black_hand_gotthard_corpse_rule)
set_rule(self.world.get_location("LC: Gotthard Twinswords", self.player), black_hand_gotthard_corpse_rule) set_rule(self.multiworld.get_location("LC: Gotthard Twinswords", self.player), black_hand_gotthard_corpse_rule)
self.world.completion_condition[self.player] = lambda state: \ self.multiworld.completion_condition[self.player] = lambda state: \
state.has("Cinders of a Lord - Abyss Watcher", self.player) and \ state.has("Cinders of a Lord - Abyss Watcher", self.player) and \
state.has("Cinders of a Lord - Yhorm the Giant", self.player) and \ state.has("Cinders of a Lord - Yhorm the Giant", self.player) and \
state.has("Cinders of a Lord - Aldrich", self.player) and \ state.has("Cinders of a Lord - Aldrich", self.player) and \
@ -226,30 +226,30 @@ class DarkSouls3World(World):
def generate_basic(self): def generate_basic(self):
# Depending on the specified option, add the Basin of Vows to a specific location or to the item pool # Depending on the specified option, add the Basin of Vows to a specific location or to the item pool
item = self.create_item("Basin of Vows") item = self.create_item("Basin of Vows")
if self.world.late_basin_of_vows[self.player]: if self.multiworld.late_basin_of_vows[self.player]:
self.world.get_location("IBV: Soul of Pontiff Sulyvahn", self.player).place_locked_item(item) self.multiworld.get_location("IBV: Soul of Pontiff Sulyvahn", self.player).place_locked_item(item)
else: else:
self.world.itempool += [item] self.multiworld.itempool += [item]
# Fill item pool with additional items # Fill item pool with additional items
item_pool_len = self.item_name_to_id.__len__() item_pool_len = self.item_name_to_id.__len__()
total_required_locations = self.location_name_to_id.__len__() total_required_locations = self.location_name_to_id.__len__()
for i in range(item_pool_len, total_required_locations): for i in range(item_pool_len, total_required_locations):
self.world.itempool += [self.create_item("Soul of an Intrepid Hero")] self.multiworld.itempool += [self.create_item("Soul of an Intrepid Hero")]
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
# Depending on the specified option, modify items hexadecimal value to add an upgrade level # Depending on the specified option, modify items hexadecimal value to add an upgrade level
item_dictionary_copy = item_dictionary.copy() item_dictionary_copy = item_dictionary.copy()
if self.world.randomize_weapons_level[self.player]: if self.multiworld.randomize_weapons_level[self.player]:
# Randomize some weapons upgrades # Randomize some weapons upgrades
for name in weapons_upgrade_5_table.keys(): for name in weapons_upgrade_5_table.keys():
if self.world.random.randint(0, 100) < 33: if self.multiworld.random.randint(0, 100) < 33:
value = self.world.random.randint(1, 5) value = self.multiworld.random.randint(1, 5)
item_dictionary_copy[name] += value item_dictionary_copy[name] += value
for name in weapons_upgrade_10_table.keys(): for name in weapons_upgrade_10_table.keys():
if self.world.random.randint(0, 100) < 33: if self.multiworld.random.randint(0, 100) < 33:
value = self.world.random.randint(1, 10) value = self.multiworld.random.randint(1, 10)
item_dictionary_copy[name] += value item_dictionary_copy[name] += value
# Create the mandatory lists to generate the player's output file # Create the mandatory lists to generate the player's output file
@ -258,7 +258,7 @@ class DarkSouls3World(World):
locations_id = [] locations_id = []
locations_address = [] locations_address = []
locations_target = [] locations_target = []
for location in self.world.get_filled_locations(): for location in self.multiworld.get_filled_locations():
if location.item.player == self.player: if location.item.player == self.player:
items_id.append(location.item.code) items_id.append(location.item.code)
items_address.append(item_dictionary[location.item.name]) items_address.append(item_dictionary[location.item.name])
@ -273,12 +273,12 @@ class DarkSouls3World(World):
data = { data = {
"options": { "options": {
"auto_equip": self.world.auto_equip[self.player].value, "auto_equip": self.multiworld.auto_equip[self.player].value,
"lock_equip": self.world.lock_equip[self.player].value, "lock_equip": self.multiworld.lock_equip[self.player].value,
"no_weapon_requirements": self.world.no_weapon_requirements[self.player].value, "no_weapon_requirements": self.multiworld.no_weapon_requirements[self.player].value,
}, },
"seed": self.world.seed_name, # to verify the server's multiworld "seed": self.multiworld.seed_name, # to verify the server's multiworld
"slot": self.world.player_name[self.player], # to connect to server "slot": self.multiworld.player_name[self.player], # to connect to server
"base_id": self.base_id, # to merge location and items lists "base_id": self.base_id, # to merge location and items lists
"locationsId": locations_id, "locationsId": locations_id,
"locationsAddress": locations_address, "locationsAddress": locations_address,
@ -288,6 +288,6 @@ class DarkSouls3World(World):
} }
# generate the file # generate the file
filename = f"AP-{self.world.seed_name}-P{self.player}-{self.world.player_name[self.player]}.json" filename = f"AP-{self.multiworld.seed_name}-P{self.player}-{self.multiworld.player_name[self.player]}.json"
with open(os.path.join(output_directory, filename), 'w') as outfile: with open(os.path.join(output_directory, filename), 'w') as outfile:
json.dump(data, outfile) json.dump(data, outfile)

View File

@ -913,7 +913,7 @@ def connect_regions(world, player, level_list):
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None): def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None):
# Shamelessly stolen from the ROR2 definition # Shamelessly stolen from the ROR2 definition
ret = Region(name, None, name, player) ret = Region(name, None, name, player)
ret.world = world ret.multiworld = world
if locations: if locations:
for locationName, locationData in locations.items(): for locationName, locationData in locations.items():
loc_id = active_locations.get(locationName, 0) loc_id = active_locations.get(locationName, 0)

View File

@ -69,13 +69,13 @@ class DKC3World(World):
def fill_slot_data(self) -> dict: def fill_slot_data(self) -> dict:
slot_data = self._get_slot_data() slot_data = self._get_slot_data()
for option_name in dkc3_options: for option_name in dkc3_options:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
slot_data[option_name] = option.value slot_data[option_name] = option.value
return slot_data return slot_data
def generate_basic(self): def generate_basic(self):
self.topology_present = self.world.level_shuffle[self.player].value self.topology_present = self.multiworld.level_shuffle[self.player].value
itempool: typing.List[DKC3Item] = [] itempool: typing.List[DKC3Item] = []
# Levels # Levels
@ -85,14 +85,14 @@ class DKC3World(World):
# Rocket Rush Cog # Rocket Rush Cog
total_required_locations -= 1 total_required_locations -= 1
number_of_cogs = 4 number_of_cogs = 4
self.world.get_location(LocationName.rocket_rush_flag, self.player).place_locked_item(self.create_item(ItemName.krematoa_cog)) self.multiworld.get_location(LocationName.rocket_rush_flag, self.player).place_locked_item(self.create_item(ItemName.krematoa_cog))
number_of_bosses = 8 number_of_bosses = 8
if self.world.goal[self.player] == "knautilus": if self.multiworld.goal[self.player] == "knautilus":
self.world.get_location(LocationName.kastle_kaos, self.player).place_locked_item(self.create_item(ItemName.victory)) self.multiworld.get_location(LocationName.kastle_kaos, self.player).place_locked_item(self.create_item(ItemName.victory))
number_of_bosses = 7 number_of_bosses = 7
else: else:
self.world.get_location(LocationName.banana_bird_mother, self.player).place_locked_item(self.create_item(ItemName.victory)) self.multiworld.get_location(LocationName.banana_bird_mother, self.player).place_locked_item(self.create_item(ItemName.victory))
number_of_banana_birds = self.world.number_of_banana_birds[self.player] number_of_banana_birds = self.multiworld.number_of_banana_birds[self.player]
# Bosses # Bosses
total_required_locations += number_of_bosses total_required_locations += number_of_bosses
@ -100,15 +100,15 @@ class DKC3World(World):
# Secret Caves # Secret Caves
total_required_locations += 13 total_required_locations += 13
if self.world.kongsanity[self.player]: if self.multiworld.kongsanity[self.player]:
total_required_locations += 39 total_required_locations += 39
## Brothers Bear ## Brothers Bear
if False:#self.world.include_trade_sequence[self.player]: if False:#self.world.include_trade_sequence[self.player]:
total_required_locations += 10 total_required_locations += 10
number_of_bonus_coins = (self.world.krematoa_bonus_coin_cost[self.player] * 5) number_of_bonus_coins = (self.multiworld.krematoa_bonus_coin_cost[self.player] * 5)
number_of_bonus_coins += math.ceil((85 - number_of_bonus_coins) * self.world.percentage_of_extra_bonus_coins[self.player] / 100) number_of_bonus_coins += math.ceil((85 - number_of_bonus_coins) * self.multiworld.percentage_of_extra_bonus_coins[self.player] / 100)
itempool += [self.create_item(ItemName.bonus_coin) for _ in range(number_of_bonus_coins)] itempool += [self.create_item(ItemName.bonus_coin) for _ in range(number_of_bonus_coins)]
itempool += [self.create_item(ItemName.dk_coin) for _ in range(41)] itempool += [self.create_item(ItemName.dk_coin) for _ in range(41)]
@ -119,27 +119,27 @@ class DKC3World(World):
total_junk_count = total_required_locations - len(itempool) total_junk_count = total_required_locations - len(itempool)
junk_pool = [] junk_pool = []
for item_name in self.world.random.choices(list(junk_table.keys()), k=total_junk_count): for item_name in self.multiworld.random.choices(list(junk_table.keys()), k=total_junk_count):
junk_pool.append(self.create_item(item_name)) junk_pool.append(self.create_item(item_name))
itempool += junk_pool itempool += junk_pool
self.active_level_list = level_list.copy() self.active_level_list = level_list.copy()
if self.world.level_shuffle[self.player]: if self.multiworld.level_shuffle[self.player]:
self.world.random.shuffle(self.active_level_list) self.multiworld.random.shuffle(self.active_level_list)
connect_regions(self.world, self.player, self.active_level_list) connect_regions(self.multiworld, self.player, self.active_level_list)
self.world.itempool += itempool self.multiworld.itempool += itempool
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
try: try:
world = self.world world = self.multiworld
player = self.player player = self.player
rom = LocalRom(get_base_rom_path()) rom = LocalRom(get_base_rom_path())
patch_rom(self.world, rom, self.player, self.active_level_list) patch_rom(self.multiworld, rom, self.player, self.active_level_list)
self.active_level_list.append(LocationName.rocket_rush_region) self.active_level_list.append(LocationName.rocket_rush_region)
@ -165,7 +165,7 @@ class DKC3World(World):
# we skip in case of error, so that the original error in the output thread is the one that gets raised # we skip in case of error, so that the original error in the output thread is the one that gets raised
if rom_name: if rom_name:
new_name = base64.b64encode(bytes(self.rom_name)).decode() new_name = base64.b64encode(bytes(self.rom_name)).decode()
multidata["connect_names"][new_name] = multidata["connect_names"][self.world.player_name[self.player]] multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
if self.topology_present: if self.topology_present:
world_names = [ world_names = [
@ -181,14 +181,14 @@ class DKC3World(World):
er_hint_data = {} er_hint_data = {}
for world_index in range(len(world_names)): for world_index in range(len(world_names)):
for level_index in range(5): for level_index in range(5):
level_region = self.world.get_region(self.active_level_list[world_index * 5 + level_index], self.player) level_region = self.multiworld.get_region(self.active_level_list[world_index * 5 + level_index], self.player)
for location in level_region.locations: for location in level_region.locations:
er_hint_data[location.address] = world_names[world_index] er_hint_data[location.address] = world_names[world_index]
multidata['er_hint_data'][self.player] = er_hint_data multidata['er_hint_data'][self.player] = er_hint_data
def create_regions(self): def create_regions(self):
location_table = setup_locations(self.world, self.player) location_table = setup_locations(self.multiworld, self.player)
create_regions(self.world, self.player, location_table) create_regions(self.multiworld, self.player, location_table)
def create_item(self, name: str, force_non_progression=False) -> Item: def create_item(self, name: str, force_non_progression=False) -> Item:
data = item_table[name] data = item_table[name]
@ -205,4 +205,4 @@ class DKC3World(World):
return created_item return created_item
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)

View File

@ -77,7 +77,7 @@ class FactorioModFile(worlds.Files.APContainer):
def generate_mod(world: "Factorio", output_directory: str): def generate_mod(world: "Factorio", output_directory: str):
player = world.player player = world.player
multiworld = world.world multiworld = world.multiworld
global data_final_template, locale_template, control_template, data_template, settings_template global data_final_template, locale_template, control_template, data_template, settings_template
with template_load_lock: with template_load_lock:
if not data_final_template: if not data_final_template:

View File

@ -20,7 +20,7 @@ def _sorter(location: "FactorioScienceLocation"):
def get_shapes(factorio_world: "Factorio") -> Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]]: def get_shapes(factorio_world: "Factorio") -> Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]]:
world = factorio_world.world world = factorio_world.multiworld
player = factorio_world.player player = factorio_world.player
prerequisites: Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]] = {} prerequisites: Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]] = {}
layout = world.tech_tree_layout[player].value layout = world.tech_tree_layout[player].value

View File

@ -73,32 +73,32 @@ class Factorio(World):
generate_output = generate_mod generate_output = generate_mod
def generate_early(self) -> None: def generate_early(self) -> None:
self.world.max_tech_cost[self.player] = max(self.world.max_tech_cost[self.player], self.multiworld.max_tech_cost[self.player] = max(self.multiworld.max_tech_cost[self.player],
self.world.min_tech_cost[self.player]) self.multiworld.min_tech_cost[self.player])
self.tech_mix = self.world.tech_cost_mix[self.player] self.tech_mix = self.multiworld.tech_cost_mix[self.player]
self.skip_silo = self.world.silo[self.player].value == Silo.option_spawn self.skip_silo = self.multiworld.silo[self.player].value == Silo.option_spawn
def create_regions(self): def create_regions(self):
player = self.player player = self.player
random = self.world.random random = self.multiworld.random
menu = Region("Menu", RegionType.Generic, "Menu", player, self.world) menu = Region("Menu", RegionType.Generic, "Menu", player, self.multiworld)
crash = Entrance(player, "Crash Land", menu) crash = Entrance(player, "Crash Land", menu)
menu.exits.append(crash) menu.exits.append(crash)
nauvis = Region("Nauvis", RegionType.Generic, "Nauvis", player, self.world) nauvis = Region("Nauvis", RegionType.Generic, "Nauvis", player, self.multiworld)
location_count = len(base_tech_table) - len(useless_technologies) - self.skip_silo + \ location_count = len(base_tech_table) - len(useless_technologies) - self.skip_silo + \
self.world.evolution_traps[player].value + self.world.attack_traps[player].value self.multiworld.evolution_traps[player].value + self.multiworld.attack_traps[player].value
location_pool = [] location_pool = []
for pack in sorted(self.world.max_science_pack[self.player].get_allowed_packs()): for pack in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
location_pool.extend(location_pools[pack]) location_pool.extend(location_pools[pack])
location_names = self.world.random.sample(location_pool, location_count) location_names = self.multiworld.random.sample(location_pool, location_count)
self.locations = [FactorioScienceLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis) self.locations = [FactorioScienceLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis)
for loc_name in location_names] for loc_name in location_names]
rand_values = sorted(random.randint(self.world.min_tech_cost[self.player], rand_values = sorted(random.randint(self.multiworld.min_tech_cost[self.player],
self.world.max_tech_cost[self.player]) for _ in self.locations) self.multiworld.max_tech_cost[self.player]) for _ in self.locations)
for i, location in enumerate(sorted(self.locations, key=lambda loc: loc.rel_cost)): for i, location in enumerate(sorted(self.locations, key=lambda loc: loc.rel_cost)):
location.count = rand_values[i] location.count = rand_values[i]
del rand_values del rand_values
@ -108,27 +108,27 @@ class Factorio(World):
event = FactorioItem("Victory", ItemClassification.progression, None, player) event = FactorioItem("Victory", ItemClassification.progression, None, player)
location.place_locked_item(event) location.place_locked_item(event)
for ingredient in sorted(self.world.max_science_pack[self.player].get_allowed_packs()): for ingredient in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
location = FactorioLocation(player, f"Automate {ingredient}", None, nauvis) location = FactorioLocation(player, f"Automate {ingredient}", None, nauvis)
nauvis.locations.append(location) nauvis.locations.append(location)
event = FactorioItem(f"Automated {ingredient}", ItemClassification.progression, None, player) event = FactorioItem(f"Automated {ingredient}", ItemClassification.progression, None, player)
location.place_locked_item(event) location.place_locked_item(event)
crash.connect(nauvis) crash.connect(nauvis)
self.world.regions += [menu, nauvis] self.multiworld.regions += [menu, nauvis]
def set_rules(self): def set_rules(self):
world = self.world world = self.multiworld
player = self.player player = self.player
self.custom_technologies = self.set_custom_technologies() self.custom_technologies = self.set_custom_technologies()
self.set_custom_recipes() self.set_custom_recipes()
shapes = get_shapes(self) shapes = get_shapes(self)
if world.logic[player] != 'nologic': if world.logic[player] != 'nologic':
from worlds.generic import Rules from worlds.generic import Rules
for ingredient in self.world.max_science_pack[self.player].get_allowed_packs(): for ingredient in self.multiworld.max_science_pack[self.player].get_allowed_packs():
location = world.get_location(f"Automate {ingredient}", player) location = world.get_location(f"Automate {ingredient}", player)
if self.world.recipe_ingredients[self.player]: if self.multiworld.recipe_ingredients[self.player]:
custom_recipe = self.custom_recipes[ingredient] custom_recipe = self.custom_recipes[ingredient]
location.access_rule = lambda state, ingredient=ingredient, custom_recipe=custom_recipe: \ location.access_rule = lambda state, ingredient=ingredient, custom_recipe=custom_recipe: \
@ -148,12 +148,12 @@ class Factorio(World):
prerequisites: all(state.can_reach(loc) for loc in locations)) prerequisites: all(state.can_reach(loc) for loc in locations))
silo_recipe = None silo_recipe = None
if self.world.silo[self.player] == Silo.option_spawn: if self.multiworld.silo[self.player] == Silo.option_spawn:
silo_recipe = self.custom_recipes["rocket-silo"] if "rocket-silo" in self.custom_recipes \ silo_recipe = self.custom_recipes["rocket-silo"] if "rocket-silo" in self.custom_recipes \
else next(iter(all_product_sources.get("rocket-silo"))) else next(iter(all_product_sources.get("rocket-silo")))
part_recipe = self.custom_recipes["rocket-part"] part_recipe = self.custom_recipes["rocket-part"]
satellite_recipe = None satellite_recipe = None
if self.world.goal[self.player] == Goal.option_satellite: if self.multiworld.goal[self.player] == Goal.option_satellite:
satellite_recipe = self.custom_recipes["satellite"] if "satellite" in self.custom_recipes \ satellite_recipe = self.custom_recipes["satellite"] if "satellite" in self.custom_recipes \
else next(iter(all_product_sources.get("satellite"))) else next(iter(all_product_sources.get("satellite")))
victory_tech_names = get_rocket_requirements(silo_recipe, part_recipe, satellite_recipe) victory_tech_names = get_rocket_requirements(silo_recipe, part_recipe, satellite_recipe)
@ -165,12 +165,12 @@ class Factorio(World):
def generate_basic(self): def generate_basic(self):
player = self.player player = self.player
want_progressives = collections.defaultdict(lambda: self.world.progressive[player]. want_progressives = collections.defaultdict(lambda: self.multiworld.progressive[player].
want_progressives(self.world.random)) want_progressives(self.multiworld.random))
self.world.itempool.extend(self.create_item("Evolution Trap") for _ in self.multiworld.itempool.extend(self.create_item("Evolution Trap") for _ in
range(self.world.evolution_traps[player].value)) range(self.multiworld.evolution_traps[player].value))
self.world.itempool.extend(self.create_item("Attack Trap") for _ in self.multiworld.itempool.extend(self.create_item("Attack Trap") for _ in
range(self.world.attack_traps[player].value)) range(self.multiworld.attack_traps[player].value))
cost_sorted_locations = sorted(self.locations, key=lambda location: location.name) cost_sorted_locations = sorted(self.locations, key=lambda location: location.name)
special_index = {"automation": 0, special_index = {"automation": 0,
@ -189,19 +189,19 @@ class Factorio(World):
tech_item = self.create_item(item_name) tech_item = self.create_item(item_name)
index = special_index.get(tech_name, None) index = special_index.get(tech_name, None)
if index is None: if index is None:
self.world.itempool.append(tech_item) self.multiworld.itempool.append(tech_item)
else: else:
loc = cost_sorted_locations[index] loc = cost_sorted_locations[index]
loc.place_locked_item(tech_item) loc.place_locked_item(tech_item)
loc.revealed = True loc.revealed = True
map_basic_settings = self.world.world_gen[player].value["basic"] map_basic_settings = self.multiworld.world_gen[player].value["basic"]
if map_basic_settings.get("seed", None) is None: # allow seed 0 if map_basic_settings.get("seed", None) is None: # allow seed 0
map_basic_settings["seed"] = self.world.slot_seeds[player].randint(0, 2 ** 32 - 1) # 32 bit uint map_basic_settings["seed"] = self.multiworld.slot_seeds[player].randint(0, 2 ** 32 - 1) # 32 bit uint
if self.world.tech_tree_information[player] == TechTreeInformation.option_full: if self.multiworld.tech_tree_information[player] == TechTreeInformation.option_full:
# mark all locations as pre-hinted # mark all locations as pre-hinted
self.world.start_location_hints[self.player].value.update(base_tech_table) self.multiworld.start_location_hints[self.player].value.update(base_tech_table)
for loc in self.locations: for loc in self.locations:
loc.revealed = True loc.revealed = True
@ -259,7 +259,7 @@ class Factorio(World):
# have to first sort for determinism, while filtering out non-stacking items # have to first sort for determinism, while filtering out non-stacking items
pool: typing.List[str] = sorted(pool & valid_ingredients) pool: typing.List[str] = sorted(pool & valid_ingredients)
# then sort with random data to shuffle # then sort with random data to shuffle
self.world.random.shuffle(pool) self.multiworld.random.shuffle(pool)
target_raw = int(sum((count for ingredient, count in original.base_cost.items())) * factor) target_raw = int(sum((count for ingredient, count in original.base_cost.items())) * factor)
target_energy = original.total_energy * factor target_energy = original.total_energy * factor
target_num_ingredients = len(original.ingredients) target_num_ingredients = len(original.ingredients)
@ -303,7 +303,7 @@ class Factorio(World):
if min_num > max_num: if min_num > max_num:
fallback_pool.append(ingredient) fallback_pool.append(ingredient)
continue # can't use that ingredient continue # can't use that ingredient
num = self.world.random.randint(min_num, max_num) num = self.multiworld.random.randint(min_num, max_num)
new_ingredients[ingredient] = num new_ingredients[ingredient] = num
remaining_raw -= num * ingredient_raw remaining_raw -= num * ingredient_raw
remaining_energy -= num * ingredient_energy remaining_energy -= num * ingredient_energy
@ -347,58 +347,58 @@ class Factorio(World):
def set_custom_technologies(self): def set_custom_technologies(self):
custom_technologies = {} custom_technologies = {}
allowed_packs = self.world.max_science_pack[self.player].get_allowed_packs() allowed_packs = self.multiworld.max_science_pack[self.player].get_allowed_packs()
for technology_name, technology in base_technology_table.items(): for technology_name, technology in base_technology_table.items():
custom_technologies[technology_name] = technology.get_custom(self.world, allowed_packs, self.player) custom_technologies[technology_name] = technology.get_custom(self.multiworld, allowed_packs, self.player)
return custom_technologies return custom_technologies
def set_custom_recipes(self): def set_custom_recipes(self):
original_rocket_part = recipes["rocket-part"] original_rocket_part = recipes["rocket-part"]
science_pack_pools = get_science_pack_pools() science_pack_pools = get_science_pack_pools()
valid_pool = sorted(science_pack_pools[self.world.max_science_pack[self.player].get_max_pack()] & valid_ingredients) valid_pool = sorted(science_pack_pools[self.multiworld.max_science_pack[self.player].get_max_pack()] & valid_ingredients)
self.world.random.shuffle(valid_pool) self.multiworld.random.shuffle(valid_pool)
self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category, self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category,
{valid_pool[x]: 10 for x in range(3)}, {valid_pool[x]: 10 for x in range(3)},
original_rocket_part.products, original_rocket_part.products,
original_rocket_part.energy)} original_rocket_part.energy)}
if self.world.recipe_ingredients[self.player]: if self.multiworld.recipe_ingredients[self.player]:
valid_pool = [] valid_pool = []
for pack in self.world.max_science_pack[self.player].get_ordered_science_packs(): for pack in self.multiworld.max_science_pack[self.player].get_ordered_science_packs():
valid_pool += sorted(science_pack_pools[pack]) valid_pool += sorted(science_pack_pools[pack])
self.world.random.shuffle(valid_pool) self.multiworld.random.shuffle(valid_pool)
if pack in recipes: # skips over space science pack if pack in recipes: # skips over space science pack
new_recipe = self.make_quick_recipe(recipes[pack], valid_pool) new_recipe = self.make_quick_recipe(recipes[pack], valid_pool)
self.custom_recipes[pack] = new_recipe self.custom_recipes[pack] = new_recipe
if self.world.silo[self.player].value == Silo.option_randomize_recipe \ if self.multiworld.silo[self.player].value == Silo.option_randomize_recipe \
or self.world.satellite[self.player].value == Satellite.option_randomize_recipe: or self.multiworld.satellite[self.player].value == Satellite.option_randomize_recipe:
valid_pool = set() valid_pool = set()
for pack in sorted(self.world.max_science_pack[self.player].get_allowed_packs()): for pack in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
valid_pool |= science_pack_pools[pack] valid_pool |= science_pack_pools[pack]
if self.world.silo[self.player].value == Silo.option_randomize_recipe: if self.multiworld.silo[self.player].value == Silo.option_randomize_recipe:
new_recipe = self.make_balanced_recipe(recipes["rocket-silo"], valid_pool, new_recipe = self.make_balanced_recipe(recipes["rocket-silo"], valid_pool,
factor=(self.world.max_science_pack[self.player].value + 1) / 7) factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7)
self.custom_recipes["rocket-silo"] = new_recipe self.custom_recipes["rocket-silo"] = new_recipe
if self.world.satellite[self.player].value == Satellite.option_randomize_recipe: if self.multiworld.satellite[self.player].value == Satellite.option_randomize_recipe:
new_recipe = self.make_balanced_recipe(recipes["satellite"], valid_pool, new_recipe = self.make_balanced_recipe(recipes["satellite"], valid_pool,
factor=(self.world.max_science_pack[self.player].value + 1) / 7) factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7)
self.custom_recipes["satellite"] = new_recipe self.custom_recipes["satellite"] = new_recipe
bridge = "ap-energy-bridge" bridge = "ap-energy-bridge"
new_recipe = self.make_quick_recipe( new_recipe = self.make_quick_recipe(
Recipe(bridge, "crafting", {"replace_1": 1, "replace_2": 1, "replace_3": 1}, Recipe(bridge, "crafting", {"replace_1": 1, "replace_2": 1, "replace_3": 1},
{bridge: 1}, 10), {bridge: 1}, 10),
sorted(science_pack_pools[self.world.max_science_pack[self.player].get_ordered_science_packs()[0]])) sorted(science_pack_pools[self.multiworld.max_science_pack[self.player].get_ordered_science_packs()[0]]))
for ingredient_name in new_recipe.ingredients: for ingredient_name in new_recipe.ingredients:
new_recipe.ingredients[ingredient_name] = self.world.random.randint(10, 100) new_recipe.ingredients[ingredient_name] = self.multiworld.random.randint(10, 100)
self.custom_recipes[bridge] = new_recipe self.custom_recipes[bridge] = new_recipe
needed_recipes = self.world.max_science_pack[self.player].get_allowed_packs() | {"rocket-part"} needed_recipes = self.multiworld.max_science_pack[self.player].get_allowed_packs() | {"rocket-part"}
if self.world.silo[self.player] != Silo.option_spawn: if self.multiworld.silo[self.player] != Silo.option_spawn:
needed_recipes |= {"rocket-silo"} needed_recipes |= {"rocket-silo"}
if self.world.goal[self.player].value == Goal.option_satellite: if self.multiworld.goal[self.player].value == Goal.option_satellite:
needed_recipes |= {"satellite"} needed_recipes |= {"satellite"}
for recipe in needed_recipes: for recipe in needed_recipes:
@ -448,10 +448,10 @@ class FactorioScienceLocation(FactorioLocation):
self.ingredients = {Factorio.ordered_science_packs[self.complexity]: 1} self.ingredients = {Factorio.ordered_science_packs[self.complexity]: 1}
for complexity in range(self.complexity): for complexity in range(self.complexity):
if parent.world.tech_cost_mix[self.player] > parent.world.random.randint(0, 99): if parent.multiworld.tech_cost_mix[self.player] > parent.multiworld.random.randint(0, 99):
self.ingredients[Factorio.ordered_science_packs[complexity]] = 1 self.ingredients[Factorio.ordered_science_packs[complexity]] = 1
self.count = parent.world.random.randint(parent.world.min_tech_cost[self.player], self.count = parent.multiworld.random.randint(parent.multiworld.min_tech_cost[self.player],
parent.world.max_tech_cost[self.player]) parent.multiworld.max_tech_cost[self.player])
@property @property
def factorio_ingredients(self) -> typing.List[typing.Tuple[str, int]]: def factorio_ingredients(self) -> typing.List[typing.Tuple[str, int]]:

View File

@ -51,15 +51,15 @@ class FF1World(World):
return return
def create_regions(self): def create_regions(self):
locations = get_options(self.world, 'locations', self.player) locations = get_options(self.multiworld, 'locations', self.player)
rules = get_options(self.world, 'rules', self.player) rules = get_options(self.multiworld, 'rules', self.player)
menu_region = self.ff1_locations.create_menu_region(self.player, locations, rules) menu_region = self.ff1_locations.create_menu_region(self.player, locations, rules)
menu_region.world = self.world menu_region.multiworld = self.multiworld
terminated_event = Location(self.player, CHAOS_TERMINATED_EVENT, EventId, menu_region) terminated_event = Location(self.player, CHAOS_TERMINATED_EVENT, EventId, menu_region)
terminated_item = Item(CHAOS_TERMINATED_EVENT, ItemClassification.progression, EventId, self.player) terminated_item = Item(CHAOS_TERMINATED_EVENT, ItemClassification.progression, EventId, self.player)
terminated_event.place_locked_item(terminated_item) terminated_event.place_locked_item(terminated_item)
items = get_options(self.world, 'items', self.player) items = get_options(self.multiworld, 'items', self.player)
goal_rule = generate_rule([[name for name in items.keys() if name in FF1_PROGRESSION_LIST and name != "Shard"]], goal_rule = generate_rule([[name for name in items.keys() if name in FF1_PROGRESSION_LIST and name != "Shard"]],
self.player) self.player)
if "Shard" in items.keys(): if "Shard" in items.keys():
@ -71,22 +71,22 @@ class FF1World(World):
raise Exception("FFR Noverworld seeds must be generated on an older version of FFR. Please ensure you generated the settings using " raise Exception("FFR Noverworld seeds must be generated on an older version of FFR. Please ensure you generated the settings using "
"4-4-0.finalfantasyrandomizer.com") "4-4-0.finalfantasyrandomizer.com")
menu_region.locations.append(terminated_event) menu_region.locations.append(terminated_event)
self.world.regions += [menu_region] self.multiworld.regions += [menu_region]
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
return self.ff1_items.generate_item(name, self.player) return self.ff1_items.generate_item(name, self.player)
def set_rules(self): def set_rules(self):
self.world.completion_condition[self.player] = lambda state: state.has(CHAOS_TERMINATED_EVENT, self.player) self.multiworld.completion_condition[self.player] = lambda state: state.has(CHAOS_TERMINATED_EVENT, self.player)
def generate_basic(self): def generate_basic(self):
items = get_options(self.world, 'items', self.player) items = get_options(self.multiworld, 'items', self.player)
if FF1_BRIDGE in items.keys(): if FF1_BRIDGE in items.keys():
self._place_locked_item_in_sphere0(FF1_BRIDGE) self._place_locked_item_in_sphere0(FF1_BRIDGE)
if items: if items:
possible_early_items = [name for name in FF1_STARTER_ITEMS if name in items.keys()] possible_early_items = [name for name in FF1_STARTER_ITEMS if name in items.keys()]
if possible_early_items: if possible_early_items:
progression_item = self.world.random.choice(possible_early_items) progression_item = self.multiworld.random.choice(possible_early_items)
self._place_locked_item_in_sphere0(progression_item) self._place_locked_item_in_sphere0(progression_item)
else: else:
# Fail generation if there are no items in the pool # Fail generation if there are no items in the pool
@ -96,16 +96,16 @@ class FF1World(World):
items = [self.create_item(name) for name, data in items.items() for x in range(data['count']) if name not in items = [self.create_item(name) for name, data in items.items() for x in range(data['count']) if name not in
self.locked_items] self.locked_items]
self.world.itempool += items self.multiworld.itempool += items
def _place_locked_item_in_sphere0(self, progression_item: str): def _place_locked_item_in_sphere0(self, progression_item: str):
if progression_item: if progression_item:
rules = get_options(self.world, 'rules', self.player) rules = get_options(self.multiworld, 'rules', self.player)
sphere_0_locations = [name for name, rules in rules.items() sphere_0_locations = [name for name, rules in rules.items()
if rules and len(rules[0]) == 0 and name not in self.locked_locations] if rules and len(rules[0]) == 0 and name not in self.locked_locations]
if sphere_0_locations: if sphere_0_locations:
initial_location = self.world.random.choice(sphere_0_locations) initial_location = self.multiworld.random.choice(sphere_0_locations)
locked_location = self.world.get_location(initial_location, self.player) locked_location = self.multiworld.get_location(initial_location, self.player)
locked_location.place_locked_item(self.create_item(progression_item)) locked_location.place_locked_item(self.create_item(progression_item))
self.locked_items.append(progression_item) self.locked_items.append(progression_item)
self.locked_locations.append(locked_location.name) self.locked_locations.append(locked_location.name)
@ -116,7 +116,7 @@ class FF1World(World):
return slot_data return slot_data
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return self.world.random.choice(["Heal", "Pure", "Soft", "Tent", "Cabin", "House"]) return self.multiworld.random.choice(["Heal", "Pure", "Soft", "Tent", "Cabin", "House"])
def get_options(world: MultiWorld, name: str, player: int): def get_options(world: MultiWorld, name: str, player: int):

View File

@ -151,7 +151,7 @@ def item_in_locations(state: "BaseClasses.CollectionState", item: str, player: i
def item_name(state: "BaseClasses.CollectionState", location: str, player: int) -> \ def item_name(state: "BaseClasses.CollectionState", location: str, player: int) -> \
typing.Optional[typing.Tuple[str, int]]: typing.Optional[typing.Tuple[str, int]]:
location = state.world.get_location(location, player) location = state.multiworld.get_location(location, player)
if location.item is None: if location.item is None:
return None return None
return location.item.name, location.item.player return location.item.name, location.item.player

View File

@ -44,7 +44,7 @@ class GenericWorld(World):
web = GenericWeb() web = GenericWeb()
def generate_early(self): def generate_early(self):
self.world.player_types[self.player] = SlotType.spectator # mark as spectator self.multiworld.player_types[self.player] = SlotType.spectator # mark as spectator
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
if name == "Nothing": if name == "Nothing":

View File

@ -29,7 +29,7 @@ def hk_set_rule(hk_world: World, location: str, rule):
locations = hk_world.created_multi_locations.get(location) locations = hk_world.created_multi_locations.get(location)
if locations is None: if locations is None:
try: try:
locations = [hk_world.world.get_location(location, player)] locations = [hk_world.multiworld.get_location(location, player)]
except KeyError: except KeyError:
return return
@ -39,7 +39,7 @@ def hk_set_rule(hk_world: World, location: str, rule):
def set_rules(hk_world: World): def set_rules(hk_world: World):
player = hk_world.player player = hk_world.player
world = hk_world.world world = hk_world.multiworld
set_generated_rules(hk_world, hk_set_rule) set_generated_rules(hk_world, hk_set_rule)
# Shop costs # Shop costs

View File

@ -166,7 +166,7 @@ class HKWorld(World):
self.vanilla_shop_costs = deepcopy(vanilla_shop_costs) self.vanilla_shop_costs = deepcopy(vanilla_shop_costs)
def generate_early(self): def generate_early(self):
world = self.world world = self.multiworld
charm_costs = world.RandomCharmCosts[self.player].get_costs(world.random) charm_costs = world.RandomCharmCosts[self.player].get_costs(world.random)
self.charm_costs = world.PlandoCharmCosts[self.player].get_costs(charm_costs) self.charm_costs = world.PlandoCharmCosts[self.player].get_costs(charm_costs)
# world.exclude_locations[self.player].value.update(white_palace_locations) # world.exclude_locations[self.player].value.update(white_palace_locations)
@ -182,22 +182,22 @@ class HKWorld(World):
def white_palace_exclusions(self): def white_palace_exclusions(self):
exclusions = set() exclusions = set()
wp = self.world.WhitePalace[self.player] wp = self.multiworld.WhitePalace[self.player]
if wp <= WhitePalace.option_nopathofpain: if wp <= WhitePalace.option_nopathofpain:
exclusions.update(path_of_pain_locations) exclusions.update(path_of_pain_locations)
if wp <= WhitePalace.option_kingfragment: if wp <= WhitePalace.option_kingfragment:
exclusions.update(white_palace_checks) exclusions.update(white_palace_checks)
if wp == WhitePalace.option_exclude: if wp == WhitePalace.option_exclude:
exclusions.add("King_Fragment") exclusions.add("King_Fragment")
if self.world.RandomizeCharms[self.player]: if self.multiworld.RandomizeCharms[self.player]:
# If charms are randomized, this will be junk-filled -- so transitions and events are not progression # If charms are randomized, this will be junk-filled -- so transitions and events are not progression
exclusions.update(white_palace_transitions) exclusions.update(white_palace_transitions)
exclusions.update(white_palace_events) exclusions.update(white_palace_events)
return exclusions return exclusions
def create_regions(self): def create_regions(self):
menu_region: Region = create_region(self.world, self.player, 'Menu') menu_region: Region = create_region(self.multiworld, self.player, 'Menu')
self.world.regions.append(menu_region) self.multiworld.regions.append(menu_region)
# wp_exclusions = self.white_palace_exclusions() # wp_exclusions = self.white_palace_exclusions()
# Link regions # Link regions
@ -226,12 +226,12 @@ class HKWorld(World):
pool: typing.List[HKItem] = [] pool: typing.List[HKItem] = []
wp_exclusions = self.white_palace_exclusions() wp_exclusions = self.white_palace_exclusions()
junk_replace: typing.Set[str] = set() junk_replace: typing.Set[str] = set()
if self.world.RemoveSpellUpgrades[self.player]: if self.multiworld.RemoveSpellUpgrades[self.player]:
junk_replace.update(("Abyss_Shriek", "Shade_Soul", "Descending_Dark")) junk_replace.update(("Abyss_Shriek", "Shade_Soul", "Descending_Dark"))
randomized_starting_items = set() randomized_starting_items = set()
for attr, items in randomizable_starting_items.items(): for attr, items in randomizable_starting_items.items():
if getattr(self.world, attr)[self.player]: if getattr(self.multiworld, attr)[self.player]:
randomized_starting_items.update(items) randomized_starting_items.update(items)
# noinspection PyShadowingNames # noinspection PyShadowingNames
@ -262,7 +262,7 @@ class HKWorld(World):
unfilled_locations += 1 unfilled_locations += 1
pool.append(item) pool.append(item)
else: else:
self.world.push_precollected(item) self.multiworld.push_precollected(item)
return return
if vanilla: if vanilla:
@ -277,49 +277,49 @@ class HKWorld(World):
location.progress_type = LocationProgressType.EXCLUDED location.progress_type = LocationProgressType.EXCLUDED
for option_key, option in hollow_knight_randomize_options.items(): for option_key, option in hollow_knight_randomize_options.items():
randomized = getattr(self.world, option_key)[self.player] randomized = getattr(self.multiworld, option_key)[self.player]
for item_name, location_name in zip(option.items, option.locations): for item_name, location_name in zip(option.items, option.locations):
if item_name in junk_replace: if item_name in junk_replace:
item_name = self.get_filler_item_name() item_name = self.get_filler_item_name()
if (item_name == "Crystal_Heart" and self.world.SplitCrystalHeart[self.player]) or \ if (item_name == "Crystal_Heart" and self.multiworld.SplitCrystalHeart[self.player]) or \
(item_name == "Mothwing_Cloak" and self.world.SplitMothwingCloak[self.player]): (item_name == "Mothwing_Cloak" and self.multiworld.SplitMothwingCloak[self.player]):
_add("Left_" + item_name, location_name) _add("Left_" + item_name, location_name)
_add("Right_" + item_name, "Split_" + location_name) _add("Right_" + item_name, "Split_" + location_name)
continue continue
if item_name == "Mantis_Claw" and self.world.SplitMantisClaw[self.player]: if item_name == "Mantis_Claw" and self.multiworld.SplitMantisClaw[self.player]:
_add("Left_" + item_name, "Left_" + location_name) _add("Left_" + item_name, "Left_" + location_name)
_add("Right_" + item_name, "Right_" + location_name) _add("Right_" + item_name, "Right_" + location_name)
continue continue
if item_name == "Shade_Cloak" and self.world.SplitMothwingCloak[self.player]: if item_name == "Shade_Cloak" and self.multiworld.SplitMothwingCloak[self.player]:
if self.world.random.randint(0, 1): if self.multiworld.random.randint(0, 1):
item_name = "Left_Mothwing_Cloak" item_name = "Left_Mothwing_Cloak"
else: else:
item_name = "Right_Mothwing_Cloak" item_name = "Right_Mothwing_Cloak"
_add(item_name, location_name) _add(item_name, location_name)
if self.world.RandomizeElevatorPass[self.player]: if self.multiworld.RandomizeElevatorPass[self.player]:
randomized = True randomized = True
_add("Elevator_Pass", "Elevator_Pass") _add("Elevator_Pass", "Elevator_Pass")
for shop, locations in self.created_multi_locations.items(): for shop, locations in self.created_multi_locations.items():
for _ in range(len(locations), getattr(self.world, shop_to_option[shop])[self.player].value): for _ in range(len(locations), getattr(self.multiworld, shop_to_option[shop])[self.player].value):
loc = self.create_location(shop) loc = self.create_location(shop)
unfilled_locations += 1 unfilled_locations += 1
# Balance the pool # Balance the pool
item_count = len(pool) item_count = len(pool)
additional_shop_items = max(item_count - unfilled_locations, self.world.ExtraShopSlots[self.player].value) additional_shop_items = max(item_count - unfilled_locations, self.multiworld.ExtraShopSlots[self.player].value)
# Add additional shop items, as needed. # Add additional shop items, as needed.
if additional_shop_items > 0: if additional_shop_items > 0:
shops = list(shop for shop, locations in self.created_multi_locations.items() if len(locations) < 16) shops = list(shop for shop, locations in self.created_multi_locations.items() if len(locations) < 16)
if not self.world.EggShopSlots[self.player].value: # No eggshop, so don't place items there if not self.multiworld.EggShopSlots[self.player].value: # No eggshop, so don't place items there
shops.remove('Egg_Shop') shops.remove('Egg_Shop')
for _ in range(additional_shop_items): for _ in range(additional_shop_items):
shop = self.world.random.choice(shops) shop = self.multiworld.random.choice(shops)
loc = self.create_location(shop) loc = self.create_location(shop)
unfilled_locations += 1 unfilled_locations += 1
if len(self.created_multi_locations[shop]) >= 16: if len(self.created_multi_locations[shop]) >= 16:
@ -330,7 +330,7 @@ class HKWorld(World):
# Create filler items, if needed # Create filler items, if needed
if item_count < unfilled_locations: if item_count < unfilled_locations:
pool.extend(self.create_item(self.get_filler_item_name()) for _ in range(unfilled_locations - item_count)) pool.extend(self.create_item(self.get_filler_item_name()) for _ in range(unfilled_locations - item_count))
self.world.itempool += pool self.multiworld.itempool += pool
self.apply_costsanity() self.apply_costsanity()
self.sort_shops_by_cost() self.sort_shops_by_cost()
@ -345,24 +345,24 @@ class HKWorld(World):
loc.costs = costs loc.costs = costs
def apply_costsanity(self): def apply_costsanity(self):
setting = self.world.CostSanity[self.player].value setting = self.multiworld.CostSanity[self.player].value
if not setting: if not setting:
return # noop return # noop
def _compute_weights(weights: dict, desc: str) -> typing.Dict[str, int]: def _compute_weights(weights: dict, desc: str) -> typing.Dict[str, int]:
if all(x == 0 for x in weights.values()): if all(x == 0 for x in weights.values()):
logger.warning( logger.warning(
f"All {desc} weights were zero for {self.world.player_name[self.player]}." f"All {desc} weights were zero for {self.multiworld.player_name[self.player]}."
f" Setting them to one instead." f" Setting them to one instead."
) )
weights = {k: 1 for k in weights} weights = {k: 1 for k in weights}
return {k: v for k, v in weights.items() if v} return {k: v for k, v in weights.items() if v}
random = self.world.random random = self.multiworld.random
hybrid_chance = getattr(self.world, f"CostSanityHybridChance")[self.player].value hybrid_chance = getattr(self.multiworld, f"CostSanityHybridChance")[self.player].value
weights = { weights = {
data.term: getattr(self.world, f"CostSanity{data.option}Weight")[self.player].value data.term: getattr(self.multiworld, f"CostSanity{data.option}Weight")[self.player].value
for data in cost_terms.values() for data in cost_terms.values()
} }
weights_geoless = dict(weights) weights_geoless = dict(weights)
@ -374,16 +374,16 @@ class HKWorld(World):
if hybrid_chance > 0: if hybrid_chance > 0:
if len(weights) == 1: if len(weights) == 1:
logger.warning( logger.warning(
f"Only one cost type is available for CostSanity in {self.world.player_name[self.player]}'s world." f"Only one cost type is available for CostSanity in {self.multiworld.player_name[self.player]}'s world."
f" CostSanityHybridChance will not trigger." f" CostSanityHybridChance will not trigger."
) )
if len(weights_geoless) == 1: if len(weights_geoless) == 1:
logger.warning( logger.warning(
f"Only one cost type is available for CostSanity in {self.world.player_name[self.player]}'s world." f"Only one cost type is available for CostSanity in {self.multiworld.player_name[self.player]}'s world."
f" CostSanityHybridChance will not trigger in geoless locations." f" CostSanityHybridChance will not trigger in geoless locations."
) )
for region in self.world.get_regions(self.player): for region in self.multiworld.get_regions(self.player):
for location in region.locations: for location in region.locations:
if location.vanilla: if location.vanilla:
continue continue
@ -417,7 +417,7 @@ class HKWorld(World):
location.sort_costs() location.sort_costs()
def set_rules(self): def set_rules(self):
world = self.world world = self.multiworld
player = self.player player = self.player
if world.logic[player] != 'nologic': if world.logic[player] != 'nologic':
goal = world.Goal[player] goal = world.Goal[player]
@ -436,7 +436,7 @@ class HKWorld(World):
options = slot_data["options"] = {} options = slot_data["options"] = {}
for option_name in self.option_definitions: for option_name in self.option_definitions:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
try: try:
optionvalue = int(option.value) optionvalue = int(option.value)
except TypeError: except TypeError:
@ -445,10 +445,10 @@ class HKWorld(World):
options[option_name] = optionvalue options[option_name] = optionvalue
# 32 bit int # 32 bit int
slot_data["seed"] = self.world.slot_seeds[self.player].randint(-2147483647, 2147483646) slot_data["seed"] = self.multiworld.slot_seeds[self.player].randint(-2147483647, 2147483646)
# Backwards compatibility for shop cost data (HKAP < 0.1.0) # Backwards compatibility for shop cost data (HKAP < 0.1.0)
if not self.world.CostSanity[self.player]: if not self.multiworld.CostSanity[self.player]:
for shop, terms in shop_cost_types.items(): for shop, terms in shop_cost_types.items():
unit = cost_terms[next(iter(terms))].option unit = cost_terms[next(iter(terms))].option
if unit == "Geo": if unit == "Geo":
@ -460,7 +460,7 @@ class HKWorld(World):
# HKAP 0.1.0 and later cost data. # HKAP 0.1.0 and later cost data.
location_costs = {} location_costs = {}
for region in self.world.get_regions(self.player): for region in self.multiworld.get_regions(self.player):
for location in region.locations: for location in region.locations:
if location.costs: if location.costs:
location_costs[location.name] = location.costs location_costs[location.name] = location.costs
@ -479,7 +479,7 @@ class HKWorld(World):
basename = name basename = name
if name in shop_cost_types: if name in shop_cost_types:
costs = { costs = {
term: self.world.random.randint(*self.ranges[term]) term: self.multiworld.random.randint(*self.ranges[term])
for term in shop_cost_types[name] for term in shop_cost_types[name]
} }
elif name in vanilla_location_costs: elif name in vanilla_location_costs:
@ -491,7 +491,7 @@ class HKWorld(World):
i = len(multi) + 1 i = len(multi) + 1
name = f"{name}_{i}" name = f"{name}_{i}"
region = self.world.get_region("Menu", self.player) region = self.multiworld.get_region("Menu", self.player)
loc = HKLocation(self.player, name, loc = HKLocation(self.player, name,
self.location_name_to_id[name], region, costs=costs, vanilla=vanilla, self.location_name_to_id[name], region, costs=costs, vanilla=vanilla,
basename=basename) basename=basename)
@ -577,16 +577,16 @@ class HKWorld(World):
'RandomizeGeoRocks', 'RandomizeSoulTotems', 'RandomizeLoreTablets', 'RandomizeJunkPitChests', 'RandomizeGeoRocks', 'RandomizeSoulTotems', 'RandomizeLoreTablets', 'RandomizeJunkPitChests',
'RandomizeRancidEggs' 'RandomizeRancidEggs'
): ):
if getattr(self.world, group): if getattr(self.multiworld, group):
fillers.extend(item for item in hollow_knight_randomize_options[group].items if item not in fillers.extend(item for item in hollow_knight_randomize_options[group].items if item not in
exclusions) exclusions)
self.cached_filler_items[self.player] = fillers self.cached_filler_items[self.player] = fillers
return self.world.random.choice(self.cached_filler_items[self.player]) return self.multiworld.random.choice(self.cached_filler_items[self.player])
def create_region(world: MultiWorld, player: int, name: str, location_names=None, exits=None) -> Region: def create_region(world: MultiWorld, player: int, name: str, location_names=None, exits=None) -> Region:
ret = Region(name, RegionType.Generic, name, player) ret = Region(name, RegionType.Generic, name, player)
ret.world = world ret.multiworld = world
if location_names: if location_names:
for location in location_names: for location in location_names:
loc_id = HKWorld.location_name_to_id.get(location, None) loc_id = HKWorld.location_name_to_id.get(location, None)
@ -654,16 +654,16 @@ class HKItem(Item):
class HKLogicMixin(LogicMixin): class HKLogicMixin(LogicMixin):
world: MultiWorld multiworld: MultiWorld
def _hk_notches(self, player: int, *notches: int) -> int: def _hk_notches(self, player: int, *notches: int) -> int:
return sum(self.world.worlds[player].charm_costs[notch] for notch in notches) return sum(self.multiworld.worlds[player].charm_costs[notch] for notch in notches)
def _hk_option(self, player: int, option_name: str) -> int: def _hk_option(self, player: int, option_name: str) -> int:
return getattr(self.world, option_name)[player].value return getattr(self.multiworld, option_name)[player].value
def _hk_start(self, player, start_location: str) -> bool: def _hk_start(self, player, start_location: str) -> bool:
return self.world.StartLocation[player] == start_location return self.multiworld.StartLocation[player] == start_location
def _hk_nail_combat(self, player: int) -> bool: def _hk_nail_combat(self, player: int) -> bool:
return self.has_any({'LFFTSLASH', 'RIGHTSLASH', 'UPSLASH'}, player) return self.has_any({'LFFTSLASH', 'RIGHTSLASH', 'UPSLASH'}, player)

View File

@ -89,7 +89,7 @@ class Hylics2Logic(LogicMixin):
def set_rules(hylics2world): def set_rules(hylics2world):
world = hylics2world.world world = hylics2world.multiworld
player = hylics2world.player player = hylics2world.player
# Afterlife # Afterlife

View File

@ -64,8 +64,8 @@ class Hylics2World(World):
# set random starting location if option is enabled # set random starting location if option is enabled
def generate_early(self): def generate_early(self):
if self.world.random_start[self.player]: if self.multiworld.random_start[self.player]:
i = self.world.random.randint(0, 3) i = self.multiworld.random.randint(0, 3)
if i == 0: if i == 0:
self.start_location = "Waynehouse" self.start_location = "Waynehouse"
elif i == 1: elif i == 1:
@ -77,12 +77,12 @@ class Hylics2World(World):
def generate_basic(self): def generate_basic(self):
# create location for beating the game and place Victory event there # create location for beating the game and place Victory event there
loc = Location(self.player, "Defeat Gibby", None, self.world.get_region("Hylemxylem", self.player)) loc = Location(self.player, "Defeat Gibby", None, self.multiworld.get_region("Hylemxylem", self.player))
loc.place_locked_item(self.create_event("Victory")) loc.place_locked_item(self.create_event("Victory"))
set_rule(loc, lambda state: state._hylics2_has_upper_chamber_key(self.player) set_rule(loc, lambda state: state._hylics2_has_upper_chamber_key(self.player)
and state._hylics2_has_vessel_room_key(self.player)) and state._hylics2_has_vessel_room_key(self.player))
self.world.get_region("Hylemxylem", self.player).locations.append(loc) self.multiworld.get_region("Hylemxylem", self.player).locations.append(loc)
self.world.completion_condition[self.player] = lambda state: state.has("Victory", self.player) self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
# create item pool # create item pool
pool = [] pool = []
@ -94,53 +94,53 @@ class Hylics2World(World):
pool.append(self.add_item(data["name"], data["classification"], i)) pool.append(self.add_item(data["name"], data["classification"], i))
# add party members if option is enabled # add party members if option is enabled
if self.world.party_shuffle[self.player]: if self.multiworld.party_shuffle[self.player]:
for i, data in Items.party_item_table.items(): for i, data in Items.party_item_table.items():
pool.append(self.add_item(data["name"], data["classification"], i)) pool.append(self.add_item(data["name"], data["classification"], i))
# handle gesture shuffle options # handle gesture shuffle options
if self.world.gesture_shuffle[self.player] == 2: # vanilla locations if self.multiworld.gesture_shuffle[self.player] == 2: # vanilla locations
gestures = Items.gesture_item_table gestures = Items.gesture_item_table
self.world.get_location("Waynehouse: TV", self.player)\ self.multiworld.get_location("Waynehouse: TV", self.player)\
.place_locked_item(self.add_item(gestures[200678]["name"], gestures[200678]["classification"], 200678)) .place_locked_item(self.add_item(gestures[200678]["name"], gestures[200678]["classification"], 200678))
self.world.get_location("Afterlife: TV", self.player)\ self.multiworld.get_location("Afterlife: TV", self.player)\
.place_locked_item(self.add_item(gestures[200683]["name"], gestures[200683]["classification"], 200683)) .place_locked_item(self.add_item(gestures[200683]["name"], gestures[200683]["classification"], 200683))
self.world.get_location("New Muldul: TV", self.player)\ self.multiworld.get_location("New Muldul: TV", self.player)\
.place_locked_item(self.add_item(gestures[200679]["name"], gestures[200679]["classification"], 200679)) .place_locked_item(self.add_item(gestures[200679]["name"], gestures[200679]["classification"], 200679))
self.world.get_location("Viewax's Edifice: TV", self.player)\ self.multiworld.get_location("Viewax's Edifice: TV", self.player)\
.place_locked_item(self.add_item(gestures[200680]["name"], gestures[200680]["classification"], 200680)) .place_locked_item(self.add_item(gestures[200680]["name"], gestures[200680]["classification"], 200680))
self.world.get_location("TV Island: TV", self.player)\ self.multiworld.get_location("TV Island: TV", self.player)\
.place_locked_item(self.add_item(gestures[200681]["name"], gestures[200681]["classification"], 200681)) .place_locked_item(self.add_item(gestures[200681]["name"], gestures[200681]["classification"], 200681))
self.world.get_location("Juice Ranch: TV", self.player)\ self.multiworld.get_location("Juice Ranch: TV", self.player)\
.place_locked_item(self.add_item(gestures[200682]["name"], gestures[200682]["classification"], 200682)) .place_locked_item(self.add_item(gestures[200682]["name"], gestures[200682]["classification"], 200682))
self.world.get_location("Foglast: TV", self.player)\ self.multiworld.get_location("Foglast: TV", self.player)\
.place_locked_item(self.add_item(gestures[200684]["name"], gestures[200684]["classification"], 200684)) .place_locked_item(self.add_item(gestures[200684]["name"], gestures[200684]["classification"], 200684))
self.world.get_location("Drill Castle: TV", self.player)\ self.multiworld.get_location("Drill Castle: TV", self.player)\
.place_locked_item(self.add_item(gestures[200688]["name"], gestures[200688]["classification"], 200688)) .place_locked_item(self.add_item(gestures[200688]["name"], gestures[200688]["classification"], 200688))
self.world.get_location("Sage Airship: TV", self.player)\ self.multiworld.get_location("Sage Airship: TV", self.player)\
.place_locked_item(self.add_item(gestures[200685]["name"], gestures[200685]["classification"], 200685)) .place_locked_item(self.add_item(gestures[200685]["name"], gestures[200685]["classification"], 200685))
elif self.world.gesture_shuffle[self.player] == 1: # TVs only elif self.multiworld.gesture_shuffle[self.player] == 1: # TVs only
gestures = list(Items.gesture_item_table.items()) gestures = list(Items.gesture_item_table.items())
tvs = list(Locations.tv_location_table.items()) tvs = list(Locations.tv_location_table.items())
# if Extra Items in Logic is enabled place CHARGE UP first and make sure it doesn't get # if Extra Items in Logic is enabled place CHARGE UP first and make sure it doesn't get
# placed at Sage Airship: TV # placed at Sage Airship: TV
if self.world.extra_items_in_logic[self.player]: if self.multiworld.extra_items_in_logic[self.player]:
tv = self.world.random.choice(tvs) tv = self.multiworld.random.choice(tvs)
gest = gestures.index((200681, Items.gesture_item_table[200681])) gest = gestures.index((200681, Items.gesture_item_table[200681]))
while tv[1]["name"] == "Sage Airship: TV": while tv[1]["name"] == "Sage Airship: TV":
tv = self.world.random.choice(tvs) tv = self.multiworld.random.choice(tvs)
self.world.get_location(tv[1]["name"], self.player)\ self.multiworld.get_location(tv[1]["name"], self.player)\
.place_locked_item(self.add_item(gestures[gest][1]["name"], gestures[gest][1]["classification"], .place_locked_item(self.add_item(gestures[gest][1]["name"], gestures[gest][1]["classification"],
gestures[gest])) gestures[gest]))
gestures.remove(gestures[gest]) gestures.remove(gestures[gest])
tvs.remove(tv) tvs.remove(tv)
for i in range(len(gestures)): for i in range(len(gestures)):
gest = self.world.random.choice(gestures) gest = self.multiworld.random.choice(gestures)
tv = self.world.random.choice(tvs) tv = self.multiworld.random.choice(tvs)
self.world.get_location(tv[1]["name"], self.player)\ self.multiworld.get_location(tv[1]["name"], self.player)\
.place_locked_item(self.add_item(gest[1]["name"], gest[1]["classification"], gest[1])) .place_locked_item(self.add_item(gest[1]["name"], gest[1]["classification"], gest[1]))
gestures.remove(gest) gestures.remove(gest)
tvs.remove(tv) tvs.remove(tv)
@ -150,22 +150,22 @@ class Hylics2World(World):
pool.append(self.add_item(data["name"], data["classification"], i)) pool.append(self.add_item(data["name"], data["classification"], i))
# add '10 Bones' items if medallion shuffle is enabled # add '10 Bones' items if medallion shuffle is enabled
if self.world.medallion_shuffle[self.player]: if self.multiworld.medallion_shuffle[self.player]:
for i, data in Items.medallion_item_table.items(): for i, data in Items.medallion_item_table.items():
for j in range(data["count"]): for j in range(data["count"]):
pool.append(self.add_item(data["name"], data["classification"], i)) pool.append(self.add_item(data["name"], data["classification"], i))
# add to world's pool # add to world's pool
self.world.itempool += pool self.multiworld.itempool += pool
def fill_slot_data(self) -> Dict[str, Any]: def fill_slot_data(self) -> Dict[str, Any]:
slot_data: Dict[str, Any] = { slot_data: Dict[str, Any] = {
"party_shuffle": self.world.party_shuffle[self.player].value, "party_shuffle": self.multiworld.party_shuffle[self.player].value,
"medallion_shuffle": self.world.medallion_shuffle[self.player].value, "medallion_shuffle": self.multiworld.medallion_shuffle[self.player].value,
"random_start" : self.world.random_start[self.player].value, "random_start" : self.multiworld.random_start[self.player].value,
"start_location" : self.start_location, "start_location" : self.start_location,
"death_link": self.world.death_link[self.player].value "death_link": self.multiworld.death_link[self.player].value
} }
return slot_data return slot_data
@ -173,29 +173,29 @@ class Hylics2World(World):
def create_regions(self) -> None: def create_regions(self) -> None:
region_table: Dict[int, Region] = { region_table: Dict[int, Region] = {
0: Region("Menu", RegionType.Generic, "Menu", self.player, self.world), 0: Region("Menu", RegionType.Generic, "Menu", self.player, self.multiworld),
1: Region("Afterlife", RegionType.Generic, "Afterlife", self.player, self.world), 1: Region("Afterlife", RegionType.Generic, "Afterlife", self.player, self.multiworld),
2: Region("Waynehouse", RegionType.Generic, "Waynehouse", self.player, self.world), 2: Region("Waynehouse", RegionType.Generic, "Waynehouse", self.player, self.multiworld),
3: Region("World", RegionType.Generic, "World", self.player, self.world), 3: Region("World", RegionType.Generic, "World", self.player, self.multiworld),
4: Region("New Muldul", RegionType.Generic, "New Muldul", self.player, self.world), 4: Region("New Muldul", RegionType.Generic, "New Muldul", self.player, self.multiworld),
5: Region("New Muldul Vault", RegionType.Generic, "New Muldul Vault", self.player, self.world), 5: Region("New Muldul Vault", RegionType.Generic, "New Muldul Vault", self.player, self.multiworld),
6: Region("Viewax", RegionType.Generic, "Viewax's Edifice", self.player, self.world), 6: Region("Viewax", RegionType.Generic, "Viewax's Edifice", self.player, self.multiworld),
7: Region("Airship", RegionType.Generic, "Airship", self.player, self.world), 7: Region("Airship", RegionType.Generic, "Airship", self.player, self.multiworld),
8: Region("Arcade Island", RegionType.Generic, "Arcade Island", self.player, self.world), 8: Region("Arcade Island", RegionType.Generic, "Arcade Island", self.player, self.multiworld),
9: Region("TV Island", RegionType.Generic, "TV Island", self.player, self.world), 9: Region("TV Island", RegionType.Generic, "TV Island", self.player, self.multiworld),
10: Region("Juice Ranch", RegionType.Generic, "Juice Ranch", self.player, self.world), 10: Region("Juice Ranch", RegionType.Generic, "Juice Ranch", self.player, self.multiworld),
11: Region("Shield Facility", RegionType.Generic, "Shield Facility", self.player, self.world), 11: Region("Shield Facility", RegionType.Generic, "Shield Facility", self.player, self.multiworld),
12: Region("Worm Pod", RegionType.Generic, "Worm Pod", self.player, self.world), 12: Region("Worm Pod", RegionType.Generic, "Worm Pod", self.player, self.multiworld),
13: Region("Foglast", RegionType.Generic, "Foglast", self.player, self.world), 13: Region("Foglast", RegionType.Generic, "Foglast", self.player, self.multiworld),
14: Region("Drill Castle", RegionType.Generic, "Drill Castle", self.player, self.world), 14: Region("Drill Castle", RegionType.Generic, "Drill Castle", self.player, self.multiworld),
15: Region("Sage Labyrinth", RegionType.Generic, "Sage Labyrinth", self.player, self.world), 15: Region("Sage Labyrinth", RegionType.Generic, "Sage Labyrinth", self.player, self.multiworld),
16: Region("Sage Airship", RegionType.Generic, "Sage Airship", self.player, self.world), 16: Region("Sage Airship", RegionType.Generic, "Sage Airship", self.player, self.multiworld),
17: Region("Hylemxylem", RegionType.Generic, "Hylemxylem", self.player, self.world) 17: Region("Hylemxylem", RegionType.Generic, "Hylemxylem", self.player, self.multiworld)
} }
# create regions from table # create regions from table
for i, reg in region_table.items(): for i, reg in region_table.items():
self.world.regions.append(reg) self.multiworld.regions.append(reg)
# get all exits per region # get all exits per region
for j, exits in Exits.region_exit_table.items(): for j, exits in Exits.region_exit_table.items():
if j == i: if j == i:
@ -203,7 +203,7 @@ class Hylics2World(World):
# create entrance and connect it to parent and destination regions # create entrance and connect it to parent and destination regions
ent = Entrance(self.player, k, reg) ent = Entrance(self.player, k, reg)
reg.exits.append(ent) reg.exits.append(ent)
if k == "New Game" and self.world.random_start[self.player]: if k == "New Game" and self.multiworld.random_start[self.player]:
if self.start_location == "Waynehouse": if self.start_location == "Waynehouse":
ent.connect(region_table[2]) ent.connect(region_table[2])
elif self.start_location == "Viewax's Edifice": elif self.start_location == "Viewax's Edifice":
@ -226,13 +226,13 @@ class Hylics2World(World):
.append(Hylics2Location(self.player, data["name"], i, region_table[data["region"]])) .append(Hylics2Location(self.player, data["name"], i, region_table[data["region"]]))
# add party member locations if option is enabled # add party member locations if option is enabled
if self.world.party_shuffle[self.player]: if self.multiworld.party_shuffle[self.player]:
for i, data in Locations.party_location_table.items(): for i, data in Locations.party_location_table.items():
region_table[data["region"]].locations\ region_table[data["region"]].locations\
.append(Hylics2Location(self.player, data["name"], i, region_table[data["region"]])) .append(Hylics2Location(self.player, data["name"], i, region_table[data["region"]]))
# add medallion locations if option is enabled # add medallion locations if option is enabled
if self.world.medallion_shuffle[self.player]: if self.multiworld.medallion_shuffle[self.player]:
for i, data in Locations.medallion_location_table.items(): for i, data in Locations.medallion_location_table.items():
region_table[data["region"]].locations\ region_table[data["region"]].locations\
.append(Hylics2Location(self.player, data["name"], i, region_table[data["region"]])) .append(Hylics2Location(self.player, data["name"], i, region_table[data["region"]]))

View File

@ -88,7 +88,7 @@ class MeritousWorld(World):
return crystal_pool return crystal_pool
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
rand_crystals = self.world.random.randrange(0, 32) rand_crystals = self.multiworld.random.randrange(0, 32)
if rand_crystals < 16: if rand_crystals < 16:
return "Crystals x500" return "Crystals x500"
elif rand_crystals < 28: elif rand_crystals < 28:
@ -97,14 +97,14 @@ class MeritousWorld(World):
return "Crystals x2000" return "Crystals x2000"
def generate_early(self): def generate_early(self):
self.goal = self.world.goal[self.player].value self.goal = self.multiworld.goal[self.player].value
self.include_evolution_traps = self.world.include_evolution_traps[self.player].value self.include_evolution_traps = self.multiworld.include_evolution_traps[self.player].value
self.include_psi_keys = self.world.include_psi_keys[self.player].value self.include_psi_keys = self.multiworld.include_psi_keys[self.player].value
self.item_cache_cost = self.world.item_cache_cost[self.player].value self.item_cache_cost = self.multiworld.item_cache_cost[self.player].value
self.death_link = self.world.death_link[self.player].value self.death_link = self.multiworld.death_link[self.player].value
def create_regions(self): def create_regions(self):
create_regions(self.world, self.player) create_regions(self.multiworld, self.player)
def create_items(self): def create_items(self):
frequencies = [0, # Nothing [0] frequencies = [0, # Nothing [0]
@ -133,22 +133,22 @@ class MeritousWorld(World):
if len(item_pool) < location_count: if len(item_pool) < location_count:
item_pool += self._make_crystals(location_count - len(item_pool)) item_pool += self._make_crystals(location_count - len(item_pool))
self.world.itempool += item_pool self.multiworld.itempool += item_pool
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)
def generate_basic(self): def generate_basic(self):
self.world.get_location("Place of Power", self.player).place_locked_item( self.multiworld.get_location("Place of Power", self.player).place_locked_item(
self.create_item("Cursed Seal")) self.create_item("Cursed Seal"))
self.world.get_location("The Last Place You'll Look", self.player).place_locked_item( self.multiworld.get_location("The Last Place You'll Look", self.player).place_locked_item(
self.create_item("Agate Knife")) self.create_item("Agate Knife"))
self.world.get_location("Wervyn Anixil", self.player).place_locked_item( self.multiworld.get_location("Wervyn Anixil", self.player).place_locked_item(
self.create_event("Victory")) self.create_event("Victory"))
self.world.get_location("Wervyn Anixil?", self.player).place_locked_item( self.multiworld.get_location("Wervyn Anixil?", self.player).place_locked_item(
self.create_event("Full Victory")) self.create_event("Full Victory"))
for boss in ["Meridian", "Ataraxia", "Merodach"]: for boss in ["Meridian", "Ataraxia", "Merodach"]:
self.world.get_location(f"{boss} Defeat", self.player).place_locked_item( self.multiworld.get_location(f"{boss} Defeat", self.player).place_locked_item(
self.create_event(f"{boss} Defeated")) self.create_event(f"{boss} Defeated"))
if not self.include_psi_keys: if not self.include_psi_keys:
@ -156,22 +156,22 @@ class MeritousWorld(World):
psi_key_storage = [] psi_key_storage = []
for i in range(0, 3): for i in range(0, 3):
psi_keys += [self.create_item(f"PSI Key {i + 1}")] psi_keys += [self.create_item(f"PSI Key {i + 1}")]
psi_key_storage += [self.world.get_location( psi_key_storage += [self.multiworld.get_location(
f"PSI Key Storage {i + 1}", self.player)] f"PSI Key Storage {i + 1}", self.player)]
fill_restrictive(self.world, self.world.get_all_state( fill_restrictive(self.multiworld, self.multiworld.get_all_state(
False), psi_key_storage, psi_keys) False), psi_key_storage, psi_keys)
if not self.include_evolution_traps: if not self.include_evolution_traps:
for boss in ["Meridian", "Ataraxia", "Merodach"]: for boss in ["Meridian", "Ataraxia", "Merodach"]:
self.world.get_location(boss, self.player).place_locked_item( self.multiworld.get_location(boss, self.player).place_locked_item(
self.create_item("Evolution Trap")) self.create_item("Evolution Trap"))
if self.goal == 0: if self.goal == 0:
self.world.completion_condition[self.player] = lambda state: state.has_any( self.multiworld.completion_condition[self.player] = lambda state: state.has_any(
["Victory", "Full Victory"], self.player) ["Victory", "Full Victory"], self.player)
else: else:
self.world.completion_condition[self.player] = lambda state: state.has( self.multiworld.completion_condition[self.player] = lambda state: state.has(
"Full Victory", self.player) "Full Victory", self.player)
def fill_slot_data(self) -> dict: def fill_slot_data(self) -> dict:

View File

@ -45,7 +45,7 @@ class MinecraftLogic(LogicMixin):
self.can_reach('Bastion Remnant', 'Region', player)) self.can_reach('Bastion Remnant', 'Region', player))
def _mc_overworld_villager(self, player: int): def _mc_overworld_villager(self, player: int):
village_region = self.world.get_region('Village', player).entrances[0].parent_region.name village_region = self.multiworld.get_region('Village', player).entrances[0].parent_region.name
if village_region == 'The Nether': # 2 options: cure zombie villager or build portal in village if village_region == 'The Nether': # 2 options: cure zombie villager or build portal in village
return (self.can_reach('Zombie Doctor', 'Location', player) or return (self.can_reach('Zombie Doctor', 'Location', player) or
(self._mc_has_diamond_pickaxe(player) and self.can_reach('Village', 'Region', player))) (self._mc_has_diamond_pickaxe(player) and self.can_reach('Village', 'Region', player)))
@ -58,10 +58,10 @@ class MinecraftLogic(LogicMixin):
# Difficulty-dependent functions # Difficulty-dependent functions
def _mc_combat_difficulty(self, player: int): def _mc_combat_difficulty(self, player: int):
return self.world.combat_difficulty[player].current_key return self.multiworld.combat_difficulty[player].current_key
def _mc_can_adventure(self, player: int): def _mc_can_adventure(self, player: int):
death_link_check = not self.world.death_link[player] or self.has('Bed', player) death_link_check = not self.multiworld.death_link[player] or self.has('Bed', player)
if self._mc_combat_difficulty(player) == 'easy': if self._mc_combat_difficulty(player) == 'easy':
return self.has('Progressive Weapons', player, 2) and self._mc_has_iron_ingots(player) and death_link_check return self.has('Progressive Weapons', player, 2) and self._mc_has_iron_ingots(player) and death_link_check
elif self._mc_combat_difficulty(player) == 'hard': elif self._mc_combat_difficulty(player) == 'hard':
@ -112,9 +112,9 @@ class MinecraftLogic(LogicMixin):
return self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and self.has('Archery', player) return self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and self.has('Archery', player)
def _mc_has_structure_compass(self, entrance_name: str, player: int): def _mc_has_structure_compass(self, entrance_name: str, player: int):
if not self.world.structure_compasses[player]: if not self.multiworld.structure_compasses[player]:
return True return True
return self.has(f"Structure Compass ({self.world.get_entrance(entrance_name, player).connected_region.name})", player) return self.has(f"Structure Compass ({self.multiworld.get_entrance(entrance_name, player).connected_region.name})", player)
# Sets rules on entrances and advancements that are always applied # Sets rules on entrances and advancements that are always applied
def set_advancement_rules(world: MultiWorld, player: int): def set_advancement_rules(world: MultiWorld, player: int):

View File

@ -70,21 +70,21 @@ class MinecraftWorld(World):
def _get_mc_data(self): def _get_mc_data(self):
exits = [connection[0] for connection in default_connections] exits = [connection[0] for connection in default_connections]
return { return {
'world_seed': self.world.slot_seeds[self.player].getrandbits(32), 'world_seed': self.multiworld.slot_seeds[self.player].getrandbits(32),
'seed_name': self.world.seed_name, 'seed_name': self.multiworld.seed_name,
'player_name': self.world.get_player_name(self.player), 'player_name': self.multiworld.get_player_name(self.player),
'player_id': self.player, 'player_id': self.player,
'client_version': client_version, 'client_version': client_version,
'structures': {exit: self.world.get_entrance(exit, self.player).connected_region.name for exit in exits}, 'structures': {exit: self.multiworld.get_entrance(exit, self.player).connected_region.name for exit in exits},
'advancement_goal': self.world.advancement_goal[self.player].value, 'advancement_goal': self.multiworld.advancement_goal[self.player].value,
'egg_shards_required': min(self.world.egg_shards_required[self.player].value, 'egg_shards_required': min(self.multiworld.egg_shards_required[self.player].value,
self.world.egg_shards_available[self.player].value), self.multiworld.egg_shards_available[self.player].value),
'egg_shards_available': self.world.egg_shards_available[self.player].value, 'egg_shards_available': self.multiworld.egg_shards_available[self.player].value,
'required_bosses': self.world.required_bosses[self.player].current_key, 'required_bosses': self.multiworld.required_bosses[self.player].current_key,
'MC35': bool(self.world.send_defeated_mobs[self.player].value), 'MC35': bool(self.multiworld.send_defeated_mobs[self.player].value),
'death_link': bool(self.world.death_link[self.player].value), 'death_link': bool(self.multiworld.death_link[self.player].value),
'starting_items': str(self.world.starting_items[self.player].value), 'starting_items': str(self.multiworld.starting_items[self.player].value),
'race': self.world.is_race, 'race': self.multiworld.is_race,
} }
def generate_basic(self): def generate_basic(self):
@ -96,18 +96,18 @@ class MinecraftWorld(World):
for (name, num) in required_items.items(): for (name, num) in required_items.items():
itempool += [name] * num itempool += [name] * num
# Add structure compasses if desired # Add structure compasses if desired
if self.world.structure_compasses[self.player]: if self.multiworld.structure_compasses[self.player]:
structures = [connection[1] for connection in default_connections] structures = [connection[1] for connection in default_connections]
for struct_name in structures: for struct_name in structures:
itempool.append(f"Structure Compass ({struct_name})") itempool.append(f"Structure Compass ({struct_name})")
# Add dragon egg shards # Add dragon egg shards
if self.world.egg_shards_required[self.player] > 0: if self.multiworld.egg_shards_required[self.player] > 0:
itempool += ["Dragon Egg Shard"] * self.world.egg_shards_available[self.player] itempool += ["Dragon Egg Shard"] * self.multiworld.egg_shards_available[self.player]
# Add bee traps if desired # Add bee traps if desired
bee_trap_quantity = ceil(self.world.bee_traps[self.player] * (len(self.location_names)-len(itempool)) * 0.01) bee_trap_quantity = ceil(self.multiworld.bee_traps[self.player] * (len(self.location_names) - len(itempool)) * 0.01)
itempool += ["Bee Trap"] * bee_trap_quantity itempool += ["Bee Trap"] * bee_trap_quantity
# Fill remaining items with randomly generated junk # Fill remaining items with randomly generated junk
itempool += self.world.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()), k=len(self.location_names)-len(itempool)) itempool += self.multiworld.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()), k=len(self.location_names) - len(itempool))
# Convert itempool into real items # Convert itempool into real items
itempool = [item for item in map(lambda name: self.create_item(name), itempool)] itempool = [item for item in map(lambda name: self.create_item(name), itempool)]
@ -115,29 +115,29 @@ class MinecraftWorld(World):
exclusion_pool = set() exclusion_pool = set()
exclusion_types = ['hard', 'unreasonable'] exclusion_types = ['hard', 'unreasonable']
for key in exclusion_types: for key in exclusion_types:
if not getattr(self.world, f"include_{key}_advancements")[self.player]: if not getattr(self.multiworld, f"include_{key}_advancements")[self.player]:
exclusion_pool.update(exclusion_table[key]) exclusion_pool.update(exclusion_table[key])
# For postgame advancements, check with the boss goal # For postgame advancements, check with the boss goal
exclusion_pool.update(get_postgame_advancements(self.world.required_bosses[self.player].current_key)) exclusion_pool.update(get_postgame_advancements(self.multiworld.required_bosses[self.player].current_key))
exclusion_rules(self.world, self.player, exclusion_pool) exclusion_rules(self.multiworld, self.player, exclusion_pool)
# Prefill event locations with their events # Prefill event locations with their events
self.world.get_location("Blaze Spawner", self.player).place_locked_item(self.create_item("Blaze Rods")) self.multiworld.get_location("Blaze Spawner", self.player).place_locked_item(self.create_item("Blaze Rods"))
self.world.get_location("Ender Dragon", self.player).place_locked_item(self.create_item("Defeat Ender Dragon")) self.multiworld.get_location("Ender Dragon", self.player).place_locked_item(self.create_item("Defeat Ender Dragon"))
self.world.get_location("Wither", self.player).place_locked_item(self.create_item("Defeat Wither")) self.multiworld.get_location("Wither", self.player).place_locked_item(self.create_item("Defeat Wither"))
self.world.itempool += itempool self.multiworld.itempool += itempool
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return self.world.random.choices(list(junk_weights.keys()), weights=list(junk_weights.values()))[0] return self.multiworld.random.choices(list(junk_weights.keys()), weights=list(junk_weights.values()))[0]
def set_rules(self): def set_rules(self):
set_advancement_rules(self.world, self.player) set_advancement_rules(self.multiworld, self.player)
set_completion_rules(self.world, self.player) set_completion_rules(self.multiworld, self.player)
def create_regions(self): def create_regions(self):
def MCRegion(region_name: str, exits=[]): def MCRegion(region_name: str, exits=[]):
ret = Region(region_name, None, region_name, self.player, self.world) ret = Region(region_name, None, region_name, self.player, self.multiworld)
ret.locations = [MinecraftAdvancement(self.player, loc_name, loc_data.id, ret) ret.locations = [MinecraftAdvancement(self.player, loc_name, loc_data.id, ret)
for loc_name, loc_data in advancement_table.items() for loc_name, loc_data in advancement_table.items()
if loc_data.region == region_name] if loc_data.region == region_name]
@ -145,19 +145,19 @@ class MinecraftWorld(World):
ret.exits.append(Entrance(self.player, exit, ret)) ret.exits.append(Entrance(self.player, exit, ret))
return ret return ret
self.world.regions += [MCRegion(*r) for r in mc_regions] self.multiworld.regions += [MCRegion(*r) for r in mc_regions]
link_minecraft_structures(self.world, self.player) link_minecraft_structures(self.multiworld, self.player)
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
data = self._get_mc_data() data = self._get_mc_data()
filename = f"{self.world.get_out_file_name_base(self.player)}.apmc" filename = f"AP_{self.multiworld.get_out_file_name_base(self.player)}.apmc"
with open(os.path.join(output_directory, filename), 'wb') as f: with open(os.path.join(output_directory, filename), 'wb') as f:
f.write(b64encode(bytes(json.dumps(data), 'utf-8'))) f.write(b64encode(bytes(json.dumps(data), 'utf-8')))
def fill_slot_data(self): def fill_slot_data(self):
slot_data = self._get_mc_data() slot_data = self._get_mc_data()
for option_name in minecraft_options: for option_name in minecraft_options:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}: if slot_data.get(option_name, None) is None and type(option.value) in {str, int}:
slot_data[option_name] = int(option.value) slot_data[option_name] = int(option.value)
return slot_data return slot_data

View File

@ -768,7 +768,7 @@ patch_sets = {
def patch_cosmetics(ootworld, rom): def patch_cosmetics(ootworld, rom):
# Use the world's slot seed for cosmetics # Use the world's slot seed for cosmetics
random.seed(ootworld.world.slot_seeds[ootworld.player]) random.seed(ootworld.multiworld.slot_seeds[ootworld.player])
# try to detect the cosmetic patch data format # try to detect the cosmetic patch data format
versioned_patch_set = None versioned_patch_set = None

View File

@ -9,7 +9,7 @@ class Dungeon(object):
else: else:
return [obj] return [obj]
self.world = world self.multiworld = world
self.name = name self.name = name
self.hint_text = hint self.hint_text = hint
self.font_color = font_color self.font_color = font_color
@ -18,7 +18,7 @@ class Dungeon(object):
self.small_keys = to_array(small_keys) self.small_keys = to_array(small_keys)
self.dungeon_items = to_array(dungeon_items) self.dungeon_items = to_array(dungeon_items)
for region in world.world.regions: for region in world.multiworld.regions:
if region.player == world.player and region.dungeon == self.name: if region.player == world.player and region.dungeon == self.name:
region.dungeon = self region.dungeon = self
self.regions.append(region) self.regions.append(region)

View File

@ -7,7 +7,7 @@ class OOTEntrance(Entrance):
def __init__(self, player, world, name='', parent=None): def __init__(self, player, world, name='', parent=None):
super(OOTEntrance, self).__init__(player, name, parent) super(OOTEntrance, self).__init__(player, name, parent)
self.world = world self.multiworld = world
self.access_rules = [] self.access_rules = []
self.reverse = None self.reverse = None
self.replaces = None self.replaces = None
@ -30,8 +30,8 @@ class OOTEntrance(Entrance):
return previously_connected return previously_connected
def get_new_target(self): def get_new_target(self):
root = self.world.get_region('Root Exits', self.player) root = self.multiworld.get_region('Root Exits', self.player)
target_entrance = OOTEntrance(self.player, self.world, 'Root -> ' + self.connected_region.name, root) target_entrance = OOTEntrance(self.player, self.multiworld, 'Root -> ' + self.connected_region.name, root)
target_entrance.connect(self.connected_region) target_entrance.connect(self.connected_region)
target_entrance.replaces = self target_entrance.replaces = self
root.exits.append(target_entrance) root.exits.append(target_entrance)

View File

@ -369,7 +369,7 @@ class EntranceShuffleError(Exception):
def shuffle_random_entrances(ootworld): def shuffle_random_entrances(ootworld):
world = ootworld.world world = ootworld.multiworld
player = ootworld.player player = ootworld.player
# Gather locations to keep reachable for validation # Gather locations to keep reachable for validation
@ -593,7 +593,7 @@ def place_one_way_priority_entrance(ootworld, priority_name, allowed_regions, al
all_state, none_state, one_way_entrance_pools, one_way_target_entrance_pools): all_state, none_state, one_way_entrance_pools, one_way_target_entrance_pools):
avail_pool = list(chain.from_iterable(one_way_entrance_pools[t] for t in allowed_types if t in one_way_entrance_pools)) avail_pool = list(chain.from_iterable(one_way_entrance_pools[t] for t in allowed_types if t in one_way_entrance_pools))
ootworld.world.random.shuffle(avail_pool) ootworld.multiworld.random.shuffle(avail_pool)
for entrance in avail_pool: for entrance in avail_pool:
if entrance.replaces: if entrance.replaces:
@ -643,11 +643,11 @@ def shuffle_entrance_pool(ootworld, pool_type, entrance_pool, target_entrances,
raise EntranceShuffleError(f'Entrance placement attempt count exceeded for world {ootworld.player}') raise EntranceShuffleError(f'Entrance placement attempt count exceeded for world {ootworld.player}')
def shuffle_entrances(ootworld, pool_type, entrances, target_entrances, rollbacks, locations_to_ensure_reachable, all_state, none_state): def shuffle_entrances(ootworld, pool_type, entrances, target_entrances, rollbacks, locations_to_ensure_reachable, all_state, none_state):
ootworld.world.random.shuffle(entrances) ootworld.multiworld.random.shuffle(entrances)
for entrance in entrances: for entrance in entrances:
if entrance.connected_region != None: if entrance.connected_region != None:
continue continue
ootworld.world.random.shuffle(target_entrances) ootworld.multiworld.random.shuffle(target_entrances)
# Here we deliberately introduce bias by prioritizing certain interiors, i.e. the ones most likely to cause problems. # Here we deliberately introduce bias by prioritizing certain interiors, i.e. the ones most likely to cause problems.
# success rate over randomization # success rate over randomization
if pool_type in {'InteriorSoft', 'MixedSoft'}: if pool_type in {'InteriorSoft', 'MixedSoft'}:
@ -662,7 +662,7 @@ def shuffle_entrances(ootworld, pool_type, entrances, target_entrances, rollback
def split_entrances_by_requirements(ootworld, entrances_to_split, assumed_entrances): def split_entrances_by_requirements(ootworld, entrances_to_split, assumed_entrances):
world = ootworld.world world = ootworld.multiworld
player = ootworld.player player = ootworld.player
# Disconnect all root assumed entrances and save original connections # Disconnect all root assumed entrances and save original connections
@ -704,7 +704,7 @@ def split_entrances_by_requirements(ootworld, entrances_to_split, assumed_entran
# TODO: improve this function # TODO: improve this function
def validate_world(ootworld, entrance_placed, locations_to_ensure_reachable, all_state_orig, none_state_orig): def validate_world(ootworld, entrance_placed, locations_to_ensure_reachable, all_state_orig, none_state_orig):
world = ootworld.world world = ootworld.multiworld
player = ootworld.player player = ootworld.player
all_state = all_state_orig.copy() all_state = all_state_orig.copy()
@ -827,7 +827,7 @@ def same_hint_area(first, second):
return False return False
def get_entrance_replacing(region, entrance_name, player): def get_entrance_replacing(region, entrance_name, player):
original_entrance = region.world.get_entrance(entrance_name, player) original_entrance = region.multiworld.get_entrance(entrance_name, player)
if not original_entrance.shuffled: if not original_entrance.shuffled:
return original_entrance return original_entrance
@ -842,14 +842,14 @@ def get_entrance_replacing(region, entrance_name, player):
def change_connections(entrance, target): def change_connections(entrance, target):
entrance.connect(target.disconnect()) entrance.connect(target.disconnect())
entrance.replaces = target.replaces entrance.replaces = target.replaces
if entrance.reverse and not entrance.world.worlds[entrance.player].decouple_entrances: if entrance.reverse and not entrance.multiworld.worlds[entrance.player].decouple_entrances:
target.replaces.reverse.connect(entrance.reverse.assumed.disconnect()) target.replaces.reverse.connect(entrance.reverse.assumed.disconnect())
target.replaces.reverse.replaces = entrance.reverse target.replaces.reverse.replaces = entrance.reverse
def restore_connections(entrance, target): def restore_connections(entrance, target):
target.connect(entrance.disconnect()) target.connect(entrance.disconnect())
entrance.replaces = None entrance.replaces = None
if entrance.reverse and not entrance.world.worlds[entrance.player].decouple_entrances: if entrance.reverse and not entrance.multiworld.worlds[entrance.player].decouple_entrances:
entrance.reverse.assumed.connect(target.replaces.reverse.disconnect()) entrance.reverse.assumed.connect(target.replaces.reverse.disconnect())
target.replaces.reverse.replaces = None target.replaces.reverse.replaces = None
@ -866,7 +866,7 @@ def check_entrances_compatibility(entrance, target, rollbacks):
def confirm_replacement(entrance, target): def confirm_replacement(entrance, target):
delete_target_entrance(target) delete_target_entrance(target)
logging.getLogger('').debug(f'Connected {entrance} to {entrance.connected_region}') logging.getLogger('').debug(f'Connected {entrance} to {entrance.connected_region}')
if entrance.reverse and not entrance.world.worlds[entrance.player].decouple_entrances: if entrance.reverse and not entrance.multiworld.worlds[entrance.player].decouple_entrances:
replaced_reverse = target.replaces.reverse replaced_reverse = target.replaces.reverse
delete_target_entrance(entrance.reverse.assumed) delete_target_entrance(entrance.reverse.assumed)
logging.getLogger('').debug(f'Connected {replaced_reverse} to {replaced_reverse.connected_region}') logging.getLogger('').debug(f'Connected {replaced_reverse} to {replaced_reverse.connected_region}')

View File

@ -131,13 +131,13 @@ def getItemGenericName(item):
def isRestrictedDungeonItem(dungeon, item): def isRestrictedDungeonItem(dungeon, item):
if not isinstance(item, OOTItem): if not isinstance(item, OOTItem):
return False return False
if (item.map or item.compass) and dungeon.world.shuffle_mapcompass == 'dungeon': if (item.map or item.compass) and dungeon.multiworld.shuffle_mapcompass == 'dungeon':
return item in dungeon.dungeon_items return item in dungeon.dungeon_items
if item.type == 'SmallKey' and dungeon.world.shuffle_smallkeys == 'dungeon': if item.type == 'SmallKey' and dungeon.multiworld.shuffle_smallkeys == 'dungeon':
return item in dungeon.small_keys return item in dungeon.small_keys
if item.type == 'BossKey' and dungeon.world.shuffle_bosskeys == 'dungeon': if item.type == 'BossKey' and dungeon.multiworld.shuffle_bosskeys == 'dungeon':
return item in dungeon.boss_key return item in dungeon.boss_key
if item.type == 'GanonBossKey' and dungeon.world.shuffle_ganon_bosskey == 'dungeon': if item.type == 'GanonBossKey' and dungeon.multiworld.shuffle_ganon_bosskey == 'dungeon':
return item in dungeon.boss_key return item in dungeon.boss_key
return False return False
@ -148,7 +148,7 @@ def isRestrictedDungeonItem(dungeon, item):
def attach_name(text, hinted_object, world): def attach_name(text, hinted_object, world):
if hinted_object.player == world.player: if hinted_object.player == world.player:
return text return text
return f"{text} for {world.world.get_player_name(hinted_object.player)}" return f"{text} for {world.multiworld.get_player_name(hinted_object.player)}"
def add_hint(world, groups, gossip_text, count, location=None, force_reachable=False): def add_hint(world, groups, gossip_text, count, location=None, force_reachable=False):
@ -439,7 +439,7 @@ def get_specific_item_hint(world, checked):
itemname = world.named_item_pool.pop(0) itemname = world.named_item_pool.pop(0)
if itemname == "Bottle" and world.hint_dist == "bingo": if itemname == "Bottle" and world.hint_dist == "bingo":
locations = [ locations = [
location for location in world.world.get_filled_locations() location for location in world.multiworld.get_filled_locations()
if (is_not_checked(location, checked) if (is_not_checked(location, checked)
and location.name not in world.hint_exclusions and location.name not in world.hint_exclusions
and location.item.name in bingoBottlesForHints and location.item.name in bingoBottlesForHints
@ -448,7 +448,7 @@ def get_specific_item_hint(world, checked):
] ]
else: else:
locations = [ locations = [
location for location in world.world.get_filled_locations() location for location in world.multiworld.get_filled_locations()
if (is_not_checked(location, checked) if (is_not_checked(location, checked)
and location.name not in world.hint_exclusions and location.name not in world.hint_exclusions
and location.item.name == itemname and location.item.name == itemname
@ -489,7 +489,7 @@ def get_random_location_hint(world, checked):
and location.name not in world.hint_exclusions and location.name not in world.hint_exclusions
and location.name not in world.hint_type_overrides['item'] and location.name not in world.hint_type_overrides['item']
and location.item.name not in world.item_hint_type_overrides['item'], and location.item.name not in world.item_hint_type_overrides['item'],
world.world.get_filled_locations(world.player))) world.multiworld.get_filled_locations(world.player)))
if not locations: if not locations:
return None return None
@ -639,13 +639,13 @@ def buildWorldGossipHints(world, checkedLocations=None):
world.woth_dungeon = 0 world.woth_dungeon = 0
if checkedLocations is None: if checkedLocations is None:
checkedLocations = {player: set() for player in world.world.get_all_ids()} checkedLocations = {player: set() for player in world.multiworld.get_all_ids()}
# If Ganondorf hints Light Arrows and is reachable without them, add to checkedLocations to prevent extra hinting # If Ganondorf hints Light Arrows and is reachable without them, add to checkedLocations to prevent extra hinting
# Can only be forced with vanilla bridge or trials # Can only be forced with vanilla bridge or trials
if world.bridge != 'vanilla' and world.trials == 0 and world.misc_hints: if world.bridge != 'vanilla' and world.trials == 0 and world.misc_hints:
try: try:
light_arrow_location = world.world.find_item("Light Arrows", world.player) light_arrow_location = world.multiworld.find_item("Light Arrows", world.player)
checkedLocations[light_arrow_location.player].add(light_arrow_location.name) checkedLocations[light_arrow_location.player].add(light_arrow_location.name)
except StopIteration: # start with them except StopIteration: # start with them
pass pass
@ -885,7 +885,7 @@ def buildAltarHints(world, messages, include_rewards=True, include_wincons=True)
# pulls text string from hintlist for reward after sending the location to hintlist. # pulls text string from hintlist for reward after sending the location to hintlist.
def buildBossString(reward, color, world): def buildBossString(reward, color, world):
for location in world.world.get_filled_locations(world.player): for location in world.multiworld.get_filled_locations(world.player):
if location.item.name == reward: if location.item.name == reward:
item_icon = chr(location.item.special['item_id']) item_icon = chr(location.item.special['item_id'])
location_text = getHint(location.name, world.clearer_hints).text location_text = getHint(location.name, world.clearer_hints).text
@ -956,18 +956,18 @@ def buildGanonText(world, messages):
text += "\x05\x42your pocket\x05\x40" text += "\x05\x42your pocket\x05\x40"
else: else:
try: try:
find_light_arrows = world.world.find_item('Light Arrows', world.player) find_light_arrows = world.multiworld.find_item('Light Arrows', world.player)
text = get_raw_text(getHint('Light Arrow Location', world.clearer_hints).text) text = get_raw_text(getHint('Light Arrow Location', world.clearer_hints).text)
location = find_light_arrows location = find_light_arrows
location_hint = get_hint_area(location) location_hint = get_hint_area(location)
if world.player != location.player: if world.player != location.player:
text += "\x05\x42%s's\x05\x40 %s" % (world.world.get_player_name(location.player), get_raw_text(location_hint)) text += "\x05\x42%s's\x05\x40 %s" % (world.multiworld.get_player_name(location.player), get_raw_text(location_hint))
else: else:
location_hint = location_hint.replace('Ganon\'s Castle', 'my castle') location_hint = location_hint.replace('Ganon\'s Castle', 'my castle')
text += get_raw_text(location_hint) text += get_raw_text(location_hint)
except StopIteration: except StopIteration:
text = get_raw_text(getHint('Validation Line', world.clearer_hints).text) text = get_raw_text(getHint('Validation Line', world.clearer_hints).text)
for location in world.world.get_filled_locations(world.player): for location in world.multiworld.get_filled_locations(world.player):
if location.name == 'Ganons Tower Boss Key Chest': if location.name == 'Ganons Tower Boss Key Chest':
text += get_raw_text(getHint(getItemGenericName(location.item), world.clearer_hints).text) text += get_raw_text(getHint(getItemGenericName(location.item), world.clearer_hints).text)
break break

View File

@ -748,7 +748,7 @@ def replace_max_item(items, item, max):
def generate_itempool(ootworld): def generate_itempool(ootworld):
world = ootworld.world world = ootworld.multiworld
player = ootworld.player player = ootworld.player
global random global random
random = world.random random = world.random
@ -1280,32 +1280,32 @@ def get_pool_core(world):
if world.free_scarecrow: if world.free_scarecrow:
item = world.create_item('Scarecrow Song') item = world.create_item('Scarecrow Song')
world.world.push_precollected(item) world.multiworld.push_precollected(item)
world.remove_from_start_inventory.append(item.name) world.remove_from_start_inventory.append(item.name)
if world.no_epona_race: if world.no_epona_race:
item = world.create_item('Epona') item = world.create_item('Epona')
world.world.push_precollected(item) world.multiworld.push_precollected(item)
world.remove_from_start_inventory.append(item.name) world.remove_from_start_inventory.append(item.name)
if world.shuffle_mapcompass == 'remove' or world.shuffle_mapcompass == 'startwith': if world.shuffle_mapcompass == 'remove' or world.shuffle_mapcompass == 'startwith':
for item in [item for dungeon in world.dungeons for item in dungeon.dungeon_items]: for item in [item for dungeon in world.dungeons for item in dungeon.dungeon_items]:
world.world.push_precollected(item) world.multiworld.push_precollected(item)
world.remove_from_start_inventory.append(item.name) world.remove_from_start_inventory.append(item.name)
pool.extend(get_junk_item()) pool.extend(get_junk_item())
if world.shuffle_smallkeys == 'remove': if world.shuffle_smallkeys == 'remove':
for item in [item for dungeon in world.dungeons for item in dungeon.small_keys]: for item in [item for dungeon in world.dungeons for item in dungeon.small_keys]:
world.world.push_precollected(item) world.multiworld.push_precollected(item)
world.remove_from_start_inventory.append(item.name) world.remove_from_start_inventory.append(item.name)
pool.extend(get_junk_item()) pool.extend(get_junk_item())
if world.shuffle_bosskeys == 'remove': if world.shuffle_bosskeys == 'remove':
for item in [item for dungeon in world.dungeons if dungeon.name != 'Ganons Castle' for item in dungeon.boss_key]: for item in [item for dungeon in world.dungeons if dungeon.name != 'Ganons Castle' for item in dungeon.boss_key]:
world.world.push_precollected(item) world.multiworld.push_precollected(item)
world.remove_from_start_inventory.append(item.name) world.remove_from_start_inventory.append(item.name)
pool.extend(get_junk_item()) pool.extend(get_junk_item())
if world.shuffle_ganon_bosskey in ['remove', 'triforce']: if world.shuffle_ganon_bosskey in ['remove', 'triforce']:
for item in [item for dungeon in world.dungeons if dungeon.name == 'Ganons Castle' for item in dungeon.boss_key]: for item in [item for dungeon in world.dungeons if dungeon.name == 'Ganons Castle' for item in dungeon.boss_key]:
world.world.push_precollected(item) world.multiworld.push_precollected(item)
world.remove_from_start_inventory.append(item.name) world.remove_from_start_inventory.append(item.name)
pool.extend(get_junk_item()) pool.extend(get_junk_item())
@ -1331,7 +1331,7 @@ def get_pool_core(world):
# Yes somehow you need 3 keys. This dungeon is bonkers # Yes somehow you need 3 keys. This dungeon is bonkers
items = [world.create_item('Small Key (Spirit Temple)') for i in range(3)] items = [world.create_item('Small Key (Spirit Temple)') for i in range(3)]
for item in items: for item in items:
world.world.push_precollected(item) world.multiworld.push_precollected(item)
world.remove_from_start_inventory.append(item.name) world.remove_from_start_inventory.append(item.name)
#if not world.dungeon_mq['Fire Temple']: #if not world.dungeon_mq['Fire Temple']:
# world.state.collect(ItemFactory('Small Key (Fire Temple)')) # world.state.collect(ItemFactory('Small Key (Fire Temple)'))
@ -1346,7 +1346,7 @@ def get_pool_core(world):
if not world.keysanity and not world.dungeon_mq['Fire Temple']: if not world.keysanity and not world.dungeon_mq['Fire Temple']:
item = world.create_item('Small Key (Fire Temple)') item = world.create_item('Small Key (Fire Temple)')
world.world.push_precollected(item) world.multiworld.push_precollected(item)
world.remove_from_start_inventory.append(item.name) world.remove_from_start_inventory.append(item.name)
if world.triforce_hunt: if world.triforce_hunt:

View File

@ -882,7 +882,7 @@ def make_player_message(text):
def update_item_messages(messages, world): def update_item_messages(messages, world):
new_item_messages = {**ITEM_MESSAGES, **KEYSANITY_MESSAGES} new_item_messages = {**ITEM_MESSAGES, **KEYSANITY_MESSAGES}
for id, text in new_item_messages.items(): for id, text in new_item_messages.items():
if len(world.world.worlds) > 1: if len(world.multiworld.worlds) > 1:
update_message_by_id(messages, id, make_player_message(text), 0x23) update_message_by_id(messages, id, make_player_message(text), 0x23)
else: else:
update_message_by_id(messages, id, text, 0x23) update_message_by_id(messages, id, text, 0x23)
@ -1020,7 +1020,7 @@ def update_warp_song_text(messages, ootworld):
} }
for id, entr in msg_list.items(): for id, entr in msg_list.items():
destination = ootworld.world.get_entrance(entr, ootworld.player).connected_region destination = ootworld.multiworld.get_entrance(entr, ootworld.player).connected_region
if destination.pretty_name: if destination.pretty_name:
destination_name = destination.pretty_name destination_name = destination.pretty_name

View File

@ -1326,7 +1326,7 @@ def patch_rom(world, rom):
override_table = get_override_table(world) override_table = get_override_table(world)
rom.write_bytes(rom.sym('cfg_item_overrides'), get_override_table_bytes(override_table)) rom.write_bytes(rom.sym('cfg_item_overrides'), get_override_table_bytes(override_table))
rom.write_byte(rom.sym('PLAYER_ID'), min(world.player, 255)) # Write player ID rom.write_byte(rom.sym('PLAYER_ID'), min(world.player, 255)) # Write player ID
rom.write_bytes(rom.sym('AP_PLAYER_NAME'), bytearray(world.world.get_player_name(world.player), 'ascii')) rom.write_bytes(rom.sym('AP_PLAYER_NAME'), bytearray(world.multiworld.get_player_name(world.player), 'ascii'))
if world.death_link: if world.death_link:
rom.write_byte(rom.sym('DEATH_LINK'), 0x01) rom.write_byte(rom.sym('DEATH_LINK'), 0x01)
@ -1359,7 +1359,7 @@ def patch_rom(world, rom):
rom.write_byte(rom.sym('CFG_DAMAGE_MULTIPLYER'), 3) rom.write_byte(rom.sym('CFG_DAMAGE_MULTIPLYER'), 3)
# Patch songs and boss rewards # Patch songs and boss rewards
for location in world.world.get_filled_locations(world.player): for location in world.multiworld.get_filled_locations(world.player):
item = location.item item = location.item
special = item.special if item.game == 'Ocarina of Time' else {} # this shouldn't matter hopefully special = item.special if item.game == 'Ocarina of Time' else {} # this shouldn't matter hopefully
locationaddress = location.address1 locationaddress = location.address1
@ -1686,7 +1686,7 @@ def patch_rom(world, rom):
pass pass
elif dungeon in ['Bottom of the Well', 'Ice Cavern']: elif dungeon in ['Bottom of the Well', 'Ice Cavern']:
dungeon_name, boss_name, compass_id, map_id = dungeon_list[dungeon] dungeon_name, boss_name, compass_id, map_id = dungeon_list[dungeon]
if len(world.world.worlds) > 1: if len(world.multiworld.worlds) > 1:
map_message = "\x13\x76\x08\x05\x42\x0F\x05\x40 found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x09" % (dungeon_name) map_message = "\x13\x76\x08\x05\x42\x0F\x05\x40 found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x09" % (dungeon_name)
else: else:
map_message = "\x13\x76\x08You found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x01It\'s %s!\x09" % (dungeon_name, "masterful" if world.dungeon_mq[dungeon] else "ordinary") map_message = "\x13\x76\x08You found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x01It\'s %s!\x09" % (dungeon_name, "masterful" if world.dungeon_mq[dungeon] else "ordinary")
@ -1696,13 +1696,13 @@ def patch_rom(world, rom):
else: else:
dungeon_name, boss_name, compass_id, map_id = dungeon_list[dungeon] dungeon_name, boss_name, compass_id, map_id = dungeon_list[dungeon]
dungeon_reward = reward_list[world.get_location(boss_name).item.name] dungeon_reward = reward_list[world.get_location(boss_name).item.name]
if len(world.world.worlds) > 1: if len(world.multiworld.worlds) > 1:
compass_message = "\x13\x75\x08\x05\x42\x0F\x05\x40 found the \x05\x41Compass\x05\x40\x01for %s\x05\x40!\x09" % (dungeon_name) compass_message = "\x13\x75\x08\x05\x42\x0F\x05\x40 found the \x05\x41Compass\x05\x40\x01for %s\x05\x40!\x09" % (dungeon_name)
else: else:
compass_message = "\x13\x75\x08You found the \x05\x41Compass\x05\x40\x01for %s\x05\x40!\x01It holds the %s!\x09" % (dungeon_name, dungeon_reward) compass_message = "\x13\x75\x08You found the \x05\x41Compass\x05\x40\x01for %s\x05\x40!\x01It holds the %s!\x09" % (dungeon_name, dungeon_reward)
update_message_by_id(messages, compass_id, compass_message) update_message_by_id(messages, compass_id, compass_message)
if world.mq_dungeons_random or world.mq_dungeons != 0 and world.mq_dungeons != 12: if world.mq_dungeons_random or world.mq_dungeons != 0 and world.mq_dungeons != 12:
if len(world.world.worlds) > 1: if len(world.multiworld.worlds) > 1:
map_message = "\x13\x76\x08\x05\x42\x0F\x05\x40 found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x09" % (dungeon_name) map_message = "\x13\x76\x08\x05\x42\x0F\x05\x40 found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x09" % (dungeon_name)
else: else:
map_message = "\x13\x76\x08You found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x01It\'s %s!\x09" % (dungeon_name, "masterful" if world.dungeon_mq[dungeon] else "ordinary") map_message = "\x13\x76\x08You found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x01It\'s %s!\x09" % (dungeon_name, "masterful" if world.dungeon_mq[dungeon] else "ordinary")
@ -1730,7 +1730,7 @@ def patch_rom(world, rom):
rom.write_int16(0xB6D57E, 0x0003) rom.write_int16(0xB6D57E, 0x0003)
rom.write_int16(0xB6EC52, 999) rom.write_int16(0xB6EC52, 999)
tycoon_message = "\x08\x13\x57You got a \x05\x43Tycoon's Wallet\x05\x40!\x01Now you can hold\x01up to \x05\x46999\x05\x40 \x05\x46Rupees\x05\x40." tycoon_message = "\x08\x13\x57You got a \x05\x43Tycoon's Wallet\x05\x40!\x01Now you can hold\x01up to \x05\x46999\x05\x40 \x05\x46Rupees\x05\x40."
if len(world.world.worlds) > 1: if len(world.multiworld.worlds) > 1:
tycoon_message = make_player_message(tycoon_message) tycoon_message = make_player_message(tycoon_message)
update_message_by_id(messages, 0x00F8, tycoon_message, 0x23) update_message_by_id(messages, 0x00F8, tycoon_message, 0x23)
@ -1844,7 +1844,7 @@ def write_rom_item(rom, item_id, item):
def get_override_table(world): def get_override_table(world):
return list(filter(lambda val: val != None, map(partial(get_override_entry, world), world.world.get_filled_locations(world.player)))) return list(filter(lambda val: val != None, map(partial(get_override_entry, world), world.multiworld.get_filled_locations(world.player))))
override_struct = struct.Struct('>xBBBHBB') # match override_t in get_items.c override_struct = struct.Struct('>xBBBHBB') # match override_t in get_items.c
@ -2154,8 +2154,8 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
if location.item.name == 'Ice Trap': if location.item.name == 'Ice Trap':
split_item_name[0] = create_fake_name(split_item_name[0]) split_item_name[0] = create_fake_name(split_item_name[0])
if len(world.world.worlds) > 1: # OOTWorld.MultiWorld.AutoWorld[] if len(world.multiworld.worlds) > 1: # OOTWorld.MultiWorld.AutoWorld[]
description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1], world.world.get_player_name(location.item.player)) description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1], world.multiworld.get_player_name(location.item.player))
else: else:
description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1]) description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1])
purchase_text = '\x08%s %d Rupees\x09\x01%s\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (split_item_name[0], location.price, split_item_name[1]) purchase_text = '\x08%s %d Rupees\x09\x01%s\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (split_item_name[0], location.price, split_item_name[1])
@ -2168,10 +2168,10 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
if location.item.trap: if location.item.trap:
shop_item_name = create_fake_name(shop_item_name) shop_item_name = create_fake_name(shop_item_name)
if len(world.world.worlds) > 1: if len(world.multiworld.worlds) > 1:
shop_item_name = ''.join(filter(lambda char: char in character_table, shop_item_name)) shop_item_name = ''.join(filter(lambda char: char in character_table, shop_item_name))
do_line_break = sum(character_table[char] for char in f"{shop_item_name} {location.price} Rupees") > NORMAL_LINE_WIDTH do_line_break = sum(character_table[char] for char in f"{shop_item_name} {location.price} Rupees") > NORMAL_LINE_WIDTH
description_text = '\x08\x05\x41%s%s%d Rupees\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (shop_item_name, '\x01' if do_line_break else ' ', location.price, world.world.get_player_name(location.item.player)) description_text = '\x08\x05\x41%s%s%d Rupees\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (shop_item_name, '\x01' if do_line_break else ' ', location.price, world.multiworld.get_player_name(location.item.player))
else: else:
description_text = '\x08\x05\x41%s %d Rupees\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (shop_item_name, location.price) description_text = '\x08\x05\x41%s %d Rupees\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (shop_item_name, location.price)
purchase_text = '\x08%s %d Rupees\x09\x01\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (shop_item_name, location.price) purchase_text = '\x08%s %d Rupees\x09\x01\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (shop_item_name, location.price)

View File

@ -53,7 +53,7 @@ def isliteral(expr):
class Rule_AST_Transformer(ast.NodeTransformer): class Rule_AST_Transformer(ast.NodeTransformer):
def __init__(self, world, player): def __init__(self, world, player):
self.world = world self.multiworld = world
self.player = player self.player = player
self.events = set() self.events = set()
# map Region -> rule ast string -> item name # map Region -> rule ast string -> item name
@ -86,9 +86,9 @@ class Rule_AST_Transformer(ast.NodeTransformer):
ctx=ast.Load()), ctx=ast.Load()),
args=[ast.Str(escaped_items[node.id]), ast.Constant(self.player)], args=[ast.Str(escaped_items[node.id]), ast.Constant(self.player)],
keywords=[]) keywords=[])
elif node.id in self.world.__dict__: elif node.id in self.multiworld.__dict__:
# Settings are constant # Settings are constant
return ast.parse('%r' % self.world.__dict__[node.id], mode='eval').body return ast.parse('%r' % self.multiworld.__dict__[node.id], mode='eval').body
elif node.id in State.__dict__: elif node.id in State.__dict__:
return self.make_call(node, node.id, [], []) return self.make_call(node, node.id, [], [])
elif node.id in self.kwarg_defaults or node.id in allowed_globals: elif node.id in self.kwarg_defaults or node.id in allowed_globals:
@ -137,7 +137,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
if isinstance(count, ast.Name): if isinstance(count, ast.Name):
# Must be a settings constant # Must be a settings constant
count = ast.parse('%r' % self.world.__dict__[count.id], mode='eval').body count = ast.parse('%r' % self.multiworld.__dict__[count.id], mode='eval').body
if iname in escaped_items: if iname in escaped_items:
iname = escaped_items[iname] iname = escaped_items[iname]
@ -182,7 +182,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
new_args = [] new_args = []
for child in node.args: for child in node.args:
if isinstance(child, ast.Name): if isinstance(child, ast.Name):
if child.id in self.world.__dict__: if child.id in self.multiworld.__dict__:
# child = ast.Attribute( # child = ast.Attribute(
# value=ast.Attribute( # value=ast.Attribute(
# value=ast.Name(id='state', ctx=ast.Load()), # value=ast.Name(id='state', ctx=ast.Load()),
@ -190,7 +190,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
# ctx=ast.Load()), # ctx=ast.Load()),
# attr=child.id, # attr=child.id,
# ctx=ast.Load()) # ctx=ast.Load())
child = ast.Constant(getattr(self.world, child.id)) child = ast.Constant(getattr(self.multiworld, child.id))
elif child.id in rule_aliases: elif child.id in rule_aliases:
child = self.visit(child) child = self.visit(child)
elif child.id in escaped_items: elif child.id in escaped_items:
@ -217,7 +217,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
value=ast.Attribute( value=ast.Attribute(
value=ast.Attribute( value=ast.Attribute(
value=ast.Name(id='state', ctx=ast.Load()), value=ast.Name(id='state', ctx=ast.Load()),
attr='world', attr='multiworld',
ctx=ast.Load()), ctx=ast.Load()),
attr='worlds', attr='worlds',
ctx=ast.Load()), ctx=ast.Load()),
@ -242,7 +242,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
# Fast check for json can_use # Fast check for json can_use
if (len(node.ops) == 1 and isinstance(node.ops[0], ast.Eq) if (len(node.ops) == 1 and isinstance(node.ops[0], ast.Eq)
and isinstance(node.left, ast.Name) and isinstance(node.comparators[0], ast.Name) and isinstance(node.left, ast.Name) and isinstance(node.comparators[0], ast.Name)
and node.left.id not in self.world.__dict__ and node.comparators[0].id not in self.world.__dict__): and node.left.id not in self.multiworld.__dict__ and node.comparators[0].id not in self.multiworld.__dict__):
return ast.NameConstant(node.left.id == node.comparators[0].id) return ast.NameConstant(node.left.id == node.comparators[0].id)
node.left = escape_or_string(node.left) node.left = escape_or_string(node.left)
@ -378,7 +378,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
# Requires the target regions have been defined in the world. # Requires the target regions have been defined in the world.
def create_delayed_rules(self): def create_delayed_rules(self):
for region_name, node, subrule_name in self.delayed_rules: for region_name, node, subrule_name in self.delayed_rules:
region = self.world.world.get_region(region_name, self.player) region = self.multiworld.multiworld.get_region(region_name, self.player)
event = OOTLocation(self.player, subrule_name, type='Event', parent=region, internal=True) event = OOTLocation(self.player, subrule_name, type='Event', parent=region, internal=True)
event.show_in_spoiler = False event.show_in_spoiler = False
@ -395,7 +395,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
set_rule(event, access_rule) set_rule(event, access_rule)
region.locations.append(event) region.locations.append(event)
self.world.make_event_item(subrule_name, event) self.multiworld.make_event_item(subrule_name, event)
# Safeguard in case this is called multiple times per world # Safeguard in case this is called multiple times per world
self.delayed_rules.clear() self.delayed_rules.clear()
@ -448,7 +448,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
## Handlers for compile-time optimizations (former State functions) ## Handlers for compile-time optimizations (former State functions)
def at_day(self, node): def at_day(self, node):
if self.world.ensure_tod_access: if self.multiworld.ensure_tod_access:
# tod has DAY or (tod == NONE and (ss or find a path from a provider)) # tod has DAY or (tod == NONE and (ss or find a path from a provider))
# parsing is better than constructing this expression by hand # parsing is better than constructing this expression by hand
r = self.current_spot if type(self.current_spot) == OOTRegion else self.current_spot.parent_region r = self.current_spot if type(self.current_spot) == OOTRegion else self.current_spot.parent_region
@ -456,7 +456,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
return ast.NameConstant(True) return ast.NameConstant(True)
def at_dampe_time(self, node): def at_dampe_time(self, node):
if self.world.ensure_tod_access: if self.multiworld.ensure_tod_access:
# tod has DAMPE or (tod == NONE and (find a path from a provider)) # tod has DAMPE or (tod == NONE and (find a path from a provider))
# parsing is better than constructing this expression by hand # parsing is better than constructing this expression by hand
r = self.current_spot if type(self.current_spot) == OOTRegion else self.current_spot.parent_region r = self.current_spot if type(self.current_spot) == OOTRegion else self.current_spot.parent_region
@ -464,10 +464,10 @@ class Rule_AST_Transformer(ast.NodeTransformer):
return ast.NameConstant(True) return ast.NameConstant(True)
def at_night(self, node): def at_night(self, node):
if self.current_spot.type == 'GS Token' and self.world.logic_no_night_tokens_without_suns_song: if self.current_spot.type == 'GS Token' and self.multiworld.logic_no_night_tokens_without_suns_song:
# Using visit here to resolve 'can_play' rule # Using visit here to resolve 'can_play' rule
return self.visit(ast.parse('can_play(Suns_Song)', mode='eval').body) return self.visit(ast.parse('can_play(Suns_Song)', mode='eval').body)
if self.world.ensure_tod_access: if self.multiworld.ensure_tod_access:
# tod has DAMPE or (tod == NONE and (ss or find a path from a provider)) # tod has DAMPE or (tod == NONE and (ss or find a path from a provider))
# parsing is better than constructing this expression by hand # parsing is better than constructing this expression by hand
r = self.current_spot if type(self.current_spot) == OOTRegion else self.current_spot.parent_region r = self.current_spot if type(self.current_spot) == OOTRegion else self.current_spot.parent_region
@ -501,7 +501,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
return ast.parse(f"state._oot_reach_as_age('{r.name}', 'adult', {self.player})", mode='eval').body return ast.parse(f"state._oot_reach_as_age('{r.name}', 'adult', {self.player})", mode='eval').body
def current_spot_starting_age_access(self, node): def current_spot_starting_age_access(self, node):
return self.current_spot_child_access(node) if self.world.starting_age == 'child' else self.current_spot_adult_access(node) return self.current_spot_child_access(node) if self.multiworld.starting_age == 'child' else self.current_spot_adult_access(node)
def has_bottle(self, node): def has_bottle(self, node):
return ast.parse(f"state._oot_has_bottle({self.player})", mode='eval').body return ast.parse(f"state._oot_has_bottle({self.player})", mode='eval').body

View File

@ -26,7 +26,7 @@ class OOTLogic(LogicMixin):
# Used for fall damage and other situations where damage is unavoidable # Used for fall damage and other situations where damage is unavoidable
def _oot_can_live_dmg(self, player, hearts): def _oot_can_live_dmg(self, player, hearts):
mult = self.world.worlds[player].damage_multiplier mult = self.multiworld.worlds[player].damage_multiplier
if hearts*4 >= 3: if hearts*4 >= 3:
return mult != 'ohko' and mult != 'quadruple' return mult != 'ohko' and mult != 'quadruple'
else: else:
@ -39,7 +39,7 @@ class OOTLogic(LogicMixin):
def _oot_reach_as_age(self, regionname, age, player): def _oot_reach_as_age(self, regionname, age, player):
if self.age[player] is None: if self.age[player] is None:
self.age[player] = age self.age[player] = age
can_reach = self.world.get_region(regionname, player).can_reach(self) can_reach = self.multiworld.get_region(regionname, player).can_reach(self)
self.age[player] = None self.age[player] = None
return can_reach return can_reach
return self.age[player] == age return self.age[player] == age
@ -52,7 +52,7 @@ class OOTLogic(LogicMixin):
} }
if regionname in name_map[tod]: if regionname in name_map[tod]:
return True return True
region = self.world.get_region(regionname, player) region = self.multiworld.get_region(regionname, player)
if region.provides_time == TimeOfDay.ALL or regionname == 'Root': if region.provides_time == TimeOfDay.ALL or regionname == 'Root':
self.day_reachable_regions[player].add(regionname) self.day_reachable_regions[player].add(regionname)
self.dampe_reachable_regions[player].add(regionname) self.dampe_reachable_regions[player].add(regionname)
@ -82,7 +82,7 @@ class OOTLogic(LogicMixin):
rrp = getattr(self, f'{age}_reachable_regions')[player] rrp = getattr(self, f'{age}_reachable_regions')[player]
bc = getattr(self, f'{age}_blocked_connections')[player] bc = getattr(self, f'{age}_blocked_connections')[player]
queue = deque(getattr(self, f'{age}_blocked_connections')[player]) queue = deque(getattr(self, f'{age}_blocked_connections')[player])
start = self.world.get_region('Menu', player) start = self.multiworld.get_region('Menu', player)
# init on first call - this can't be done on construction since the regions don't exist yet # init on first call - this can't be done on construction since the regions don't exist yet
if not start in rrp: if not start in rrp:
@ -110,7 +110,7 @@ class OOTLogic(LogicMixin):
def set_rules(ootworld): def set_rules(ootworld):
logger = logging.getLogger('') logger = logging.getLogger('')
world = ootworld.world world = ootworld.multiworld
player = ootworld.player player = ootworld.player
if ootworld.logic_rules != 'no_logic': if ootworld.logic_rules != 'no_logic':
@ -213,10 +213,10 @@ def set_shop_rules(ootworld):
# The goal is to automatically set item rules based on age requirements in case entrances were shuffled # The goal is to automatically set item rules based on age requirements in case entrances were shuffled
def set_entrances_based_rules(ootworld): def set_entrances_based_rules(ootworld):
if ootworld.world.accessibility == 'beatable': if ootworld.multiworld.accessibility == 'beatable':
return return
all_state = ootworld.world.get_all_state(False) all_state = ootworld.multiworld.get_all_state(False)
for location in filter(lambda location: location.type == 'Shop', ootworld.get_locations()): for location in filter(lambda location: location.type == 'Shop', ootworld.get_locations()):
# If a shop is not reachable as adult, it can't have Goron Tunic or Zora Tunic as child can't buy these # If a shop is not reachable as adult, it can't have Goron Tunic or Zora Tunic as child can't buy these

View File

@ -118,14 +118,14 @@ class OOTWorld(World):
def generate_early(self): def generate_early(self):
# Player name MUST be at most 16 bytes ascii-encoded, otherwise won't write to ROM correctly # Player name MUST be at most 16 bytes ascii-encoded, otherwise won't write to ROM correctly
if len(bytes(self.world.get_player_name(self.player), 'ascii')) > 16: if len(bytes(self.multiworld.get_player_name(self.player), 'ascii')) > 16:
raise Exception( raise Exception(
f"OoT: Player {self.player}'s name ({self.world.get_player_name(self.player)}) must be ASCII-compatible") f"OoT: Player {self.player}'s name ({self.multiworld.get_player_name(self.player)}) must be ASCII-compatible")
self.parser = Rule_AST_Transformer(self, self.player) self.parser = Rule_AST_Transformer(self, self.player)
for (option_name, option) in oot_options.items(): for (option_name, option) in oot_options.items():
result = getattr(self.world, option_name)[self.player] result = getattr(self.multiworld, option_name)[self.player]
if isinstance(result, Range): if isinstance(result, Range):
option_value = int(result) option_value = int(result)
elif isinstance(result, Toggle): elif isinstance(result, Toggle):
@ -141,7 +141,7 @@ class OOTWorld(World):
self.remove_from_start_inventory = [] # some items will be precollected but not in the inventory self.remove_from_start_inventory = [] # some items will be precollected but not in the inventory
self.starting_items = Counter() self.starting_items = Counter()
self.starting_songs = False # whether starting_items contains a song self.starting_songs = False # whether starting_items contains a song
self.file_hash = [self.world.random.randint(0, 31) for i in range(5)] self.file_hash = [self.multiworld.random.randint(0, 31) for i in range(5)]
self.item_name_groups = { self.item_name_groups = {
"medallions": {"Light Medallion", "Forest Medallion", "Fire Medallion", "Water Medallion", "medallions": {"Light Medallion", "Forest Medallion", "Fire Medallion", "Water Medallion",
@ -185,13 +185,13 @@ class OOTWorld(World):
# Determine skipped trials in GT # Determine skipped trials in GT
# This needs to be done before the logic rules in GT are parsed # This needs to be done before the logic rules in GT are parsed
trial_list = ['Forest', 'Fire', 'Water', 'Spirit', 'Shadow', 'Light'] trial_list = ['Forest', 'Fire', 'Water', 'Spirit', 'Shadow', 'Light']
chosen_trials = self.world.random.sample(trial_list, self.trials) # chooses a list of trials to NOT skip chosen_trials = self.multiworld.random.sample(trial_list, self.trials) # chooses a list of trials to NOT skip
self.skipped_trials = {trial: (trial not in chosen_trials) for trial in trial_list} self.skipped_trials = {trial: (trial not in chosen_trials) for trial in trial_list}
# Determine which dungeons are MQ # Determine which dungeons are MQ
# Possible future plan: allow user to pick which dungeons are MQ # Possible future plan: allow user to pick which dungeons are MQ
if self.logic_rules == 'glitchless': if self.logic_rules == 'glitchless':
mq_dungeons = self.world.random.sample(dungeon_table, self.mq_dungeons) mq_dungeons = self.multiworld.random.sample(dungeon_table, self.mq_dungeons)
else: else:
self.mq_dungeons = 0 self.mq_dungeons = 0
mq_dungeons = [] mq_dungeons = []
@ -208,8 +208,8 @@ class OOTWorld(World):
# No Logic forces all tricks on, prog balancing off and beatable-only # No Logic forces all tricks on, prog balancing off and beatable-only
elif self.logic_rules == 'no_logic': elif self.logic_rules == 'no_logic':
self.world.progression_balancing[self.player].value = False self.multiworld.progression_balancing[self.player].value = False
self.world.accessibility[self.player] = self.world.accessibility[self.player].from_text("minimal") self.multiworld.accessibility[self.player] = self.multiworld.accessibility[self.player].from_text("minimal")
for trick in normalized_name_tricks.values(): for trick in normalized_name_tricks.values():
setattr(self, trick['name'], True) setattr(self, trick['name'], True)
@ -310,7 +310,7 @@ class OOTWorld(World):
for region in region_json: for region in region_json:
new_region = OOTRegion(region['region_name'], RegionType.Generic, None, self.player) new_region = OOTRegion(region['region_name'], RegionType.Generic, None, self.player)
new_region.world = self.world new_region.multiworld = self.multiworld
if 'pretty_name' in region: if 'pretty_name' in region:
new_region.pretty_name = region['pretty_name'] new_region.pretty_name = region['pretty_name']
if 'font_color' in region: if 'font_color' in region:
@ -355,19 +355,19 @@ class OOTWorld(World):
new_location.show_in_spoiler = False new_location.show_in_spoiler = False
if 'exits' in region: if 'exits' in region:
for exit, rule in region['exits'].items(): for exit, rule in region['exits'].items():
new_exit = OOTEntrance(self.player, self.world, '%s -> %s' % (new_region.name, exit), new_region) new_exit = OOTEntrance(self.player, self.multiworld, '%s -> %s' % (new_region.name, exit), new_region)
new_exit.vanilla_connected_region = exit new_exit.vanilla_connected_region = exit
new_exit.rule_string = rule new_exit.rule_string = rule
if self.world.logic_rules != 'none': if self.multiworld.logic_rules != 'none':
self.parser.parse_spot_rule(new_exit) self.parser.parse_spot_rule(new_exit)
if new_exit.never: if new_exit.never:
logger.debug('Dropping unreachable exit: %s', new_exit.name) logger.debug('Dropping unreachable exit: %s', new_exit.name)
else: else:
new_region.exits.append(new_exit) new_region.exits.append(new_exit)
self.world.regions.append(new_region) self.multiworld.regions.append(new_region)
self.regions.append(new_region) self.regions.append(new_region)
self.world._recache() self.multiworld._recache()
def set_scrub_prices(self): def set_scrub_prices(self):
# Get Deku Scrub Locations # Get Deku Scrub Locations
@ -387,7 +387,7 @@ class OOTWorld(World):
elif self.shuffle_scrubs == 'random': elif self.shuffle_scrubs == 'random':
# this is a random value between 0-99 # this is a random value between 0-99
# average value is ~33 rupees # average value is ~33 rupees
price = int(self.world.random.betavariate(1, 2) * 99) price = int(self.multiworld.random.betavariate(1, 2) * 99)
# Set price in the dictionary as well as the location. # Set price in the dictionary as well as the location.
self.scrub_prices[scrub_item] = price self.scrub_prices[scrub_item] = price
@ -402,14 +402,14 @@ class OOTWorld(World):
self.shop_prices = {} self.shop_prices = {}
for region in self.regions: for region in self.regions:
if self.shopsanity == 'random': if self.shopsanity == 'random':
shop_item_count = self.world.random.randint(0, 4) shop_item_count = self.multiworld.random.randint(0, 4)
else: else:
shop_item_count = int(self.shopsanity) shop_item_count = int(self.shopsanity)
for location in region.locations: for location in region.locations:
if location.type == 'Shop': if location.type == 'Shop':
if location.name[-1:] in shop_item_indexes[:shop_item_count]: if location.name[-1:] in shop_item_indexes[:shop_item_count]:
self.shop_prices[location.name] = int(self.world.random.betavariate(1.5, 2) * 60) * 5 self.shop_prices[location.name] = int(self.multiworld.random.betavariate(1.5, 2) * 60) * 5
def fill_bosses(self, bossCount=9): def fill_bosses(self, bossCount=9):
boss_location_names = ( boss_location_names = (
@ -424,7 +424,7 @@ class OOTWorld(World):
'Links Pocket' 'Links Pocket'
) )
boss_rewards = [item for item in self.itempool if item.type == 'DungeonReward'] boss_rewards = [item for item in self.itempool if item.type == 'DungeonReward']
boss_locations = [self.world.get_location(loc, self.player) for loc in boss_location_names] boss_locations = [self.multiworld.get_location(loc, self.player) for loc in boss_location_names]
placed_prizes = [loc.item.name for loc in boss_locations if loc.item is not None] placed_prizes = [loc.item.name for loc in boss_locations if loc.item is not None]
prizepool = [item for item in boss_rewards if item.name not in placed_prizes] prizepool = [item for item in boss_rewards if item.name not in placed_prizes]
@ -432,12 +432,12 @@ class OOTWorld(World):
while bossCount: while bossCount:
bossCount -= 1 bossCount -= 1
self.world.random.shuffle(prizepool) self.multiworld.random.shuffle(prizepool)
self.world.random.shuffle(prize_locs) self.multiworld.random.shuffle(prize_locs)
item = prizepool.pop() item = prizepool.pop()
loc = prize_locs.pop() loc = prize_locs.pop()
loc.place_locked_item(item) loc.place_locked_item(item)
self.world.itempool.remove(item) self.multiworld.itempool.remove(item)
def create_item(self, name: str): def create_item(self, name: str):
if name in item_table: if name in item_table:
@ -449,7 +449,7 @@ class OOTWorld(World):
def make_event_item(self, name, location, item=None): def make_event_item(self, name, location, item=None):
if item is None: if item is None:
item = self.create_item(name) item = self.create_item(name)
self.world.push_item(location, item, collect=False) self.multiworld.push_item(location, item, collect=False)
location.locked = True location.locked = True
location.event = True location.event = True
if name not in item_table: if name not in item_table:
@ -463,11 +463,11 @@ class OOTWorld(World):
world_type = 'Glitched World' world_type = 'Glitched World'
overworld_data_path = data_path(world_type, 'Overworld.json') overworld_data_path = data_path(world_type, 'Overworld.json')
menu = OOTRegion('Menu', None, None, self.player) menu = OOTRegion('Menu', None, None, self.player)
start = OOTEntrance(self.player, self.world, 'New Game', menu) start = OOTEntrance(self.player, self.multiworld, 'New Game', menu)
menu.exits.append(start) menu.exits.append(start)
self.world.regions.append(menu) self.multiworld.regions.append(menu)
self.load_regions_from_json(overworld_data_path) self.load_regions_from_json(overworld_data_path)
start.connect(self.world.get_region('Root', self.player)) start.connect(self.multiworld.get_region('Root', self.player))
create_dungeons(self) create_dungeons(self)
self.parser.create_delayed_rules() self.parser.create_delayed_rules()
@ -478,7 +478,7 @@ class OOTWorld(World):
# Bind entrances to vanilla # Bind entrances to vanilla
for region in self.regions: for region in self.regions:
for exit in region.exits: for exit in region.exits:
exit.connect(self.world.get_region(exit.vanilla_connected_region, self.player)) exit.connect(self.multiworld.get_region(exit.vanilla_connected_region, self.player))
def create_items(self): def create_items(self):
# Generate itempool # Generate itempool
@ -491,7 +491,7 @@ class OOTWorld(World):
junk_pool = get_junk_pool(self) junk_pool = get_junk_pool(self)
removed_items = [] removed_items = []
# Determine starting items # Determine starting items
for item in self.world.precollected_items[self.player]: for item in self.multiworld.precollected_items[self.player]:
if item.name in self.remove_from_start_inventory: if item.name in self.remove_from_start_inventory:
self.remove_from_start_inventory.remove(item.name) self.remove_from_start_inventory.remove(item.name)
removed_items.append(item.name) removed_items.append(item.name)
@ -512,7 +512,7 @@ class OOTWorld(World):
if self.start_with_rupees: if self.start_with_rupees:
self.starting_items['Rupees'] = 999 self.starting_items['Rupees'] = 999
self.world.itempool += self.itempool self.multiworld.itempool += self.itempool
self.remove_from_start_inventory.extend(removed_items) self.remove_from_start_inventory.extend(removed_items)
def set_rules(self): def set_rules(self):
@ -533,7 +533,7 @@ class OOTWorld(World):
for entrance in self.get_shuffled_entrances(): for entrance in self.get_shuffled_entrances():
if entrance.connected_region is not None: if entrance.connected_region is not None:
entrance.disconnect() entrance.disconnect()
entrance.connect(self.world.get_region(entrance.vanilla_connected_region, self.player)) entrance.connect(self.multiworld.get_region(entrance.vanilla_connected_region, self.player))
if entrance.assumed: if entrance.assumed:
assumed_entrance = entrance.assumed assumed_entrance = entrance.assumed
if assumed_entrance.connected_region is not None: if assumed_entrance.connected_region is not None:
@ -570,27 +570,27 @@ class OOTWorld(World):
# Kill unreachable events that can't be gotten even with all items # Kill unreachable events that can't be gotten even with all items
# Make sure to only kill actual internal events, not in-game "events" # Make sure to only kill actual internal events, not in-game "events"
all_state = self.world.get_all_state(False) all_state = self.multiworld.get_all_state(False)
all_locations = self.get_locations() all_locations = self.get_locations()
reachable = self.world.get_reachable_locations(all_state, self.player) reachable = self.multiworld.get_reachable_locations(all_state, self.player)
unreachable = [loc for loc in all_locations if unreachable = [loc for loc in all_locations if
(loc.internal or loc.type == 'Drop') and loc.event and loc.locked and loc not in reachable] (loc.internal or loc.type == 'Drop') and loc.event and loc.locked and loc not in reachable]
for loc in unreachable: for loc in unreachable:
loc.parent_region.locations.remove(loc) loc.parent_region.locations.remove(loc)
# Exception: Sell Big Poe is an event which is only reachable if Bottle with Big Poe is in the item pool. # Exception: Sell Big Poe is an event which is only reachable if Bottle with Big Poe is in the item pool.
# We allow it to be removed only if Bottle with Big Poe is not in the itempool. # We allow it to be removed only if Bottle with Big Poe is not in the itempool.
bigpoe = self.world.get_location('Sell Big Poe from Market Guard House', self.player) bigpoe = self.multiworld.get_location('Sell Big Poe from Market Guard House', self.player)
if not all_state.has('Bottle with Big Poe', self.player) and bigpoe not in reachable: if not all_state.has('Bottle with Big Poe', self.player) and bigpoe not in reachable:
bigpoe.parent_region.locations.remove(bigpoe) bigpoe.parent_region.locations.remove(bigpoe)
self.world.clear_location_cache() self.multiworld.clear_location_cache()
# If fast scarecrow then we need to kill the Pierre location as it will be unreachable # If fast scarecrow then we need to kill the Pierre location as it will be unreachable
if self.free_scarecrow: if self.free_scarecrow:
loc = self.world.get_location("Pierre", self.player) loc = self.multiworld.get_location("Pierre", self.player)
loc.parent_region.locations.remove(loc) loc.parent_region.locations.remove(loc)
# If open zora's domain then we need to kill Deliver Rutos Letter # If open zora's domain then we need to kill Deliver Rutos Letter
if self.zora_fountain == 'open': if self.zora_fountain == 'open':
loc = self.world.get_location("Deliver Rutos Letter", self.player) loc = self.multiworld.get_location("Deliver Rutos Letter", self.player)
loc.parent_region.locations.remove(loc) loc.parent_region.locations.remove(loc)
def pre_fill(self): def pre_fill(self):
@ -641,11 +641,11 @@ class OOTWorld(World):
if loc.item is None and ( if loc.item is None and (
self.shuffle_song_items != 'dungeon' or loc.name not in dungeon_song_locations)] self.shuffle_song_items != 'dungeon' or loc.name not in dungeon_song_locations)]
if itempools['dungeon']: # only do this if there's anything to shuffle if itempools['dungeon']: # only do this if there's anything to shuffle
dungeon_itempool = [item for item in self.world.itempool if item.player == self.player and item.name in itempools['dungeon']] dungeon_itempool = [item for item in self.multiworld.itempool if item.player == self.player and item.name in itempools['dungeon']]
for item in dungeon_itempool: for item in dungeon_itempool:
self.world.itempool.remove(item) self.multiworld.itempool.remove(item)
self.world.random.shuffle(dungeon_locations) self.multiworld.random.shuffle(dungeon_locations)
fill_restrictive(self.world, self.world.get_all_state(False), dungeon_locations, fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), dungeon_locations,
dungeon_itempool, True, True) dungeon_itempool, True, True)
any_dungeon_locations.extend(dungeon_locations) # adds only the unfilled locations any_dungeon_locations.extend(dungeon_locations) # adds only the unfilled locations
@ -653,22 +653,22 @@ class OOTWorld(World):
if self.shuffle_fortresskeys == 'any_dungeon': if self.shuffle_fortresskeys == 'any_dungeon':
itempools['any_dungeon'].add('Small Key (Thieves Hideout)') itempools['any_dungeon'].add('Small Key (Thieves Hideout)')
if itempools['any_dungeon']: if itempools['any_dungeon']:
any_dungeon_itempool = [item for item in self.world.itempool if item.player == self.player and item.name in itempools['any_dungeon']] any_dungeon_itempool = [item for item in self.multiworld.itempool if item.player == self.player and item.name in itempools['any_dungeon']]
for item in any_dungeon_itempool: for item in any_dungeon_itempool:
self.world.itempool.remove(item) self.multiworld.itempool.remove(item)
any_dungeon_itempool.sort(key=lambda item: any_dungeon_itempool.sort(key=lambda item:
{'GanonBossKey': 4, 'BossKey': 3, 'SmallKey': 2, 'HideoutSmallKey': 1}.get(item.type, 0)) {'GanonBossKey': 4, 'BossKey': 3, 'SmallKey': 2, 'HideoutSmallKey': 1}.get(item.type, 0))
self.world.random.shuffle(any_dungeon_locations) self.multiworld.random.shuffle(any_dungeon_locations)
fill_restrictive(self.world, self.world.get_all_state(False), any_dungeon_locations, fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), any_dungeon_locations,
any_dungeon_itempool, True, True) any_dungeon_itempool, True, True)
# If anything is overworld-only, fill into local non-dungeon locations # If anything is overworld-only, fill into local non-dungeon locations
if self.shuffle_fortresskeys == 'overworld': if self.shuffle_fortresskeys == 'overworld':
itempools['overworld'].add('Small Key (Thieves Hideout)') itempools['overworld'].add('Small Key (Thieves Hideout)')
if itempools['overworld']: if itempools['overworld']:
overworld_itempool = [item for item in self.world.itempool if item.player == self.player and item.name in itempools['overworld']] overworld_itempool = [item for item in self.multiworld.itempool if item.player == self.player and item.name in itempools['overworld']]
for item in overworld_itempool: for item in overworld_itempool:
self.world.itempool.remove(item) self.multiworld.itempool.remove(item)
overworld_itempool.sort(key=lambda item: overworld_itempool.sort(key=lambda item:
{'GanonBossKey': 4, 'BossKey': 3, 'SmallKey': 2, 'HideoutSmallKey': 1}.get(item.type, 0)) {'GanonBossKey': 4, 'BossKey': 3, 'SmallKey': 2, 'HideoutSmallKey': 1}.get(item.type, 0))
non_dungeon_locations = [loc for loc in self.get_locations() if non_dungeon_locations = [loc for loc in self.get_locations() if
@ -676,8 +676,8 @@ class OOTWorld(World):
(loc.type != 'Shop' or loc.name in self.shop_prices) and (loc.type != 'Shop' or loc.name in self.shop_prices) and
(loc.type != 'Song' or self.shuffle_song_items != 'song') and (loc.type != 'Song' or self.shuffle_song_items != 'song') and
(loc.name not in dungeon_song_locations or self.shuffle_song_items != 'dungeon')] (loc.name not in dungeon_song_locations or self.shuffle_song_items != 'dungeon')]
self.world.random.shuffle(non_dungeon_locations) self.multiworld.random.shuffle(non_dungeon_locations)
fill_restrictive(self.world, self.world.get_all_state(False), non_dungeon_locations, fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), non_dungeon_locations,
overworld_itempool, True, True) overworld_itempool, True, True)
# Place songs # Place songs
@ -686,16 +686,16 @@ class OOTWorld(World):
tries = 5 tries = 5
if self.shuffle_song_items == 'song': if self.shuffle_song_items == 'song':
song_locations = list(filter(lambda location: location.type == 'Song', song_locations = list(filter(lambda location: location.type == 'Song',
self.world.get_unfilled_locations(player=self.player))) self.multiworld.get_unfilled_locations(player=self.player)))
elif self.shuffle_song_items == 'dungeon': elif self.shuffle_song_items == 'dungeon':
song_locations = list(filter(lambda location: location.name in dungeon_song_locations, song_locations = list(filter(lambda location: location.name in dungeon_song_locations,
self.world.get_unfilled_locations(player=self.player))) self.multiworld.get_unfilled_locations(player=self.player)))
else: else:
raise Exception(f"Unknown song shuffle type: {self.shuffle_song_items}") raise Exception(f"Unknown song shuffle type: {self.shuffle_song_items}")
songs = list(filter(lambda item: item.player == self.player and item.type == 'Song', self.world.itempool)) songs = list(filter(lambda item: item.player == self.player and item.type == 'Song', self.multiworld.itempool))
for song in songs: for song in songs:
self.world.itempool.remove(song) self.multiworld.itempool.remove(song)
important_warps = (self.shuffle_special_interior_entrances or self.shuffle_overworld_entrances or important_warps = (self.shuffle_special_interior_entrances or self.shuffle_overworld_entrances or
self.warp_songs or self.spawn_positions) self.warp_songs or self.spawn_positions)
@ -717,8 +717,8 @@ class OOTWorld(World):
while tries: while tries:
try: try:
self.world.random.shuffle(song_locations) self.multiworld.random.shuffle(song_locations)
fill_restrictive(self.world, self.world.get_all_state(False), song_locations[:], songs[:], fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), song_locations[:], songs[:],
True, True) True, True)
logger.debug(f"Successfully placed songs for player {self.player} after {6 - tries} attempt(s)") logger.debug(f"Successfully placed songs for player {self.player} after {6 - tries} attempt(s)")
except FillError as e: except FillError as e:
@ -741,33 +741,33 @@ class OOTWorld(World):
# fast fill will fail because there is some logic on the shop items. we'll gather them up and place the shop items # fast fill will fail because there is some logic on the shop items. we'll gather them up and place the shop items
if self.shopsanity != 'off': if self.shopsanity != 'off':
shop_items = list( shop_items = list(
filter(lambda item: item.player == self.player and item.type == 'Shop', self.world.itempool)) filter(lambda item: item.player == self.player and item.type == 'Shop', self.multiworld.itempool))
shop_locations = list( shop_locations = list(
filter(lambda location: location.type == 'Shop' and location.name not in self.shop_prices, filter(lambda location: location.type == 'Shop' and location.name not in self.shop_prices,
self.world.get_unfilled_locations(player=self.player))) self.multiworld.get_unfilled_locations(player=self.player)))
shop_items.sort(key=lambda item: { shop_items.sort(key=lambda item: {
'Buy Deku Shield': 3 * int(self.open_forest == 'closed'), 'Buy Deku Shield': 3 * int(self.open_forest == 'closed'),
'Buy Goron Tunic': 2, 'Buy Goron Tunic': 2,
'Buy Zora Tunic': 2 'Buy Zora Tunic': 2
}.get(item.name, }.get(item.name,
int(item.advancement))) # place Deku Shields if needed, then tunics, then other advancement, then junk int(item.advancement))) # place Deku Shields if needed, then tunics, then other advancement, then junk
self.world.random.shuffle(shop_locations) self.multiworld.random.shuffle(shop_locations)
for item in shop_items: for item in shop_items:
self.world.itempool.remove(item) self.multiworld.itempool.remove(item)
fill_restrictive(self.world, self.world.get_all_state(False), shop_locations, shop_items, True, True) fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), shop_locations, shop_items, True, True)
set_shop_rules(self) # sets wallet requirements on shop items, must be done after they are filled set_shop_rules(self) # sets wallet requirements on shop items, must be done after they are filled
# If skip child zelda is active and Song from Impa is unfilled, put a local giveable item into it. # If skip child zelda is active and Song from Impa is unfilled, put a local giveable item into it.
impa = self.world.get_location("Song from Impa", self.player) impa = self.multiworld.get_location("Song from Impa", self.player)
if self.skip_child_zelda: if self.skip_child_zelda:
if impa.item is None: if impa.item is None:
item_to_place = self.world.random.choice(list(item for item in self.world.itempool if item_to_place = self.multiworld.random.choice(list(item for item in self.multiworld.itempool if
item.player == self.player and item.name in SaveContext.giveable_items)) item.player == self.player and item.name in SaveContext.giveable_items))
impa.place_locked_item(item_to_place) impa.place_locked_item(item_to_place)
self.world.itempool.remove(item_to_place) self.multiworld.itempool.remove(item_to_place)
# Give items to startinventory # Give items to startinventory
self.world.push_precollected(impa.item) self.multiworld.push_precollected(impa.item)
self.world.push_precollected(self.create_item("Zeldas Letter")) self.multiworld.push_precollected(self.create_item("Zeldas Letter"))
# Exclude locations in Ganon's Castle proportional to the number of items required to make the bridge # Exclude locations in Ganon's Castle proportional to the number of items required to make the bridge
# Check for dungeon ER later # Check for dungeon ER later
@ -789,8 +789,8 @@ class OOTWorld(World):
gc = next(filter(lambda dungeon: dungeon.name == 'Ganons Castle', self.dungeons)) gc = next(filter(lambda dungeon: dungeon.name == 'Ganons Castle', self.dungeons))
locations = [loc.name for region in gc.regions for loc in region.locations if loc.item is None] locations = [loc.name for region in gc.regions for loc in region.locations if loc.item is None]
junk_fill_locations = self.world.random.sample(locations, round(len(locations) * ganon_junk_fill)) junk_fill_locations = self.multiworld.random.sample(locations, round(len(locations) * ganon_junk_fill))
exclusion_rules(self.world, self.player, junk_fill_locations) exclusion_rules(self.multiworld, self.player, junk_fill_locations)
# Locations which are not sendable must be converted to events # Locations which are not sendable must be converted to events
# This includes all locations for which show_in_spoiler is false, and shuffled shop items. # This includes all locations for which show_in_spoiler is false, and shuffled shop items.
@ -813,12 +813,12 @@ class OOTWorld(World):
trap_location_ids = [loc.address for loc in self.get_locations() if loc.item.trap] trap_location_ids = [loc.address for loc in self.get_locations() if loc.item.trap]
self.trap_appearances = {} self.trap_appearances = {}
for loc_id in trap_location_ids: for loc_id in trap_location_ids:
self.trap_appearances[loc_id] = self.create_item(self.world.slot_seeds[self.player].choice(self.fake_items).name) self.trap_appearances[loc_id] = self.create_item(self.multiworld.slot_seeds[self.player].choice(self.fake_items).name)
# Seed hint RNG, used for ganon text lines also # Seed hint RNG, used for ganon text lines also
self.hint_rng = self.world.slot_seeds[self.player] self.hint_rng = self.multiworld.slot_seeds[self.player]
outfile_name = self.world.get_out_file_name_base(self.player) outfile_name = self.multiworld.get_out_file_name_base(self.player)
rom = Rom(file=get_options()['oot_options']['rom_file']) rom = Rom(file=get_options()['oot_options']['rom_file'])
if self.hints != 'none': if self.hints != 'none':
buildWorldGossipHints(self) buildWorldGossipHints(self)
@ -841,17 +841,17 @@ class OOTWorld(World):
else: else:
entrance = loadzone.reverse entrance = loadzone.reverse
if entrance.reverse is not None: if entrance.reverse is not None:
self.world.spoiler.set_entrance(entrance, entrance.replaces.reverse, 'both', self.player) self.multiworld.spoiler.set_entrance(entrance, entrance.replaces.reverse, 'both', self.player)
else: else:
self.world.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player) self.multiworld.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player)
else: else:
reverse = loadzone.replaces.reverse reverse = loadzone.replaces.reverse
if reverse in all_entrances: if reverse in all_entrances:
all_entrances.remove(reverse) all_entrances.remove(reverse)
self.world.spoiler.set_entrance(loadzone, reverse, 'both', self.player) self.multiworld.spoiler.set_entrance(loadzone, reverse, 'both', self.player)
else: else:
for entrance in all_entrances: for entrance in all_entrances:
self.world.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player) self.multiworld.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player)
# Gathers hint data for OoT. Loops over all world locations for woth, barren, and major item locations. # Gathers hint data for OoT. Loops over all world locations for woth, barren, and major item locations.
@classmethod @classmethod
@ -979,9 +979,9 @@ class OOTWorld(World):
# Helper functions # Helper functions
def get_shufflable_entrances(self, type=None, only_primary=False): def get_shufflable_entrances(self, type=None, only_primary=False):
return [entrance for entrance in self.world.get_entrances() if (entrance.player == self.player and return [entrance for entrance in self.multiworld.get_entrances() if (entrance.player == self.player and
(type == None or entrance.type == type) and (type == None or entrance.type == type) and
(not only_primary or entrance.primary))] (not only_primary or entrance.primary))]
def get_shuffled_entrances(self, type=None, only_primary=False): def get_shuffled_entrances(self, type=None, only_primary=False):
return [entrance for entrance in self.get_shufflable_entrances(type=type, only_primary=only_primary) if return [entrance for entrance in self.get_shufflable_entrances(type=type, only_primary=only_primary) if
@ -993,13 +993,13 @@ class OOTWorld(World):
yield loc yield loc
def get_location(self, location): def get_location(self, location):
return self.world.get_location(location, self.player) return self.multiworld.get_location(location, self.player)
def get_region(self, region): def get_region(self, region):
return self.world.get_region(region, self.player) return self.multiworld.get_region(region, self.player)
def get_entrance(self, entrance): def get_entrance(self, entrance):
return self.world.get_entrance(entrance, self.player) return self.multiworld.get_entrance(entrance, self.player)
def is_major_item(self, item: OOTItem): def is_major_item(self, item: OOTItem):
if item.type == 'Token': if item.type == 'Token':
@ -1030,7 +1030,7 @@ class OOTWorld(World):
# Specifically ensures that only real items are gotten, not any events. # Specifically ensures that only real items are gotten, not any events.
# In particular, ensures that Time Travel needs to be found. # In particular, ensures that Time Travel needs to be found.
def get_state_with_complete_itempool(self): def get_state_with_complete_itempool(self):
all_state = self.world.get_all_state(use_cache=False) all_state = self.multiworld.get_all_state(use_cache=False)
# Remove event progression items # Remove event progression items
for item, player in all_state.prog_items: for item, player in all_state.prog_items:
if player == self.player and (item not in item_table or (item_table[item][2] is None and item_table[item][0] != 'DungeonReward')): if player == self.player and (item not in item_table or (item_table[item][2] is None and item_table[item][0] != 'DungeonReward')):

View File

@ -6,9 +6,9 @@ from BaseClasses import Location
# TODO: implement Mapstone counting, Open, OpenWorld, connection rules # TODO: implement Mapstone counting, Open, OpenWorld, connection rules
def set_rules(world): def set_rules(world):
temp_base_rule(world.world, world.player) temp_base_rule(world.multiworld, world.player)
for logicset in world.logic_sets: for logicset in world.logic_sets:
apply_or_ruleset(world.world, world.player, logicset) apply_or_ruleset(world.multiworld, world.player, logicset)
def tautology(state): def tautology(state):

View File

@ -24,17 +24,17 @@ class OriBlindForest(World):
def generate_early(self): def generate_early(self):
logic_sets = {"casual-core"} logic_sets = {"casual-core"}
for logic_set in location_rules: for logic_set in location_rules:
if logic_set != "casual-core" and getattr(self.world, logic_set.replace("-", "_")): if logic_set != "casual-core" and getattr(self.multiworld, logic_set.replace("-", "_")):
logic_sets.add(logic_set) logic_sets.add(logic_set)
self.logic_sets = logic_sets self.logic_sets = logic_sets
set_rules = set_rules set_rules = set_rules
def create_region(self, name: str): def create_region(self, name: str):
return Region(name, RegionType.Generic, name, self.player, self.world) return Region(name, RegionType.Generic, name, self.player, self.multiworld)
def create_regions(self): def create_regions(self):
world = self.world world = self.multiworld
menu = self.create_region("Menu") menu = self.create_region("Menu")
world.regions.append(menu) world.regions.append(menu)
start = Entrance(self.player, "Start Game", menu) start = Entrance(self.player, "Start Game", menu)
@ -62,7 +62,7 @@ class OriBlindForest(World):
def generate_basic(self): def generate_basic(self):
for item_name, count in default_pool.items(): for item_name, count in default_pool.items():
self.world.itempool.extend([self.create_item(item_name) for _ in range(count)]) self.multiworld.itempool.extend([self.create_item(item_name) for _ in range(count)])
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
return Item(name, return Item(name,

View File

@ -159,7 +159,7 @@ class Overcooked2Level:
sublevel: int sublevel: int
def __init__(self): def __init__(self):
self.world = Overcooked2GameWorld.ONE self.multiworld = Overcooked2GameWorld.ONE
self.sublevel = 0 self.sublevel = 0
def __iter__(self): def __iter__(self):
@ -167,21 +167,21 @@ class Overcooked2Level:
def __next__(self): def __next__(self):
self.sublevel += 1 self.sublevel += 1
if self.sublevel > self.world.sublevel_count: if self.sublevel > self.multiworld.sublevel_count:
if self.world == Overcooked2GameWorld.KEVIN: if self.multiworld == Overcooked2GameWorld.KEVIN:
raise StopIteration raise StopIteration
self.world = Overcooked2GameWorld(self.world.value + 1) self.multiworld = Overcooked2GameWorld(self.multiworld.value + 1)
self.sublevel = 1 self.sublevel = 1
return self return self
@property @property
def level_id(self) -> int: def level_id(self) -> int:
return self.world.base_id + (self.sublevel - 1) return self.multiworld.base_id + (self.sublevel - 1)
@property @property
def level_name(self) -> str: def level_name(self) -> str:
return self.world.as_str + "-" + str(self.sublevel) return self.multiworld.as_str + "-" + str(self.sublevel)
@property @property
def location_name_item(self) -> str: def location_name_item(self) -> str:

View File

@ -79,7 +79,7 @@ class Overcooked2World(World):
def place_event(self, location_name: str, item_name: str, def place_event(self, location_name: str, item_name: str,
classification: ItemClassification = ItemClassification.progression_skip_balancing): classification: ItemClassification = ItemClassification.progression_skip_balancing):
location: Location = self.world.get_location(location_name, self.player) location: Location = self.multiworld.get_location(location_name, self.player)
location.place_locked_item(self.create_event(item_name, classification)) location.place_locked_item(self.create_event(item_name, classification))
def add_region(self, region_name: str): def add_region(self, region_name: str):
@ -88,13 +88,13 @@ class Overcooked2World(World):
RegionType.Generic, RegionType.Generic,
region_name, region_name,
self.player, self.player,
self.world, self.multiworld,
) )
self.world.regions.append(region) self.multiworld.regions.append(region)
def connect_regions(self, source: str, target: str, rule: Optional[Callable[[CollectionState], bool]] = None): def connect_regions(self, source: str, target: str, rule: Optional[Callable[[CollectionState], bool]] = None):
sourceRegion = self.world.get_region(source, self.player) sourceRegion = self.multiworld.get_region(source, self.player)
targetRegion = self.world.get_region(target, self.player) targetRegion = self.multiworld.get_region(target, self.player)
connection = Entrance(self.player, '', sourceRegion) connection = Entrance(self.player, '', sourceRegion)
if rule: if rule:
@ -117,7 +117,7 @@ class Overcooked2World(World):
else: else:
location_id = level_id location_id = level_id
region = self.world.get_region(region_name, self.player) region = self.multiworld.get_region(region_name, self.player)
location = Overcooked2Location( location = Overcooked2Location(
self.player, self.player,
location_name, location_name,
@ -145,8 +145,8 @@ class Overcooked2World(World):
) )
def get_options(self) -> Dict[str, Any]: def get_options(self) -> Dict[str, Any]:
return OC2Options({option.__name__: getattr(self.world, name)[self.player].result return OC2Options({option.__name__: getattr(self.multiworld, name)[self.player].result
if issubclass(option, OC2OnToggle) else getattr(self.world, name)[self.player].value if issubclass(option, OC2OnToggle) else getattr(self.multiworld, name)[self.player].value
for name, option in overcooked_options.items()}) for name, option in overcooked_options.items()})
# Helper Data # Helper Data
@ -170,7 +170,7 @@ class Overcooked2World(World):
if self.options["ShuffleLevelOrder"]: if self.options["ShuffleLevelOrder"]:
self.level_mapping = \ self.level_mapping = \
level_shuffle_factory( level_shuffle_factory(
self.world.random, self.multiworld.random,
self.options["PrepLevels"] != PrepLevelMode.excluded.value, self.options["PrepLevels"] != PrepLevelMode.excluded.value,
self.options["IncludeHordeLevels"], self.options["IncludeHordeLevels"],
) )
@ -246,7 +246,7 @@ class Overcooked2World(World):
completion_condition: Callable[[CollectionState], bool] = lambda state: \ completion_condition: Callable[[CollectionState], bool] = lambda state: \
state.has("Victory", self.player) state.has("Victory", self.player)
self.world.completion_condition[self.player] = completion_condition self.multiworld.completion_condition[self.player] = completion_condition
def create_items(self): def create_items(self):
self.itempool = [] self.itempool = []
@ -298,7 +298,7 @@ class Overcooked2World(World):
while len(self.itempool) < pool_count: while len(self.itempool) < pool_count:
self.itempool.append(self.create_item("Bonus Star", ItemClassification.useful)) self.itempool.append(self.create_item("Bonus Star", ItemClassification.useful))
self.world.itempool += self.itempool self.multiworld.itempool += self.itempool
def set_rules(self): def set_rules(self):
pass pass
@ -324,7 +324,7 @@ class Overcooked2World(World):
# Items get distributed to locations # Items get distributed to locations
def fill_json_data(self) -> Dict[str, Any]: def fill_json_data(self) -> Dict[str, Any]:
mod_name = f"AP-{self.world.seed_name}-P{self.player}-{self.world.player_name[self.player]}" mod_name = f"AP-{self.multiworld.seed_name}-P{self.player}-{self.multiworld.player_name[self.player]}"
# Serialize Level Order # Serialize Level Order
story_level_order = dict() story_level_order = dict()
@ -363,7 +363,7 @@ class Overcooked2World(World):
# Set Kevin Unlock Requirements # Set Kevin Unlock Requirements
if self.options["KevinLevels"]: if self.options["KevinLevels"]:
def kevin_level_to_keyholder_level_id(level_id: int) -> Optional[int]: def kevin_level_to_keyholder_level_id(level_id: int) -> Optional[int]:
location = self.world.find_item(f"Kevin-{level_id-36}", self.player) location = self.multiworld.find_item(f"Kevin-{level_id-36}", self.player)
if location.player != self.player: if location.player != self.player:
return None # This kevin level will be unlocked by the server at runtime return None # This kevin level will be unlocked by the server at runtime
level_id = oc2_location_name_to_id[location.name] level_id = oc2_location_name_to_id[location.name]
@ -376,7 +376,7 @@ class Overcooked2World(World):
# Place Items at Level Completion Screens (local only) # Place Items at Level Completion Screens (local only)
on_level_completed: Dict[str, list[Dict[str, str]]] = dict() on_level_completed: Dict[str, list[Dict[str, str]]] = dict()
regions = self.world.get_regions(self.player) regions = self.multiworld.get_regions(self.player)
for region in regions: for region in regions:
for location in region.locations: for location in region.locations:
if location.item is None: if location.item is None:

View File

@ -73,22 +73,22 @@ class PokemonRedBlueWorld(World):
def encode_name(name, t): def encode_name(name, t):
try: try:
if len(encode_text(name)) > 7: if len(encode_text(name)) > 7:
raise IndexError(f"{t} name too long for player {self.world.player_name[self.player]}. Must be 7 characters or fewer.") raise IndexError(f"{t} name too long for player {self.multiworld.player_name[self.player]}. Must be 7 characters or fewer.")
return encode_text(name, length=8, whitespace="@", safety=True) return encode_text(name, length=8, whitespace="@", safety=True)
except KeyError as e: except KeyError as e:
raise KeyError(f"Invalid character(s) in {t} name for player {self.world.player_name[self.player]}") from e raise KeyError(f"Invalid character(s) in {t} name for player {self.multiworld.player_name[self.player]}") from e
self.trainer_name = encode_name(self.world.trainer_name[self.player].value, "Player") self.trainer_name = encode_name(self.multiworld.trainer_name[self.player].value, "Player")
self.rival_name = encode_name(self.world.rival_name[self.player].value, "Rival") self.rival_name = encode_name(self.multiworld.rival_name[self.player].value, "Rival")
if self.world.badges_needed_for_hm_moves[self.player].value >= 2: if self.multiworld.badges_needed_for_hm_moves[self.player].value >= 2:
badges_to_add = ["Marsh Badge", "Volcano Badge", "Earth Badge"] badges_to_add = ["Marsh Badge", "Volcano Badge", "Earth Badge"]
if self.world.badges_needed_for_hm_moves[self.player].value == 3: if self.multiworld.badges_needed_for_hm_moves[self.player].value == 3:
badges = ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge", badges = ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge",
"Soul Badge", "Volcano Badge", "Earth Badge"] "Soul Badge", "Volcano Badge", "Earth Badge"]
self.world.random.shuffle(badges) self.multiworld.random.shuffle(badges)
badges_to_add += [badges.pop(), badges.pop()] badges_to_add += [badges.pop(), badges.pop()]
hm_moves = ["Cut", "Fly", "Surf", "Strength", "Flash"] hm_moves = ["Cut", "Fly", "Surf", "Strength", "Flash"]
self.world.random.shuffle(hm_moves) self.multiworld.random.shuffle(hm_moves)
self.extra_badges = {} self.extra_badges = {}
for badge in badges_to_add: for badge in badges_to_add:
self.extra_badges[hm_moves.pop()] = badge self.extra_badges[hm_moves.pop()] = badge
@ -99,21 +99,21 @@ class PokemonRedBlueWorld(World):
locations = [location for location in location_data if location.type == "Item"] locations = [location for location in location_data if location.type == "Item"]
item_pool = [] item_pool = []
for location in locations: for location in locations:
if "Hidden" in location.name and not self.world.randomize_hidden_items[self.player].value: if "Hidden" in location.name and not self.multiworld.randomize_hidden_items[self.player].value:
continue continue
if "Rock Tunnel B1F" in location.region and not self.world.extra_key_items[self.player].value: if "Rock Tunnel B1F" in location.region and not self.multiworld.extra_key_items[self.player].value:
continue continue
if location.name == "Celadon City - Mansion Lady" and not self.world.tea[self.player].value: if location.name == "Celadon City - Mansion Lady" and not self.multiworld.tea[self.player].value:
continue continue
item = self.create_item(location.original_item) item = self.create_item(location.original_item)
if location.event: if location.event:
self.world.get_location(location.name, self.player).place_locked_item(item) self.multiworld.get_location(location.name, self.player).place_locked_item(item)
elif ("Badge" not in item.name or self.world.badgesanity[self.player].value) and \ elif ("Badge" not in item.name or self.multiworld.badgesanity[self.player].value) and \
(item.name != "Oak's Parcel" or self.world.old_man[self.player].value != 1): (item.name != "Oak's Parcel" or self.multiworld.old_man[self.player].value != 1):
item_pool.append(item) item_pool.append(item)
self.world.random.shuffle(item_pool) self.multiworld.random.shuffle(item_pool)
self.world.itempool += item_pool self.multiworld.itempool += item_pool
def pre_fill(self): def pre_fill(self):
@ -121,17 +121,17 @@ class PokemonRedBlueWorld(World):
process_wild_pokemon(self) process_wild_pokemon(self)
process_static_pokemon(self) process_static_pokemon(self)
if self.world.old_man[self.player].value == 1: if self.multiworld.old_man[self.player].value == 1:
item = self.create_item("Oak's Parcel") item = self.create_item("Oak's Parcel")
locations = [] locations = []
for location in self.world.get_locations(): for location in self.multiworld.get_locations():
if location.player == self.player and location.item is None and location.can_reach(self.world.state) \ if location.player == self.player and location.item is None and location.can_reach(self.multiworld.state) \
and location.item_rule(item): and location.item_rule(item):
locations.append(location) locations.append(location)
self.world.random.choice(locations).place_locked_item(item) self.multiworld.random.choice(locations).place_locked_item(item)
if not self.world.badgesanity[self.player].value: if not self.multiworld.badgesanity[self.player].value:
self.world.non_local_items[self.player].value -= self.item_name_groups["Badges"] self.multiworld.non_local_items[self.player].value -= self.item_name_groups["Badges"]
for i in range(5): for i in range(5):
try: try:
badges = [] badges = []
@ -142,11 +142,11 @@ class PokemonRedBlueWorld(World):
for loc in ["Pewter Gym - Brock 1", "Cerulean Gym - Misty 1", "Vermilion Gym - Lt. Surge 1", for loc in ["Pewter Gym - Brock 1", "Cerulean Gym - Misty 1", "Vermilion Gym - Lt. Surge 1",
"Celadon Gym - Erika 1", "Fuchsia Gym - Koga 1", "Saffron Gym - Sabrina 1", "Celadon Gym - Erika 1", "Fuchsia Gym - Koga 1", "Saffron Gym - Sabrina 1",
"Cinnabar Gym - Blaine 1", "Viridian Gym - Giovanni 1"]: "Cinnabar Gym - Blaine 1", "Viridian Gym - Giovanni 1"]:
badgelocs.append(self.world.get_location(loc, self.player)) badgelocs.append(self.multiworld.get_location(loc, self.player))
state = self.world.get_all_state(False) state = self.multiworld.get_all_state(False)
self.world.random.shuffle(badges) self.multiworld.random.shuffle(badges)
self.world.random.shuffle(badgelocs) self.multiworld.random.shuffle(badgelocs)
fill_restrictive(self.world, state, badgelocs.copy(), badges, True, True) fill_restrictive(self.multiworld, state, badgelocs.copy(), badges, True, True)
except FillError: except FillError:
for location in badgelocs: for location in badgelocs:
location.item = None location.item = None
@ -155,36 +155,36 @@ class PokemonRedBlueWorld(World):
else: else:
raise FillError(f"Failed to place badges for player {self.player}") raise FillError(f"Failed to place badges for player {self.player}")
locs = [self.world.get_location("Fossil - Choice A", self.player), locs = [self.multiworld.get_location("Fossil - Choice A", self.player),
self.world.get_location("Fossil - Choice B", self.player)] self.multiworld.get_location("Fossil - Choice B", self.player)]
for loc in locs: for loc in locs:
add_item_rule(loc, lambda i: i.advancement or i.name in self.item_name_groups["Unique"] add_item_rule(loc, lambda i: i.advancement or i.name in self.item_name_groups["Unique"]
or i.name == "Master Ball") or i.name == "Master Ball")
loc = self.world.get_location("Pallet Town - Player's PC", self.player) loc = self.multiworld.get_location("Pallet Town - Player's PC", self.player)
if loc.item is None: if loc.item is None:
locs.append(loc) locs.append(loc)
for loc in locs: for loc in locs:
unplaced_items = [] unplaced_items = []
if loc.name in self.world.priority_locations[self.player].value: if loc.name in self.multiworld.priority_locations[self.player].value:
add_item_rule(loc, lambda i: i.advancement) add_item_rule(loc, lambda i: i.advancement)
for item in self.world.itempool: for item in self.multiworld.itempool:
if item.player == self.player and loc.item_rule(item): if item.player == self.player and loc.item_rule(item):
self.world.itempool.remove(item) self.multiworld.itempool.remove(item)
state = sweep_from_pool(self.world.state, self.world.itempool + unplaced_items) state = sweep_from_pool(self.multiworld.state, self.multiworld.itempool + unplaced_items)
if state.can_reach(loc, "Location", self.player): if state.can_reach(loc, "Location", self.player):
loc.place_locked_item(item) loc.place_locked_item(item)
break break
else: else:
unplaced_items.append(item) unplaced_items.append(item)
self.world.itempool += unplaced_items self.multiworld.itempool += unplaced_items
intervene = False intervene = False
test_state = self.world.get_all_state(False) test_state = self.multiworld.get_all_state(False)
if not test_state.pokemon_rb_can_surf(self.player) or not test_state.pokemon_rb_can_strength(self.player): if not test_state.pokemon_rb_can_surf(self.player) or not test_state.pokemon_rb_can_strength(self.player):
intervene = True intervene = True
elif self.world.accessibility[self.player].current_key != "minimal": elif self.multiworld.accessibility[self.player].current_key != "minimal":
if not test_state.pokemon_rb_can_cut(self.player) or not test_state.pokemon_rb_can_flash(self.player): if not test_state.pokemon_rb_can_cut(self.player) or not test_state.pokemon_rb_can_flash(self.player):
intervene = True intervene = True
if intervene: if intervene:
@ -192,12 +192,12 @@ class PokemonRedBlueWorld(World):
# let you choose the exact weights for HM compatibility # let you choose the exact weights for HM compatibility
logging.warning( logging.warning(
f"HM-compatible Pokémon possibly missing, placing Mew on Route 1 for player {self.player}") f"HM-compatible Pokémon possibly missing, placing Mew on Route 1 for player {self.player}")
loc = self.world.get_location("Route 1 - Wild Pokemon - 1", self.player) loc = self.multiworld.get_location("Route 1 - Wild Pokemon - 1", self.player)
loc.item = self.create_item("Mew") loc.item = self.create_item("Mew")
def create_regions(self): def create_regions(self):
if self.world.free_fly_location[self.player].value: if self.multiworld.free_fly_location[self.player].value:
fly_map_code = self.world.random.randint(5, 9) fly_map_code = self.multiworld.random.randint(5, 9)
if fly_map_code == 9: if fly_map_code == 9:
fly_map_code = 10 fly_map_code = 10
if fly_map_code == 5: if fly_map_code == 5:
@ -208,11 +208,11 @@ class PokemonRedBlueWorld(World):
"Vermilion City", "Celadon City", "Fuchsia City", "Cinnabar Island", "Indigo Plateau", "Vermilion City", "Celadon City", "Fuchsia City", "Cinnabar Island", "Indigo Plateau",
"Saffron City"][fly_map_code] "Saffron City"][fly_map_code]
self.fly_map_code = fly_map_code self.fly_map_code = fly_map_code
create_regions(self.world, self.player) create_regions(self.multiworld, self.player)
self.world.completion_condition[self.player] = lambda state, player=self.player: state.has("Become Champion", player=player) self.multiworld.completion_condition[self.player] = lambda state, player=self.player: state.has("Become Champion", player=player)
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
return PokemonRBItem(name, self.player) return PokemonRBItem(name, self.player)
@ -221,21 +221,21 @@ class PokemonRedBlueWorld(World):
generate_output(self, output_directory) generate_output(self, output_directory)
def write_spoiler_header(self, spoiler_handle: TextIO): def write_spoiler_header(self, spoiler_handle: TextIO):
if self.world.free_fly_location[self.player].value: if self.multiworld.free_fly_location[self.player].value:
spoiler_handle.write('Fly unlocks: %s\n' % self.fly_map) spoiler_handle.write('Fly unlocks: %s\n' % self.fly_map)
if self.extra_badges: if self.extra_badges:
for hm_move, badge in self.extra_badges.items(): for hm_move, badge in self.extra_badges.items():
spoiler_handle.write(hm_move + " enabled by: " + (" " * 20)[:20 - len(hm_move)] + badge + "\n") spoiler_handle.write(hm_move + " enabled by: " + (" " * 20)[:20 - len(hm_move)] + badge + "\n")
def write_spoiler(self, spoiler_handle): def write_spoiler(self, spoiler_handle):
if self.world.randomize_type_matchup_types[self.player].value or \ if self.multiworld.randomize_type_matchup_types[self.player].value or \
self.world.randomize_type_matchup_type_effectiveness[self.player].value: self.multiworld.randomize_type_matchup_type_effectiveness[self.player].value:
spoiler_handle.write(f"\n\nType matchups ({self.world.player_name[self.player]}):\n\n") spoiler_handle.write(f"\n\nType matchups ({self.multiworld.player_name[self.player]}):\n\n")
for matchup in self.type_chart: for matchup in self.type_chart:
spoiler_handle.write(f"{matchup[0]} deals {matchup[2] * 10}% damage to {matchup[1]}\n") spoiler_handle.write(f"{matchup[0]} deals {matchup[2] * 10}% damage to {matchup[1]}\n")
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return self.world.random.choice([item for item in item_table if item_table[item].classification in return self.multiworld.random.choice([item for item in item_table if item_table[item].classification in
[ItemClassification.filler, ItemClassification.trap]]) [ItemClassification.filler, ItemClassification.trap]])

View File

@ -6,39 +6,39 @@ class PokemonLogic(LogicMixin):
def pokemon_rb_can_surf(self, player): def pokemon_rb_can_surf(self, player):
return (((self.has("HM03 Surf", player) and self.can_learn_hm("10000", player)) return (((self.has("HM03 Surf", player) and self.can_learn_hm("10000", player))
or self.has("Flippers", player)) and (self.has("Soul Badge", player) or or self.has("Flippers", player)) and (self.has("Soul Badge", player) or
self.has(self.world.worlds[player].extra_badges.get("Surf"), player) self.has(self.multiworld.worlds[player].extra_badges.get("Surf"), player)
or self.world.badges_needed_for_hm_moves[player].value == 0)) or self.multiworld.badges_needed_for_hm_moves[player].value == 0))
def pokemon_rb_can_cut(self, player): def pokemon_rb_can_cut(self, player):
return ((self.has("HM01 Cut", player) and self.can_learn_hm("100", player) or self.has("Master Sword", player)) return ((self.has("HM01 Cut", player) and self.can_learn_hm("100", player) or self.has("Master Sword", player))
and (self.has("Cascade Badge", player) or and (self.has("Cascade Badge", player) or
self.has(self.world.worlds[player].extra_badges.get("Cut"), player) or self.has(self.multiworld.worlds[player].extra_badges.get("Cut"), player) or
self.world.badges_needed_for_hm_moves[player].value == 0)) self.multiworld.badges_needed_for_hm_moves[player].value == 0))
def pokemon_rb_can_fly(self, player): def pokemon_rb_can_fly(self, player):
return (((self.has("HM02 Fly", player) and self.can_learn_hm("1000", player)) or self.has("Flute", player)) and return (((self.has("HM02 Fly", player) and self.can_learn_hm("1000", player)) or self.has("Flute", player)) and
(self.has("Thunder Badge", player) or self.has(self.world.worlds[player].extra_badges.get("Fly"), player) (self.has("Thunder Badge", player) or self.has(self.multiworld.worlds[player].extra_badges.get("Fly"), player)
or self.world.badges_needed_for_hm_moves[player].value == 0)) or self.multiworld.badges_needed_for_hm_moves[player].value == 0))
def pokemon_rb_can_strength(self, player): def pokemon_rb_can_strength(self, player):
return ((self.has("HM04 Strength", player) and self.can_learn_hm("100000", player)) or return ((self.has("HM04 Strength", player) and self.can_learn_hm("100000", player)) or
self.has("Titan's Mitt", player)) and (self.has("Rainbow Badge", player) or self.has("Titan's Mitt", player)) and (self.has("Rainbow Badge", player) or
self.has(self.world.worlds[player].extra_badges.get("Strength"), player) self.has(self.multiworld.worlds[player].extra_badges.get("Strength"), player)
or self.world.badges_needed_for_hm_moves[player].value == 0) or self.multiworld.badges_needed_for_hm_moves[player].value == 0)
def pokemon_rb_can_flash(self, player): def pokemon_rb_can_flash(self, player):
return (((self.has("HM05 Flash", player) and self.can_learn_hm("1000000", player)) or self.has("Lamp", player)) return (((self.has("HM05 Flash", player) and self.can_learn_hm("1000000", player)) or self.has("Lamp", player))
and (self.has("Boulder Badge", player) or self.has(self.world.worlds[player].extra_badges.get("Flash"), and (self.has("Boulder Badge", player) or self.has(self.multiworld.worlds[player].extra_badges.get("Flash"),
player) or self.world.badges_needed_for_hm_moves[player].value == 0)) player) or self.multiworld.badges_needed_for_hm_moves[player].value == 0))
def can_learn_hm(self, move, player): def can_learn_hm(self, move, player):
for pokemon, data in self.world.worlds[player].local_poke_data.items(): for pokemon, data in self.multiworld.worlds[player].local_poke_data.items():
if self.has(pokemon, player) and data["tms"][6] & int(move, 2): if self.has(pokemon, player) and data["tms"][6] & int(move, 2):
return True return True
return False return False
def pokemon_rb_can_get_hidden_items(self, player): def pokemon_rb_can_get_hidden_items(self, player):
return self.has("Item Finder", player) or not self.world.require_item_finder[player].value return self.has("Item Finder", player) or not self.multiworld.require_item_finder[player].value
def pokemon_rb_cerulean_cave(self, count, player): def pokemon_rb_cerulean_cave(self, count, player):
return len([item for item in return len([item for item in
@ -49,7 +49,7 @@ class PokemonLogic(LogicMixin):
"HM04 Strength", "HM05 Flash"] if self.has(item, player)]) >= count "HM04 Strength", "HM05 Flash"] if self.has(item, player)]) >= count
def pokemon_rb_can_pass_guards(self, player): def pokemon_rb_can_pass_guards(self, player):
if self.world.tea[player].value: if self.multiworld.tea[player].value:
return self.has("Tea", player) return self.has("Tea", player)
else: else:
# this could just be "True", but you never know what weird options I might introduce later ;) # this could just be "True", but you never know what weird options I might introduce later ;)

View File

@ -150,15 +150,15 @@ def create_regions(world: MultiWorld, player: int):
connect(world, player, "Menu", "Anywhere", one_way=True) connect(world, player, "Menu", "Anywhere", one_way=True)
connect(world, player, "Menu", "Pallet Town", one_way=True) connect(world, player, "Menu", "Pallet Town", one_way=True)
connect(world, player, "Menu", "Fossil", lambda state: state.pokemon_rb_fossil_checks( connect(world, player, "Menu", "Fossil", lambda state: state.pokemon_rb_fossil_checks(
state.world.second_fossil_check_condition[player].value, player), one_way=True) state.multiworld.second_fossil_check_condition[player].value, player), one_way=True)
connect(world, player, "Pallet Town", "Route 1") connect(world, player, "Pallet Town", "Route 1")
connect(world, player, "Route 1", "Viridian City") connect(world, player, "Route 1", "Viridian City")
connect(world, player, "Viridian City", "Route 22") connect(world, player, "Viridian City", "Route 22")
connect(world, player, "Route 22", "Route 23 South", connect(world, player, "Route 22", "Route 23 South",
lambda state: state.pokemon_rb_has_badges(state.world.victory_road_condition[player].value, player)) lambda state: state.pokemon_rb_has_badges(state.multiworld.victory_road_condition[player].value, player))
connect(world, player, "Route 23 South", "Route 23 North", lambda state: state.pokemon_rb_can_surf(player)) connect(world, player, "Route 23 South", "Route 23 North", lambda state: state.pokemon_rb_can_surf(player))
connect(world, player, "Viridian City North", "Viridian Gym", lambda state: connect(world, player, "Viridian City North", "Viridian Gym", lambda state:
state.pokemon_rb_has_badges(state.world.viridian_gym_condition[player].value, player), one_way=True) state.pokemon_rb_has_badges(state.multiworld.viridian_gym_condition[player].value, player), one_way=True)
connect(world, player, "Route 2", "Route 2 East", lambda state: state.pokemon_rb_can_cut(player)) connect(world, player, "Route 2", "Route 2 East", lambda state: state.pokemon_rb_can_cut(player))
connect(world, player, "Route 2 East", "Diglett's Cave", lambda state: state.pokemon_rb_can_cut(player)) connect(world, player, "Route 2 East", "Diglett's Cave", lambda state: state.pokemon_rb_can_cut(player))
connect(world, player, "Route 2", "Viridian City North") connect(world, player, "Route 2", "Viridian City North")
@ -178,7 +178,7 @@ def create_regions(world: MultiWorld, player: int):
connect(world, player, "Route 9", "Route 10 North") connect(world, player, "Route 9", "Route 10 North")
connect(world, player, "Route 10 North", "Rock Tunnel 1F", lambda state: state.pokemon_rb_can_flash(player)) connect(world, player, "Route 10 North", "Rock Tunnel 1F", lambda state: state.pokemon_rb_can_flash(player))
connect(world, player, "Route 10 North", "Power Plant", lambda state: state.pokemon_rb_can_surf(player) and connect(world, player, "Route 10 North", "Power Plant", lambda state: state.pokemon_rb_can_surf(player) and
(state.has("Plant Key", player) or not state.world.extra_key_items[player].value), one_way=True) (state.has("Plant Key", player) or not state.multiworld.extra_key_items[player].value), one_way=True)
connect(world, player, "Rock Tunnel 1F", "Route 10 South", lambda state: state.pokemon_rb_can_flash(player)) connect(world, player, "Rock Tunnel 1F", "Route 10 South", lambda state: state.pokemon_rb_can_flash(player))
connect(world, player, "Rock Tunnel 1F", "Rock Tunnel B1F") connect(world, player, "Rock Tunnel 1F", "Rock Tunnel B1F")
connect(world, player, "Lavender Town", "Pokemon Tower 1F", one_way=True) connect(world, player, "Lavender Town", "Pokemon Tower 1F", one_way=True)
@ -205,7 +205,7 @@ def create_regions(world: MultiWorld, player: int):
connect(world, player, "S.S. Anne 1F", "S.S. Anne B1F", one_way=True) connect(world, player, "S.S. Anne 1F", "S.S. Anne B1F", one_way=True)
connect(world, player, "Vermilion City", "Route 11") connect(world, player, "Vermilion City", "Route 11")
connect(world, player, "Vermilion City", "Diglett's Cave") connect(world, player, "Vermilion City", "Diglett's Cave")
connect(world, player, "Route 12 West", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player) or not state.world.extra_strength_boulders[player].value) connect(world, player, "Route 12 West", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player) or not state.multiworld.extra_strength_boulders[player].value)
connect(world, player, "Route 12 North", "Route 12 South", lambda state: state.has("Poke Flute", player) or state.pokemon_rb_can_surf( player)) connect(world, player, "Route 12 North", "Route 12 South", lambda state: state.has("Poke Flute", player) or state.pokemon_rb_can_surf( player))
connect(world, player, "Route 12 West", "Route 12 North", lambda state: state.has("Poke Flute", player)) connect(world, player, "Route 12 West", "Route 12 North", lambda state: state.has("Poke Flute", player))
connect(world, player, "Route 12 West", "Route 12 South", lambda state: state.has("Poke Flute", player)) connect(world, player, "Route 12 West", "Route 12 South", lambda state: state.has("Poke Flute", player))
@ -227,25 +227,25 @@ def create_regions(world: MultiWorld, player: int):
connect(world, player, "Fuchsia City", "Fuchsia Gym", one_way=True) connect(world, player, "Fuchsia City", "Fuchsia Gym", one_way=True)
connect(world, player, "Fuchsia City", "Route 18") connect(world, player, "Fuchsia City", "Route 18")
connect(world, player, "Fuchsia City", "Safari Zone Gate", one_way=True) connect(world, player, "Fuchsia City", "Safari Zone Gate", one_way=True)
connect(world, player, "Safari Zone Gate", "Safari Zone Center", lambda state: state.has("Safari Pass", player) or not state.world.extra_key_items[player].value, one_way=True) connect(world, player, "Safari Zone Gate", "Safari Zone Center", lambda state: state.has("Safari Pass", player) or not state.multiworld.extra_key_items[player].value, one_way=True)
connect(world, player, "Safari Zone Center", "Safari Zone East", one_way=True) connect(world, player, "Safari Zone Center", "Safari Zone East", one_way=True)
connect(world, player, "Safari Zone Center", "Safari Zone West", one_way=True) connect(world, player, "Safari Zone Center", "Safari Zone West", one_way=True)
connect(world, player, "Safari Zone Center", "Safari Zone North", one_way=True) connect(world, player, "Safari Zone Center", "Safari Zone North", one_way=True)
connect(world, player, "Fuchsia City", "Route 15") connect(world, player, "Fuchsia City", "Route 15")
connect(world, player, "Route 15", "Route 14") connect(world, player, "Route 15", "Route 14")
connect(world, player, "Route 14", "Route 13") connect(world, player, "Route 14", "Route 13")
connect(world, player, "Route 13", "Route 12 South", lambda state: state.pokemon_rb_can_strength(player) or state.pokemon_rb_can_surf(player) or not state.world.extra_strength_boulders[player].value) connect(world, player, "Route 13", "Route 12 South", lambda state: state.pokemon_rb_can_strength(player) or state.pokemon_rb_can_surf(player) or not state.multiworld.extra_strength_boulders[player].value)
connect(world, player, "Fuchsia City", "Route 19", lambda state: state.pokemon_rb_can_surf(player)) connect(world, player, "Fuchsia City", "Route 19", lambda state: state.pokemon_rb_can_surf(player))
connect(world, player, "Route 20 East", "Route 19") connect(world, player, "Route 20 East", "Route 19")
connect(world, player, "Route 20 West", "Cinnabar Island", lambda state: state.pokemon_rb_can_surf(player)) connect(world, player, "Route 20 West", "Cinnabar Island", lambda state: state.pokemon_rb_can_surf(player))
connect(world, player, "Route 20 West", "Seafoam Islands 1F") connect(world, player, "Route 20 West", "Seafoam Islands 1F")
connect(world, player, "Route 20 East", "Seafoam Islands 1F", one_way=True) connect(world, player, "Route 20 East", "Seafoam Islands 1F", one_way=True)
connect(world, player, "Seafoam Islands 1F", "Route 20 East", lambda state: state.pokemon_rb_can_strength(player), one_way=True) connect(world, player, "Seafoam Islands 1F", "Route 20 East", lambda state: state.pokemon_rb_can_strength(player), one_way=True)
connect(world, player, "Viridian City", "Viridian City North", lambda state: state.has("Oak's Parcel", player) or state.world.old_man[player].value == 2 or state.pokemon_rb_can_cut(player)) connect(world, player, "Viridian City", "Viridian City North", lambda state: state.has("Oak's Parcel", player) or state.multiworld.old_man[player].value == 2 or state.pokemon_rb_can_cut(player))
connect(world, player, "Route 3", "Mt Moon 1F", one_way=True) connect(world, player, "Route 3", "Mt Moon 1F", one_way=True)
connect(world, player, "Route 11", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player)) connect(world, player, "Route 11", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player))
connect(world, player, "Cinnabar Island", "Cinnabar Gym", lambda state: state.has("Secret Key", player), one_way=True) connect(world, player, "Cinnabar Island", "Cinnabar Gym", lambda state: state.has("Secret Key", player), one_way=True)
connect(world, player, "Cinnabar Island", "Pokemon Mansion 1F", lambda state: state.has("Mansion Key", player) or not state.world.extra_key_items[player].value, one_way=True) connect(world, player, "Cinnabar Island", "Pokemon Mansion 1F", lambda state: state.has("Mansion Key", player) or not state.multiworld.extra_key_items[player].value, one_way=True)
connect(world, player, "Seafoam Islands 1F", "Seafoam Islands B1F", one_way=True) connect(world, player, "Seafoam Islands 1F", "Seafoam Islands B1F", one_way=True)
connect(world, player, "Seafoam Islands B1F", "Seafoam Islands B2F", one_way=True) connect(world, player, "Seafoam Islands B1F", "Seafoam Islands B2F", one_way=True)
connect(world, player, "Seafoam Islands B2F", "Seafoam Islands B3F", one_way=True) connect(world, player, "Seafoam Islands B2F", "Seafoam Islands B3F", one_way=True)
@ -263,7 +263,7 @@ def create_regions(world: MultiWorld, player: int):
connect(world, player, "Silph Co 8F", "Silph Co 9F", one_way=True) connect(world, player, "Silph Co 8F", "Silph Co 9F", one_way=True)
connect(world, player, "Silph Co 9F", "Silph Co 10F", one_way=True) connect(world, player, "Silph Co 9F", "Silph Co 10F", one_way=True)
connect(world, player, "Silph Co 10F", "Silph Co 11F", one_way=True) connect(world, player, "Silph Co 10F", "Silph Co 11F", one_way=True)
connect(world, player, "Celadon City", "Rocket Hideout B1F", lambda state: state.has("Hideout Key", player) or not state.world.extra_key_items[player].value, one_way=True) connect(world, player, "Celadon City", "Rocket Hideout B1F", lambda state: state.has("Hideout Key", player) or not state.multiworld.extra_key_items[player].value, one_way=True)
connect(world, player, "Rocket Hideout B1F", "Rocket Hideout B2F", one_way=True) connect(world, player, "Rocket Hideout B1F", "Rocket Hideout B2F", one_way=True)
connect(world, player, "Rocket Hideout B2F", "Rocket Hideout B3F", one_way=True) connect(world, player, "Rocket Hideout B2F", "Rocket Hideout B3F", one_way=True)
connect(world, player, "Rocket Hideout B3F", "Rocket Hideout B4F", one_way=True) connect(world, player, "Rocket Hideout B3F", "Rocket Hideout B4F", one_way=True)
@ -273,9 +273,9 @@ def create_regions(world: MultiWorld, player: int):
connect(world, player, "Route 23 North", "Victory Road 1F", lambda state: state.pokemon_rb_can_strength(player), one_way=True) connect(world, player, "Route 23 North", "Victory Road 1F", lambda state: state.pokemon_rb_can_strength(player), one_way=True)
connect(world, player, "Victory Road 1F", "Victory Road 2F", one_way=True) connect(world, player, "Victory Road 1F", "Victory Road 2F", one_way=True)
connect(world, player, "Victory Road 2F", "Victory Road 3F", one_way=True) connect(world, player, "Victory Road 2F", "Victory Road 3F", one_way=True)
connect(world, player, "Victory Road 2F", "Indigo Plateau", lambda state: state.pokemon_rb_has_badges(state.world.elite_four_condition[player], player), one_way=True) connect(world, player, "Victory Road 2F", "Indigo Plateau", lambda state: state.pokemon_rb_has_badges(state.multiworld.elite_four_condition[player], player), one_way=True)
connect(world, player, "Cerulean City", "Cerulean Cave 1F", lambda state: connect(world, player, "Cerulean City", "Cerulean Cave 1F", lambda state:
state.pokemon_rb_cerulean_cave(state.world.cerulean_cave_condition[player].value + (state.world.extra_key_items[player].value * 4), player) and state.pokemon_rb_cerulean_cave(state.multiworld.cerulean_cave_condition[player].value + (state.multiworld.extra_key_items[player].value * 4), player) and
state.pokemon_rb_can_surf(player), one_way=True) state.pokemon_rb_can_surf(player), one_way=True)
connect(world, player, "Cerulean Cave 1F", "Cerulean Cave 2F", one_way=True) connect(world, player, "Cerulean Cave 1F", "Cerulean Cave 2F", one_way=True)
connect(world, player, "Cerulean Cave 1F", "Cerulean Cave B1F", lambda state: state.pokemon_rb_can_surf(player), one_way=True) connect(world, player, "Cerulean Cave 1F", "Cerulean Cave B1F", lambda state: state.pokemon_rb_can_surf(player), one_way=True)

View File

@ -43,7 +43,7 @@ def get_encounter_slots(self):
for location in encounter_slots: for location in encounter_slots:
if isinstance(location.original_item, list): if isinstance(location.original_item, list):
location.original_item = location.original_item[not self.world.game_version[self.player].value] location.original_item = location.original_item[not self.multiworld.game_version[self.player].value]
return encounter_slots return encounter_slots
@ -64,19 +64,19 @@ def randomize_pokemon(self, mon, mons_list, randomize_type):
if randomize_type == 3: if randomize_type == 3:
stat_base = get_base_stat_total(mon) stat_base = get_base_stat_total(mon)
type_mons.sort(key=lambda mon: abs(get_base_stat_total(mon) - stat_base)) type_mons.sort(key=lambda mon: abs(get_base_stat_total(mon) - stat_base))
mon = type_mons[round(self.world.random.triangular(0, len(type_mons) - 1, 0))] mon = type_mons[round(self.multiworld.random.triangular(0, len(type_mons) - 1, 0))]
if randomize_type == 2: if randomize_type == 2:
stat_base = get_base_stat_total(mon) stat_base = get_base_stat_total(mon)
mons_list.sort(key=lambda mon: abs(get_base_stat_total(mon) - stat_base)) mons_list.sort(key=lambda mon: abs(get_base_stat_total(mon) - stat_base))
mon = mons_list[round(self.world.random.triangular(0, 50, 0))] mon = mons_list[round(self.multiworld.random.triangular(0, 50, 0))]
elif randomize_type == 4: elif randomize_type == 4:
mon = self.world.random.choice(mons_list) mon = self.multiworld.random.choice(mons_list)
return mon return mon
def process_trainer_data(self, data): def process_trainer_data(self, data):
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
or self.world.trainer_legendaries[self.player].value] or self.multiworld.trainer_legendaries[self.player].value]
address = rom_addresses["Trainer_Data"] address = rom_addresses["Trainer_Data"]
while address < rom_addresses["Trainer_Data_End"]: while address < rom_addresses["Trainer_Data_End"]:
if data[address] == 255: if data[address] == 255:
@ -93,14 +93,14 @@ def process_trainer_data(self, data):
for i in range(1, 4): for i in range(1, 4):
for l in ["A", "B", "C", "D", "E", "F", "G", "H"]: for l in ["A", "B", "C", "D", "E", "F", "G", "H"]:
if rom_addresses[f"Rival_Starter{i}_{l}"] == address: if rom_addresses[f"Rival_Starter{i}_{l}"] == address:
mon = " ".join(self.world.get_location(f"Pallet Town - Starter {i}", self.player).item.name.split()[1:]) mon = " ".join(self.multiworld.get_location(f"Pallet Town - Starter {i}", self.player).item.name.split()[1:])
if l in ["D", "E", "F", "G", "H"] and mon in poke_data.evolves_to: if l in ["D", "E", "F", "G", "H"] and mon in poke_data.evolves_to:
mon = poke_data.evolves_to[mon] mon = poke_data.evolves_to[mon]
if l in ["F", "G", "H"] and mon in poke_data.evolves_to: if l in ["F", "G", "H"] and mon in poke_data.evolves_to:
mon = poke_data.evolves_to[mon] mon = poke_data.evolves_to[mon]
if mon is None and self.world.randomize_trainer_parties[self.player].value: if mon is None and self.multiworld.randomize_trainer_parties[self.player].value:
mon = poke_data.id_to_mon[data[address]] mon = poke_data.id_to_mon[data[address]]
mon = randomize_pokemon(self, mon, mons_list, self.world.randomize_trainer_parties[self.player].value) mon = randomize_pokemon(self, mon, mons_list, self.multiworld.randomize_trainer_parties[self.player].value)
if mon is not None: if mon is not None:
data[address] = poke_data.pokemon_data[mon]["id"] data[address] = poke_data.pokemon_data[mon]["id"]
@ -114,22 +114,22 @@ def process_static_pokemon(self):
tower_6F_mons = set() tower_6F_mons = set()
for i in range(1, 11): for i in range(1, 11):
tower_6F_mons.add(self.world.get_location(f"Pokemon Tower 6F - Wild Pokemon - {i}", self.player).item.name) tower_6F_mons.add(self.multiworld.get_location(f"Pokemon Tower 6F - Wild Pokemon - {i}", self.player).item.name)
mons_list = [pokemon for pokemon in poke_data.first_stage_pokemon if pokemon not in poke_data.legendary_pokemon mons_list = [pokemon for pokemon in poke_data.first_stage_pokemon if pokemon not in poke_data.legendary_pokemon
or self.world.randomize_legendary_pokemon[self.player].value == 3] or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
if self.world.randomize_legendary_pokemon[self.player].value == 0: if self.multiworld.randomize_legendary_pokemon[self.player].value == 0:
for slot in legendary_slots: for slot in legendary_slots:
location = self.world.get_location(slot.name, self.player) location = self.multiworld.get_location(slot.name, self.player)
location.place_locked_item(self.create_item("Missable " + slot.original_item)) location.place_locked_item(self.create_item("Missable " + slot.original_item))
elif self.world.randomize_legendary_pokemon[self.player].value == 1: elif self.multiworld.randomize_legendary_pokemon[self.player].value == 1:
self.world.random.shuffle(legendary_mons) self.multiworld.random.shuffle(legendary_mons)
for slot in legendary_slots: for slot in legendary_slots:
location = self.world.get_location(slot.name, self.player) location = self.multiworld.get_location(slot.name, self.player)
location.place_locked_item(self.create_item("Missable " + legendary_mons.pop())) location.place_locked_item(self.create_item("Missable " + legendary_mons.pop()))
elif self.world.randomize_legendary_pokemon[self.player].value == 2: elif self.multiworld.randomize_legendary_pokemon[self.player].value == 2:
static_slots = static_slots + legendary_slots static_slots = static_slots + legendary_slots
self.world.random.shuffle(static_slots) self.multiworld.random.shuffle(static_slots)
static_slots.sort(key=lambda s: 0 if s.name == "Pokemon Tower 6F - Restless Soul" else 1) static_slots.sort(key=lambda s: 0 if s.name == "Pokemon Tower 6F - Restless Soul" else 1)
while legendary_slots: while legendary_slots:
swap_slot = legendary_slots.pop() swap_slot = legendary_slots.pop()
@ -137,15 +137,15 @@ def process_static_pokemon(self):
slot_type = slot.type.split()[0] slot_type = slot.type.split()[0]
if slot_type == "Legendary": if slot_type == "Legendary":
slot_type = "Missable" slot_type = "Missable"
location = self.world.get_location(slot.name, self.player) location = self.multiworld.get_location(slot.name, self.player)
location.place_locked_item(self.create_item(slot_type + " " + swap_slot.original_item)) location.place_locked_item(self.create_item(slot_type + " " + swap_slot.original_item))
swap_slot.original_item = slot.original_item swap_slot.original_item = slot.original_item
elif self.world.randomize_legendary_pokemon[self.player].value == 3: elif self.multiworld.randomize_legendary_pokemon[self.player].value == 3:
static_slots = static_slots + legendary_slots static_slots = static_slots + legendary_slots
for slot in static_slots: for slot in static_slots:
location = self.world.get_location(slot.name, self.player) location = self.multiworld.get_location(slot.name, self.player)
randomize_type = self.world.randomize_static_pokemon[self.player].value randomize_type = self.multiworld.randomize_static_pokemon[self.player].value
slot_type = slot.type.split()[0] slot_type = slot.type.split()[0]
if slot_type == "Legendary": if slot_type == "Legendary":
slot_type = "Missable" slot_type = "Missable"
@ -160,8 +160,8 @@ def process_static_pokemon(self):
location.place_locked_item(mon) location.place_locked_item(mon)
for slot in starter_slots: for slot in starter_slots:
location = self.world.get_location(slot.name, self.player) location = self.multiworld.get_location(slot.name, self.player)
randomize_type = self.world.randomize_starter_pokemon[self.player].value randomize_type = self.multiworld.randomize_starter_pokemon[self.player].value
slot_type = "Missable" slot_type = "Missable"
if not randomize_type: if not randomize_type:
location.place_locked_item(self.create_item(slot_type + " " + slot.original_item)) location.place_locked_item(self.create_item(slot_type + " " + slot.original_item))
@ -175,21 +175,21 @@ def process_wild_pokemon(self):
encounter_slots = get_encounter_slots(self) encounter_slots = get_encounter_slots(self)
placed_mons = {pokemon: 0 for pokemon in poke_data.pokemon_data.keys()} placed_mons = {pokemon: 0 for pokemon in poke_data.pokemon_data.keys()}
if self.world.randomize_wild_pokemon[self.player].value: if self.multiworld.randomize_wild_pokemon[self.player].value:
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
or self.world.randomize_legendary_pokemon[self.player].value == 3] or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
self.world.random.shuffle(encounter_slots) self.multiworld.random.shuffle(encounter_slots)
locations = [] locations = []
for slot in encounter_slots: for slot in encounter_slots:
mon = randomize_pokemon(self, slot.original_item, mons_list, self.world.randomize_wild_pokemon[self.player].value) mon = randomize_pokemon(self, slot.original_item, mons_list, self.multiworld.randomize_wild_pokemon[self.player].value)
# if static Pokemon are not randomized, we make sure nothing on Pokemon Tower 6F is a Marowak # if static Pokemon are not randomized, we make sure nothing on Pokemon Tower 6F is a Marowak
# if static Pokemon are randomized we deal with that during static encounter randomization # if static Pokemon are randomized we deal with that during static encounter randomization
while (self.world.randomize_static_pokemon[self.player].value == 0 and mon == "Marowak" while (self.multiworld.randomize_static_pokemon[self.player].value == 0 and mon == "Marowak"
and "Pokemon Tower 6F" in slot.name): and "Pokemon Tower 6F" in slot.name):
# to account for the possibility that only one ground type Pokemon exists, match only stats for this fix # to account for the possibility that only one ground type Pokemon exists, match only stats for this fix
mon = randomize_pokemon(self, slot.original_item, mons_list, 2) mon = randomize_pokemon(self, slot.original_item, mons_list, 2)
placed_mons[mon] += 1 placed_mons[mon] += 1
location = self.world.get_location(slot.name, self.player) location = self.multiworld.get_location(slot.name, self.player)
location.item = self.create_item(mon) location.item = self.create_item(mon)
location.event = True location.event = True
location.locked = True location.locked = True
@ -198,33 +198,33 @@ def process_wild_pokemon(self):
mons_to_add = [] mons_to_add = []
remaining_pokemon = [pokemon for pokemon in poke_data.pokemon_data.keys() if placed_mons[pokemon] == 0 and remaining_pokemon = [pokemon for pokemon in poke_data.pokemon_data.keys() if placed_mons[pokemon] == 0 and
(pokemon not in poke_data.legendary_pokemon or self.world.randomize_legendary_pokemon[self.player].value == 3)] (pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
if self.world.catch_em_all[self.player].value == 1: if self.multiworld.catch_em_all[self.player].value == 1:
mons_to_add = [pokemon for pokemon in poke_data.first_stage_pokemon if placed_mons[pokemon] == 0 and mons_to_add = [pokemon for pokemon in poke_data.first_stage_pokemon if placed_mons[pokemon] == 0 and
(pokemon not in poke_data.legendary_pokemon or self.world.randomize_legendary_pokemon[self.player].value == 3)] (pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
elif self.world.catch_em_all[self.player].value == 2: elif self.multiworld.catch_em_all[self.player].value == 2:
mons_to_add = remaining_pokemon.copy() mons_to_add = remaining_pokemon.copy()
logic_needed_mons = max(self.world.oaks_aide_rt_2[self.player].value, logic_needed_mons = max(self.multiworld.oaks_aide_rt_2[self.player].value,
self.world.oaks_aide_rt_11[self.player].value, self.multiworld.oaks_aide_rt_11[self.player].value,
self.world.oaks_aide_rt_15[self.player].value) self.multiworld.oaks_aide_rt_15[self.player].value)
if self.world.accessibility[self.player] == "minimal": if self.multiworld.accessibility[self.player] == "minimal":
logic_needed_mons = 0 logic_needed_mons = 0
self.world.random.shuffle(remaining_pokemon) self.multiworld.random.shuffle(remaining_pokemon)
while (len([pokemon for pokemon in placed_mons if placed_mons[pokemon] > 0]) while (len([pokemon for pokemon in placed_mons if placed_mons[pokemon] > 0])
+ len(mons_to_add) < logic_needed_mons): + len(mons_to_add) < logic_needed_mons):
mons_to_add.append(remaining_pokemon.pop()) mons_to_add.append(remaining_pokemon.pop())
for mon in mons_to_add: for mon in mons_to_add:
stat_base = get_base_stat_total(mon) stat_base = get_base_stat_total(mon)
candidate_locations = get_encounter_slots(self) candidate_locations = get_encounter_slots(self)
if self.world.randomize_wild_pokemon[self.player].value in [1, 3]: if self.multiworld.randomize_wild_pokemon[self.player].value in [1, 3]:
candidate_locations = [slot for slot in candidate_locations if any([poke_data.pokemon_data[slot.original_item][ candidate_locations = [slot for slot in candidate_locations if any([poke_data.pokemon_data[slot.original_item][
"type1"] in [self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]], "type1"] in [self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]],
poke_data.pokemon_data[slot.original_item]["type2"] in [self.local_poke_data[mon]["type1"], poke_data.pokemon_data[slot.original_item]["type2"] in [self.local_poke_data[mon]["type1"],
self.local_poke_data[mon]["type2"]]])] self.local_poke_data[mon]["type2"]]])]
if not candidate_locations: if not candidate_locations:
candidate_locations = location_data candidate_locations = location_data
candidate_locations = [self.world.get_location(location.name, self.player) for location in candidate_locations] candidate_locations = [self.multiworld.get_location(location.name, self.player) for location in candidate_locations]
candidate_locations.sort(key=lambda slot: abs(get_base_stat_total(slot.item.name) - stat_base)) candidate_locations.sort(key=lambda slot: abs(get_base_stat_total(slot.item.name) - stat_base))
for location in candidate_locations: for location in candidate_locations:
if placed_mons[location.item.name] > 1 or location.item.name not in poke_data.first_stage_pokemon: if placed_mons[location.item.name] > 1 or location.item.name not in poke_data.first_stage_pokemon:
@ -236,7 +236,7 @@ def process_wild_pokemon(self):
else: else:
for slot in encounter_slots: for slot in encounter_slots:
location = self.world.get_location(slot.name, self.player) location = self.multiworld.get_location(slot.name, self.player)
location.item = self.create_item(slot.original_item) location.item = self.create_item(slot.original_item)
location.event = True location.event = True
location.locked = True location.locked = True
@ -250,19 +250,19 @@ def process_pokemon_data(self):
learnsets = deepcopy(poke_data.learnsets) learnsets = deepcopy(poke_data.learnsets)
for mon, mon_data in local_poke_data.items(): for mon, mon_data in local_poke_data.items():
if self.world.randomize_pokemon_stats[self.player].value == 1: if self.multiworld.randomize_pokemon_stats[self.player].value == 1:
stats = [mon_data["hp"], mon_data["atk"], mon_data["def"], mon_data["spd"], mon_data["spc"]] stats = [mon_data["hp"], mon_data["atk"], mon_data["def"], mon_data["spd"], mon_data["spc"]]
self.world.random.shuffle(stats) self.multiworld.random.shuffle(stats)
mon_data["hp"] = stats[0] mon_data["hp"] = stats[0]
mon_data["atk"] = stats[1] mon_data["atk"] = stats[1]
mon_data["def"] = stats[2] mon_data["def"] = stats[2]
mon_data["spd"] = stats[3] mon_data["spd"] = stats[3]
mon_data["spc"] = stats[4] mon_data["spc"] = stats[4]
elif self.world.randomize_pokemon_stats[self.player].value == 2: elif self.multiworld.randomize_pokemon_stats[self.player].value == 2:
old_stats = mon_data["hp"] + mon_data["atk"] + mon_data["def"] + mon_data["spd"] + mon_data["spc"] - 5 old_stats = mon_data["hp"] + mon_data["atk"] + mon_data["def"] + mon_data["spd"] + mon_data["spc"] - 5
stats = [1, 1, 1, 1, 1] stats = [1, 1, 1, 1, 1]
while old_stats > 0: while old_stats > 0:
stat = self.world.random.randint(0, 4) stat = self.multiworld.random.randint(0, 4)
if stats[stat] < 255: if stats[stat] < 255:
old_stats -= 1 old_stats -= 1
stats[stat] += 1 stats[stat] += 1
@ -271,30 +271,30 @@ def process_pokemon_data(self):
mon_data["def"] = stats[2] mon_data["def"] = stats[2]
mon_data["spd"] = stats[3] mon_data["spd"] = stats[3]
mon_data["spc"] = stats[4] mon_data["spc"] = stats[4]
if self.world.randomize_pokemon_types[self.player].value: if self.multiworld.randomize_pokemon_types[self.player].value:
if self.world.randomize_pokemon_types[self.player].value == 1 and mon in poke_data.evolves_from: if self.multiworld.randomize_pokemon_types[self.player].value == 1 and mon in poke_data.evolves_from:
type1 = local_poke_data[poke_data.evolves_from[mon]]["type1"] type1 = local_poke_data[poke_data.evolves_from[mon]]["type1"]
type2 = local_poke_data[poke_data.evolves_from[mon]]["type2"] type2 = local_poke_data[poke_data.evolves_from[mon]]["type2"]
if type1 == type2: if type1 == type2:
if self.world.secondary_type_chance[self.player].value == -1: if self.multiworld.secondary_type_chance[self.player].value == -1:
if mon_data["type1"] != mon_data["type2"]: if mon_data["type1"] != mon_data["type2"]:
while type2 == type1: while type2 == type1:
type2 = self.world.random.choice(list(poke_data.type_names.values())) type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
elif self.world.random.randint(1, 100) <= self.world.secondary_type_chance[self.player].value: elif self.multiworld.random.randint(1, 100) <= self.multiworld.secondary_type_chance[self.player].value:
type2 = self.world.random.choice(list(poke_data.type_names.values())) type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
else: else:
type1 = self.world.random.choice(list(poke_data.type_names.values())) type1 = self.multiworld.random.choice(list(poke_data.type_names.values()))
type2 = type1 type2 = type1
if ((self.world.secondary_type_chance[self.player].value == -1 and mon_data["type1"] if ((self.multiworld.secondary_type_chance[self.player].value == -1 and mon_data["type1"]
!= mon_data["type2"]) or self.world.random.randint(1, 100) != mon_data["type2"]) or self.multiworld.random.randint(1, 100)
<= self.world.secondary_type_chance[self.player].value): <= self.multiworld.secondary_type_chance[self.player].value):
while type2 == type1: while type2 == type1:
type2 = self.world.random.choice(list(poke_data.type_names.values())) type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
mon_data["type1"] = type1 mon_data["type1"] = type1
mon_data["type2"] = type2 mon_data["type2"] = type2
if self.world.randomize_pokemon_movesets[self.player].value: if self.multiworld.randomize_pokemon_movesets[self.player].value:
if self.world.randomize_pokemon_movesets[self.player].value == 1: if self.multiworld.randomize_pokemon_movesets[self.player].value == 1:
if mon_data["type1"] == "Normal" and mon_data["type2"] == "Normal": if mon_data["type1"] == "Normal" and mon_data["type2"] == "Normal":
chances = [[75, "Normal"]] chances = [[75, "Normal"]]
elif mon_data["type1"] == "Normal" or mon_data["type2"] == "Normal": elif mon_data["type1"] == "Normal" or mon_data["type2"] == "Normal":
@ -312,30 +312,30 @@ def process_pokemon_data(self):
moves = list(poke_data.moves.keys()) moves = list(poke_data.moves.keys())
for move in ["No Move"] + poke_data.hm_moves: for move in ["No Move"] + poke_data.hm_moves:
moves.remove(move) moves.remove(move)
mon_data["start move 1"] = get_move(moves, chances, self.world.random, True) mon_data["start move 1"] = get_move(moves, chances, self.multiworld.random, True)
for i in range(2, 5): for i in range(2, 5):
if mon_data[f"start move {i}"] != "No Move" or self.world.start_with_four_moves[ if mon_data[f"start move {i}"] != "No Move" or self.multiworld.start_with_four_moves[
self.player].value == 1: self.player].value == 1:
mon_data[f"start move {i}"] = get_move(moves, chances, self.world.random) mon_data[f"start move {i}"] = get_move(moves, chances, self.multiworld.random)
if mon in learnsets: if mon in learnsets:
for move_num in range(0, len(learnsets[mon])): for move_num in range(0, len(learnsets[mon])):
learnsets[mon][move_num] = get_move(moves, chances, self.world.random) learnsets[mon][move_num] = get_move(moves, chances, self.multiworld.random)
if self.world.randomize_pokemon_catch_rates[self.player].value: if self.multiworld.randomize_pokemon_catch_rates[self.player].value:
mon_data["catch rate"] = self.world.random.randint(self.world.minimum_catch_rate[self.player], 255) mon_data["catch rate"] = self.multiworld.random.randint(self.multiworld.minimum_catch_rate[self.player], 255)
else: else:
mon_data["catch rate"] = max(self.world.minimum_catch_rate[self.player], mon_data["catch rate"]) mon_data["catch rate"] = max(self.multiworld.minimum_catch_rate[self.player], mon_data["catch rate"])
if mon in poke_data.evolves_from.keys() and mon_data["type1"] == local_poke_data[poke_data.evolves_from[mon]]["type1"] and mon_data["type2"] == local_poke_data[poke_data.evolves_from[mon]]["type2"]: if mon in poke_data.evolves_from.keys() and mon_data["type1"] == local_poke_data[poke_data.evolves_from[mon]]["type1"] and mon_data["type2"] == local_poke_data[poke_data.evolves_from[mon]]["type2"]:
mon_data["tms"] = local_poke_data[poke_data.evolves_from[mon]]["tms"] mon_data["tms"] = local_poke_data[poke_data.evolves_from[mon]]["tms"]
elif mon != "Mew": elif mon != "Mew":
tms_hms = poke_data.tm_moves + poke_data.hm_moves tms_hms = poke_data.tm_moves + poke_data.hm_moves
for flag, tm_move in enumerate(tms_hms): for flag, tm_move in enumerate(tms_hms):
if (flag < 50 and self.world.tm_compatibility[self.player].value == 1) or (flag >= 50 and self.world.hm_compatibility[self.player].value == 1): if (flag < 50 and self.multiworld.tm_compatibility[self.player].value == 1) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 1):
type_match = poke_data.moves[tm_move]["type"] in [mon_data["type1"], mon_data["type2"]] type_match = poke_data.moves[tm_move]["type"] in [mon_data["type1"], mon_data["type2"]]
bit = int(self.world.random.randint(1, 100) < [[90, 50, 25], [100, 75, 25]][flag >= 50][0 if type_match else 1 if poke_data.moves[tm_move]["type"] == "Normal" else 2]) bit = int(self.multiworld.random.randint(1, 100) < [[90, 50, 25], [100, 75, 25]][flag >= 50][0 if type_match else 1 if poke_data.moves[tm_move]["type"] == "Normal" else 2])
elif (flag < 50 and self.world.tm_compatibility[self.player].value == 2) or (flag >= 50 and self.world.hm_compatibility[self.player].value == 2): elif (flag < 50 and self.multiworld.tm_compatibility[self.player].value == 2) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 2):
bit = [0, 1][self.world.random.randint(0, 1)] bit = [0, 1][self.multiworld.random.randint(0, 1)]
elif (flag < 50 and self.world.tm_compatibility[self.player].value == 3) or (flag >= 50 and self.world.hm_compatibility[self.player].value == 3): elif (flag < 50 and self.multiworld.tm_compatibility[self.player].value == 3) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 3):
bit = 1 bit = 1
else: else:
continue continue
@ -350,14 +350,14 @@ def process_pokemon_data(self):
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
random = self.world.slot_seeds[self.player] random = self.multiworld.slot_seeds[self.player]
game_version = self.world.game_version[self.player].current_key game_version = self.multiworld.game_version[self.player].current_key
data = bytearray(get_base_rom_bytes(game_version)) data = bytearray(get_base_rom_bytes(game_version))
basemd5 = hashlib.md5() basemd5 = hashlib.md5()
basemd5.update(data) basemd5.update(data)
for location in self.world.get_locations(): for location in self.multiworld.get_locations():
if location.player != self.player or location.rom_address is None: if location.player != self.player or location.rom_address is None:
continue continue
if location.item and location.item.player == self.player: if location.item and location.item.player == self.player:
@ -377,42 +377,42 @@ def generate_output(self, output_directory: str):
data[location.rom_address] = 0x2C # AP Item data[location.rom_address] = 0x2C # AP Item
data[rom_addresses['Fly_Location']] = self.fly_map_code data[rom_addresses['Fly_Location']] = self.fly_map_code
if self.world.tea[self.player].value: if self.multiworld.tea[self.player].value:
data[rom_addresses["Option_Tea"]] = 1 data[rom_addresses["Option_Tea"]] = 1
data[rom_addresses["Guard_Drink_List"]] = 0x54 data[rom_addresses["Guard_Drink_List"]] = 0x54
data[rom_addresses["Guard_Drink_List"] + 1] = 0 data[rom_addresses["Guard_Drink_List"] + 1] = 0
data[rom_addresses["Guard_Drink_List"] + 2] = 0 data[rom_addresses["Guard_Drink_List"] + 2] = 0
if self.world.extra_key_items[self.player].value: if self.multiworld.extra_key_items[self.player].value:
data[rom_addresses['Options']] |= 4 data[rom_addresses['Options']] |= 4
data[rom_addresses["Option_Blind_Trainers"]] = round(self.world.blind_trainers[self.player].value * 2.55) data[rom_addresses["Option_Blind_Trainers"]] = round(self.multiworld.blind_trainers[self.player].value * 2.55)
data[rom_addresses['Option_Cerulean_Cave_Condition']] = self.world.cerulean_cave_condition[self.player].value data[rom_addresses['Option_Cerulean_Cave_Condition']] = self.multiworld.cerulean_cave_condition[self.player].value
data[rom_addresses['Option_Encounter_Minimum_Steps']] = self.world.minimum_steps_between_encounters[self.player].value data[rom_addresses['Option_Encounter_Minimum_Steps']] = self.multiworld.minimum_steps_between_encounters[self.player].value
data[rom_addresses['Option_Victory_Road_Badges']] = self.world.victory_road_condition[self.player].value data[rom_addresses['Option_Victory_Road_Badges']] = self.multiworld.victory_road_condition[self.player].value
data[rom_addresses['Option_Pokemon_League_Badges']] = self.world.elite_four_condition[self.player].value data[rom_addresses['Option_Pokemon_League_Badges']] = self.multiworld.elite_four_condition[self.player].value
data[rom_addresses['Option_Viridian_Gym_Badges']] = self.world.viridian_gym_condition[self.player].value data[rom_addresses['Option_Viridian_Gym_Badges']] = self.multiworld.viridian_gym_condition[self.player].value
data[rom_addresses['Option_EXP_Modifier']] = self.world.exp_modifier[self.player].value data[rom_addresses['Option_EXP_Modifier']] = self.multiworld.exp_modifier[self.player].value
if not self.world.require_item_finder[self.player].value: if not self.multiworld.require_item_finder[self.player].value:
data[rom_addresses['Option_Itemfinder']] = 0 data[rom_addresses['Option_Itemfinder']] = 0
if self.world.extra_strength_boulders[self.player].value: if self.multiworld.extra_strength_boulders[self.player].value:
for i in range(0, 3): for i in range(0, 3):
data[rom_addresses['Option_Boulders'] + (i * 3)] = 0x15 data[rom_addresses['Option_Boulders'] + (i * 3)] = 0x15
if self.world.extra_key_items[self.player].value: if self.multiworld.extra_key_items[self.player].value:
for i in range(0, 4): for i in range(0, 4):
data[rom_addresses['Option_Rock_Tunnel_Extra_Items'] + (i * 3)] = 0x15 data[rom_addresses['Option_Rock_Tunnel_Extra_Items'] + (i * 3)] = 0x15
if self.world.old_man[self.player].value == 2: if self.multiworld.old_man[self.player].value == 2:
data[rom_addresses['Option_Old_Man']] = 0x11 data[rom_addresses['Option_Old_Man']] = 0x11
data[rom_addresses['Option_Old_Man_Lying']] = 0x15 data[rom_addresses['Option_Old_Man_Lying']] = 0x15
money = str(self.world.starting_money[self.player].value) money = str(self.multiworld.starting_money[self.player].value)
while len(money) < 6: while len(money) < 6:
money = "0" + money money = "0" + money
data[rom_addresses["Starting_Money_High"]] = int(money[:2], 16) data[rom_addresses["Starting_Money_High"]] = int(money[:2], 16)
data[rom_addresses["Starting_Money_Middle"]] = int(money[2:4], 16) data[rom_addresses["Starting_Money_Middle"]] = int(money[2:4], 16)
data[rom_addresses["Starting_Money_Low"]] = int(money[4:], 16) data[rom_addresses["Starting_Money_Low"]] = int(money[4:], 16)
data[rom_addresses["Text_Badges_Needed"]] = encode_text( data[rom_addresses["Text_Badges_Needed"]] = encode_text(
str(max(self.world.victory_road_condition[self.player].value, str(max(self.multiworld.victory_road_condition[self.player].value,
self.world.elite_four_condition[self.player].value)))[0] self.multiworld.elite_four_condition[self.player].value)))[0]
if self.world.badges_needed_for_hm_moves[self.player].value == 0: if self.multiworld.badges_needed_for_hm_moves[self.player].value == 0:
for hm_move in poke_data.hm_moves: for hm_move in poke_data.hm_moves:
write_bytes(data, bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), write_bytes(data, bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
rom_addresses["HM_" + hm_move + "_Badge_a"]) rom_addresses["HM_" + hm_move + "_Badge_a"])
@ -437,7 +437,7 @@ def generate_output(self, output_directory: str):
write_bytes(data, encode_text("Nothing"), rom_addresses["Badge_Text_" + badge.replace(" ", "_")]) write_bytes(data, encode_text("Nothing"), rom_addresses["Badge_Text_" + badge.replace(" ", "_")])
chart = deepcopy(poke_data.type_chart) chart = deepcopy(poke_data.type_chart)
if self.world.randomize_type_matchup_types[self.player].value == 1: if self.multiworld.randomize_type_matchup_types[self.player].value == 1:
attacking_types = [] attacking_types = []
defending_types = [] defending_types = []
for matchup in chart: for matchup in chart:
@ -458,7 +458,7 @@ def generate_output(self, output_directory: str):
for matchup, chart_row in zip(matchups, chart): for matchup, chart_row in zip(matchups, chart):
chart_row[0] = matchup[0] chart_row[0] = matchup[0]
chart_row[1] = matchup[1] chart_row[1] = matchup[1]
elif self.world.randomize_type_matchup_types[self.player].value == 2: elif self.multiworld.randomize_type_matchup_types[self.player].value == 2:
used_matchups = [] used_matchups = []
for matchup in chart: for matchup in chart:
matchup[0] = random.choice(list(poke_data.type_names.values())) matchup[0] = random.choice(list(poke_data.type_names.values()))
@ -467,17 +467,17 @@ def generate_output(self, output_directory: str):
matchup[0] = random.choice(list(poke_data.type_names.values())) matchup[0] = random.choice(list(poke_data.type_names.values()))
matchup[1] = random.choice(list(poke_data.type_names.values())) matchup[1] = random.choice(list(poke_data.type_names.values()))
used_matchups.append([matchup[0], matchup[1]]) used_matchups.append([matchup[0], matchup[1]])
if self.world.randomize_type_matchup_type_effectiveness[self.player].value == 1: if self.multiworld.randomize_type_matchup_type_effectiveness[self.player].value == 1:
effectiveness_list = [] effectiveness_list = []
for matchup in chart: for matchup in chart:
effectiveness_list.append(matchup[2]) effectiveness_list.append(matchup[2])
random.shuffle(effectiveness_list) random.shuffle(effectiveness_list)
for (matchup, effectiveness) in zip(chart, effectiveness_list): for (matchup, effectiveness) in zip(chart, effectiveness_list):
matchup[2] = effectiveness matchup[2] = effectiveness
elif self.world.randomize_type_matchup_type_effectiveness[self.player].value == 2: elif self.multiworld.randomize_type_matchup_type_effectiveness[self.player].value == 2:
for matchup in chart: for matchup in chart:
matchup[2] = random.choice([0] + ([5, 20] * 5)) matchup[2] = random.choice([0] + ([5, 20] * 5))
elif self.world.randomize_type_matchup_type_effectiveness[self.player].value == 3: elif self.multiworld.randomize_type_matchup_type_effectiveness[self.player].value == 3:
for matchup in chart: for matchup in chart:
matchup[2] = random.choice([i for i in range(0, 21) if i != 10]) matchup[2] = random.choice([i for i in range(0, 21) if i != 10])
type_loc = rom_addresses["Type_Chart"] type_loc = rom_addresses["Type_Chart"]
@ -492,7 +492,7 @@ def generate_output(self, output_directory: str):
# to the way effectiveness messages are generated. # to the way effectiveness messages are generated.
self.type_chart = sorted(chart, key=lambda matchup: 0 - matchup[2]) self.type_chart = sorted(chart, key=lambda matchup: 0 - matchup[2])
if self.world.normalize_encounter_chances[self.player].value: if self.multiworld.normalize_encounter_chances[self.player].value:
chances = [25, 51, 77, 103, 129, 155, 180, 205, 230, 255] chances = [25, 51, 77, 103, 129, 155, 180, 205, 230, 255]
for i, chance in enumerate(chances): for i, chance in enumerate(chances):
data[rom_addresses['Encounter_Chances'] + (i * 2)] = chance data[rom_addresses['Encounter_Chances'] + (i * 2)] = chance
@ -520,14 +520,14 @@ def generate_output(self, output_directory: str):
for i, move in enumerate(self.learnsets[mon]): for i, move in enumerate(self.learnsets[mon]):
data[(address + 1) + i * 2] = poke_data.moves[move]["id"] data[(address + 1) + i * 2] = poke_data.moves[move]["id"]
data[rom_addresses["Option_Aide_Rt2"]] = self.world.oaks_aide_rt_2[self.player] data[rom_addresses["Option_Aide_Rt2"]] = self.multiworld.oaks_aide_rt_2[self.player]
data[rom_addresses["Option_Aide_Rt11"]] = self.world.oaks_aide_rt_11[self.player] data[rom_addresses["Option_Aide_Rt11"]] = self.multiworld.oaks_aide_rt_11[self.player]
data[rom_addresses["Option_Aide_Rt15"]] = self.world.oaks_aide_rt_15[self.player] data[rom_addresses["Option_Aide_Rt15"]] = self.multiworld.oaks_aide_rt_15[self.player]
if self.world.safari_zone_normal_battles[self.player].value == 1: if self.multiworld.safari_zone_normal_battles[self.player].value == 1:
data[rom_addresses["Option_Safari_Zone_Battle_Type"]] = 255 data[rom_addresses["Option_Safari_Zone_Battle_Type"]] = 255
if self.world.reusable_tms[self.player].value: if self.multiworld.reusable_tms[self.player].value:
data[rom_addresses["Option_Reusable_TMs"]] = 0xC9 data[rom_addresses["Option_Reusable_TMs"]] = 0xC9
process_trainer_data(self, data) process_trainer_data(self, data)
@ -537,17 +537,17 @@ def generate_output(self, output_directory: str):
data[rom_addresses['Title_Mon_First']] = mons.pop() data[rom_addresses['Title_Mon_First']] = mons.pop()
for mon in range(0, 16): for mon in range(0, 16):
data[rom_addresses['Title_Mons'] + mon] = mons.pop() data[rom_addresses['Title_Mons'] + mon] = mons.pop()
if self.world.game_version[self.player].value: if self.multiworld.game_version[self.player].value:
mons.sort(key=lambda mon: 0 if mon == self.world.get_location("Pallet Town - Starter 1", self.player).item.name mons.sort(key=lambda mon: 0 if mon == self.multiworld.get_location("Pallet Town - Starter 1", self.player).item.name
else 1 if mon == self.world.get_location("Pallet Town - Starter 2", self.player).item.name else else 1 if mon == self.multiworld.get_location("Pallet Town - Starter 2", self.player).item.name else
2 if mon == self.world.get_location("Pallet Town - Starter 3", self.player).item.name else 3) 2 if mon == self.multiworld.get_location("Pallet Town - Starter 3", self.player).item.name else 3)
else: else:
mons.sort(key=lambda mon: 0 if mon == self.world.get_location("Pallet Town - Starter 2", self.player).item.name mons.sort(key=lambda mon: 0 if mon == self.multiworld.get_location("Pallet Town - Starter 2", self.player).item.name
else 1 if mon == self.world.get_location("Pallet Town - Starter 1", self.player).item.name else else 1 if mon == self.multiworld.get_location("Pallet Town - Starter 1", self.player).item.name else
2 if mon == self.world.get_location("Pallet Town - Starter 3", self.player).item.name else 3) 2 if mon == self.multiworld.get_location("Pallet Town - Starter 3", self.player).item.name else 3)
write_bytes(data, encode_text(self.world.seed_name, 20, True), rom_addresses['Title_Seed']) write_bytes(data, encode_text(self.multiworld.seed_name, 20, True), rom_addresses['Title_Seed'])
slot_name = self.world.player_name[self.player] slot_name = self.multiworld.player_name[self.player]
slot_name.replace("@", " ") slot_name.replace("@", " ")
slot_name.replace("<", " ") slot_name.replace("<", " ")
slot_name.replace(">", " ") slot_name.replace(">", " ")
@ -557,23 +557,23 @@ def generate_output(self, output_directory: str):
write_bytes(data, self.rival_name, rom_addresses['Rival_Name']) write_bytes(data, self.rival_name, rom_addresses['Rival_Name'])
write_bytes(data, basemd5.digest(), 0xFFCC) write_bytes(data, basemd5.digest(), 0xFFCC)
write_bytes(data, self.world.seed_name.encode(), 0xFFDC) write_bytes(data, self.multiworld.seed_name.encode(), 0xFFDC)
write_bytes(data, self.world.player_name[self.player].encode(), 0xFFF0) write_bytes(data, self.multiworld.player_name[self.player].encode(), 0xFFF0)
outfilepname = f'_P{self.player}' outfilepname = f'_P{self.player}'
outfilepname += f"_{self.world.get_file_safe_player_name(self.player).replace(' ', '_')}" \ outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}" \
if self.world.player_name[self.player] != 'Player%d' % self.player else '' if self.multiworld.player_name[self.player] != 'Player%d' % self.player else ''
rompath = os.path.join(output_directory, f'AP_{self.world.seed_name}{outfilepname}.gb') rompath = os.path.join(output_directory, f'AP_{self.multiworld.seed_name}{outfilepname}.gb')
with open(rompath, 'wb') as outfile: with open(rompath, 'wb') as outfile:
outfile.write(data) outfile.write(data)
if self.world.game_version[self.player].current_key == "red": if self.multiworld.game_version[self.player].current_key == "red":
patch = RedDeltaPatch(os.path.splitext(rompath)[0] + RedDeltaPatch.patch_file_ending, player=self.player, patch = RedDeltaPatch(os.path.splitext(rompath)[0] + RedDeltaPatch.patch_file_ending, player=self.player,
player_name=self.world.player_name[self.player], patched_path=rompath) player_name=self.multiworld.player_name[self.player], patched_path=rompath)
else: else:
patch = BlueDeltaPatch(os.path.splitext(rompath)[0] + BlueDeltaPatch.patch_file_ending, player=self.player, patch = BlueDeltaPatch(os.path.splitext(rompath)[0] + BlueDeltaPatch.patch_file_ending, player=self.player,
player_name=self.world.player_name[self.player], patched_path=rompath) player_name=self.multiworld.player_name[self.player], patched_path=rompath)
patch.write() patch.write()
os.unlink(rompath) os.unlink(rompath)

View File

@ -10,13 +10,13 @@ def set_rules(world, player):
"Pallet Town - Rival's Sister": lambda state: state.has("Oak's Parcel", player), "Pallet Town - Rival's Sister": lambda state: state.has("Oak's Parcel", player),
"Pallet Town - Oak's Post-Route-22-Rival Gift": lambda state: state.has("Oak's Parcel", player), "Pallet Town - Oak's Post-Route-22-Rival Gift": lambda state: state.has("Oak's Parcel", player),
"Viridian City - Sleepy Guy": lambda state: state.pokemon_rb_can_cut(player) or state.pokemon_rb_can_surf(player), "Viridian City - Sleepy Guy": lambda state: state.pokemon_rb_can_cut(player) or state.pokemon_rb_can_surf(player),
"Route 2 - Oak's Aide": lambda state: state.pokemon_rb_has_pokemon(state.world.oaks_aide_rt_2[player].value + 5, player), "Route 2 - Oak's Aide": lambda state: state.pokemon_rb_has_pokemon(state.multiworld.oaks_aide_rt_2[player].value + 5, player),
"Pewter City - Museum": lambda state: state.pokemon_rb_can_cut(player), "Pewter City - Museum": lambda state: state.pokemon_rb_can_cut(player),
"Cerulean City - Bicycle Shop": lambda state: state.has("Bike Voucher", player), "Cerulean City - Bicycle Shop": lambda state: state.has("Bike Voucher", player),
"Lavender Town - Mr. Fuji": lambda state: state.has("Fuji Saved", player), "Lavender Town - Mr. Fuji": lambda state: state.has("Fuji Saved", player),
"Vermilion Gym - Lt. Surge 1": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)), "Vermilion Gym - Lt. Surge 1": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)),
"Vermilion Gym - Lt. Surge 2": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)), "Vermilion Gym - Lt. Surge 2": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)),
"Route 11 - Oak's Aide": lambda state: state.pokemon_rb_has_pokemon(state.world.oaks_aide_rt_11[player].value + 5, player), "Route 11 - Oak's Aide": lambda state: state.pokemon_rb_has_pokemon(state.multiworld.oaks_aide_rt_11[player].value + 5, player),
"Celadon City - Stranded Man": lambda state: state.pokemon_rb_can_surf(player), "Celadon City - Stranded Man": lambda state: state.pokemon_rb_can_surf(player),
"Silph Co 11F - Silph Co President": lambda state: state.has("Card Key", player), "Silph Co 11F - Silph Co President": lambda state: state.has("Card Key", player),
"Fuchsia City - Safari Zone Warden": lambda state: state.has("Gold Teeth", player), "Fuchsia City - Safari Zone Warden": lambda state: state.has("Gold Teeth", player),

View File

@ -5,10 +5,10 @@ from ..AutoWorld import LogicMixin
class RaftLogic(LogicMixin): class RaftLogic(LogicMixin):
def raft_paddleboard_mode_enabled(self, player): def raft_paddleboard_mode_enabled(self, player):
return self.world.paddleboard_mode[player].value return self.multiworld.paddleboard_mode[player].value
def raft_big_islands_available(self, player): def raft_big_islands_available(self, player):
return self.world.big_island_early_crafting[player].value or self.raft_can_access_radio_tower(player) return self.multiworld.big_island_early_crafting[player].value or self.raft_can_access_radio_tower(player)
def raft_can_smelt_items(self, player): def raft_can_smelt_items(self, player):
return self.has("Smelter", player) return self.has("Smelter", player)

View File

@ -43,8 +43,8 @@ class RaftWorld(World):
required_client_version = (0, 3, 4) required_client_version = (0, 3, 4)
def generate_basic(self): def generate_basic(self):
minRPSpecified = self.world.minimum_resource_pack_amount[self.player].value minRPSpecified = self.multiworld.minimum_resource_pack_amount[self.player].value
maxRPSpecified = self.world.maximum_resource_pack_amount[self.player].value maxRPSpecified = self.multiworld.maximum_resource_pack_amount[self.player].value
minimumResourcePackAmount = min(minRPSpecified, maxRPSpecified) minimumResourcePackAmount = min(minRPSpecified, maxRPSpecified)
maximumResourcePackAmount = max(minRPSpecified, maxRPSpecified) maximumResourcePackAmount = max(minRPSpecified, maxRPSpecified)
# Generate item pool # Generate item pool
@ -56,21 +56,21 @@ class RaftWorld(World):
extraItemNamePool = [] extraItemNamePool = []
extras = len(location_table) - len(item_table) - 1 # Victory takes up 1 unaccounted-for slot extras = len(location_table) - len(item_table) - 1 # Victory takes up 1 unaccounted-for slot
if extras > 0: if extras > 0:
if (self.world.filler_item_types[self.player].value != 1): # Use resource packs if (self.multiworld.filler_item_types[self.player].value != 1): # Use resource packs
for packItem in resourcePackItems: for packItem in resourcePackItems:
for i in range(minimumResourcePackAmount, maximumResourcePackAmount + 1): for i in range(minimumResourcePackAmount, maximumResourcePackAmount + 1):
extraItemNamePool.append(createResourcePackName(i, packItem)) extraItemNamePool.append(createResourcePackName(i, packItem))
if self.world.filler_item_types[self.player].value != 0: # Use duplicate items if self.multiworld.filler_item_types[self.player].value != 0: # Use duplicate items
dupeItemPool = item_table.copy() dupeItemPool = item_table.copy()
# Remove frequencies if necessary # Remove frequencies if necessary
if self.world.island_frequency_locations[self.player].value != 5: # Not completely random locations if self.multiworld.island_frequency_locations[self.player].value != 5: # Not completely random locations
dupeItemPool = (itm for itm in dupeItemPool if "Frequency" not in itm["name"]) dupeItemPool = (itm for itm in dupeItemPool if "Frequency" not in itm["name"])
# Remove progression or non-progression items if necessary # Remove progression or non-progression items if necessary
if (self.world.duplicate_items[self.player].value == 0): # Progression only if (self.multiworld.duplicate_items[self.player].value == 0): # Progression only
dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == True) dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == True)
elif (self.world.duplicate_items[self.player].value == 1): # Non-progression only elif (self.multiworld.duplicate_items[self.player].value == 1): # Non-progression only
dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == False) dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == False)
dupeItemPool = list(dupeItemPool) dupeItemPool = list(dupeItemPool)
@ -84,23 +84,23 @@ class RaftWorld(World):
raft_item = self.create_item_replaceAsNecessary(randomItem) raft_item = self.create_item_replaceAsNecessary(randomItem)
pool.append(raft_item) pool.append(raft_item)
self.world.itempool += pool self.multiworld.itempool += pool
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)
def create_regions(self): def create_regions(self):
create_regions(self.world, self.player) create_regions(self.multiworld, self.player)
def get_pre_fill_items(self): def get_pre_fill_items(self):
if self.world.island_frequency_locations[self.player] in [0, 1, 2, 3]: if self.multiworld.island_frequency_locations[self.player] in [0, 1, 2, 3]:
return [loc.item for loc in self.world.get_filled_locations()] return [loc.item for loc in self.multiworld.get_filled_locations()]
return [] return []
def create_item_replaceAsNecessary(self, name: str) -> Item: def create_item_replaceAsNecessary(self, name: str) -> Item:
isFrequency = "Frequency" in name isFrequency = "Frequency" in name
shouldUseProgressive = ((isFrequency and self.world.island_frequency_locations[self.player].value == 4) shouldUseProgressive = ((isFrequency and self.multiworld.island_frequency_locations[self.player].value == 4)
or (not isFrequency and self.world.progressive_items[self.player].value)) or (not isFrequency and self.multiworld.progressive_items[self.player].value))
if shouldUseProgressive and name in progressive_table: if shouldUseProgressive and name in progressive_table:
name = progressive_table[name] name = progressive_table[name]
return self.create_item(name) return self.create_item(name)
@ -128,7 +128,7 @@ class RaftWorld(World):
return super(RaftWorld, self).collect_item(state, item, remove) return super(RaftWorld, self).collect_item(state, item, remove)
def pre_fill(self): def pre_fill(self):
if self.world.island_frequency_locations[self.player] == 0: if self.multiworld.island_frequency_locations[self.player] == 0:
self.setLocationItem("Radio Tower Frequency to Vasagatan", "Vasagatan Frequency") self.setLocationItem("Radio Tower Frequency to Vasagatan", "Vasagatan Frequency")
self.setLocationItem("Vasagatan Frequency to Balboa", "Balboa Island Frequency") self.setLocationItem("Vasagatan Frequency to Balboa", "Balboa Island Frequency")
self.setLocationItem("Relay Station quest", "Caravan Island Frequency") self.setLocationItem("Relay Station quest", "Caravan Island Frequency")
@ -136,7 +136,7 @@ class RaftWorld(World):
self.setLocationItem("Tangaroa Frequency to Varuna Point", "Varuna Point Frequency") self.setLocationItem("Tangaroa Frequency to Varuna Point", "Varuna Point Frequency")
self.setLocationItem("Varuna Point Frequency to Temperance", "Temperance Frequency") self.setLocationItem("Varuna Point Frequency to Temperance", "Temperance Frequency")
self.setLocationItem("Temperance Frequency to Utopia", "Utopia Frequency") self.setLocationItem("Temperance Frequency to Utopia", "Utopia Frequency")
elif self.world.island_frequency_locations[self.player] == 1: elif self.multiworld.island_frequency_locations[self.player] == 1:
self.setLocationItemFromRegion("RadioTower", "Vasagatan Frequency") self.setLocationItemFromRegion("RadioTower", "Vasagatan Frequency")
self.setLocationItemFromRegion("Vasagatan", "Balboa Island Frequency") self.setLocationItemFromRegion("Vasagatan", "Balboa Island Frequency")
self.setLocationItemFromRegion("BalboaIsland", "Caravan Island Frequency") self.setLocationItemFromRegion("BalboaIsland", "Caravan Island Frequency")
@ -144,7 +144,7 @@ class RaftWorld(World):
self.setLocationItemFromRegion("Tangaroa", "Varuna Point Frequency") self.setLocationItemFromRegion("Tangaroa", "Varuna Point Frequency")
self.setLocationItemFromRegion("Varuna Point", "Temperance Frequency") self.setLocationItemFromRegion("Varuna Point", "Temperance Frequency")
self.setLocationItemFromRegion("Temperance", "Utopia Frequency") self.setLocationItemFromRegion("Temperance", "Utopia Frequency")
elif self.world.island_frequency_locations[self.player] in [2, 3]: elif self.multiworld.island_frequency_locations[self.player] in [2, 3]:
locationToFrequencyItemMap = { locationToFrequencyItemMap = {
"Vasagatan": "Vasagatan Frequency", "Vasagatan": "Vasagatan Frequency",
"BalboaIsland": "Balboa Island Frequency", "BalboaIsland": "Balboa Island Frequency",
@ -172,37 +172,37 @@ class RaftWorld(World):
else: else:
currentLocation = availableLocationList[0] # Utopia (only one left in list) currentLocation = availableLocationList[0] # Utopia (only one left in list)
availableLocationList.remove(currentLocation) availableLocationList.remove(currentLocation)
if self.world.island_frequency_locations[self.player] == 2: if self.multiworld.island_frequency_locations[self.player] == 2:
self.setLocationItem(locationToVanillaFrequencyLocationMap[previousLocation], locationToFrequencyItemMap[currentLocation]) self.setLocationItem(locationToVanillaFrequencyLocationMap[previousLocation], locationToFrequencyItemMap[currentLocation])
elif self.world.island_frequency_locations[self.player] == 3: elif self.multiworld.island_frequency_locations[self.player] == 3:
self.setLocationItemFromRegion(previousLocation, locationToFrequencyItemMap[currentLocation]) self.setLocationItemFromRegion(previousLocation, locationToFrequencyItemMap[currentLocation])
previousLocation = currentLocation previousLocation = currentLocation
# Victory item # Victory item
self.world.get_location("Utopia Complete", self.player).place_locked_item( self.multiworld.get_location("Utopia Complete", self.player).place_locked_item(
RaftItem("Victory", ItemClassification.progression, None, player=self.player)) RaftItem("Victory", ItemClassification.progression, None, player=self.player))
def setLocationItem(self, location: str, itemName: str): def setLocationItem(self, location: str, itemName: str):
itemToUse = next(filter(lambda itm: itm.name == itemName, self.world.itempool)) itemToUse = next(filter(lambda itm: itm.name == itemName, self.multiworld.itempool))
self.world.itempool.remove(itemToUse) self.multiworld.itempool.remove(itemToUse)
self.world.get_location(location, self.player).place_locked_item(itemToUse) self.multiworld.get_location(location, self.player).place_locked_item(itemToUse)
def setLocationItemFromRegion(self, region: str, itemName: str): def setLocationItemFromRegion(self, region: str, itemName: str):
itemToUse = next(filter(lambda itm: itm.name == itemName, self.world.itempool)) itemToUse = next(filter(lambda itm: itm.name == itemName, self.multiworld.itempool))
self.world.itempool.remove(itemToUse) self.multiworld.itempool.remove(itemToUse)
location = random.choice(list(loc for loc in location_table if loc["region"] == region)) location = random.choice(list(loc for loc in location_table if loc["region"] == region))
self.world.get_location(location["name"], self.player).place_locked_item(itemToUse) self.multiworld.get_location(location["name"], self.player).place_locked_item(itemToUse)
def fill_slot_data(self): def fill_slot_data(self):
return { return {
"IslandGenerationDistance": self.world.island_generation_distance[self.player].value, "IslandGenerationDistance": self.multiworld.island_generation_distance[self.player].value,
"ExpensiveResearch": bool(self.world.expensive_research[self.player].value), "ExpensiveResearch": bool(self.multiworld.expensive_research[self.player].value),
"DeathLink": bool(self.world.death_link[self.player].value) "DeathLink": bool(self.multiworld.death_link[self.player].value)
} }
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
ret = Region(name, RegionType.Generic, name, player) ret = Region(name, RegionType.Generic, name, player)
ret.world = world ret.multiworld = world
if locations: if locations:
for location in locations: for location in locations:
loc_id = locations_lookup_name_to_id.get(location, 0) loc_id = locations_lookup_name_to_id.get(location, 0)

View File

@ -102,7 +102,7 @@ def create_regions(world: MultiWorld, player: int):
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
ret = Region(name, RegionType.Generic, name, player) ret = Region(name, RegionType.Generic, name, player)
ret.world = world ret.multiworld = world
if locations: if locations:
for loc_name in locations: for loc_name in locations:
loc_data = location_table.get(loc_name) loc_data = location_table.get(loc_name)

View File

@ -15,10 +15,10 @@ class LegacyLogic(LogicMixin):
return self.stat_upgrade_count(player) >= amount return self.stat_upgrade_count(player) >= amount
def total_stat_upgrades_count(self, player: int) -> int: def total_stat_upgrades_count(self, player: int) -> int:
return int(self.world.health_pool[player]) + \ return int(self.multiworld.health_pool[player]) + \
int(self.world.mana_pool[player]) + \ int(self.multiworld.mana_pool[player]) + \
int(self.world.attack_pool[player]) + \ int(self.multiworld.attack_pool[player]) + \
int(self.world.magic_damage_pool[player]) int(self.multiworld.magic_damage_pool[player])
def stat_upgrade_count(self: CollectionState, player: int) -> int: def stat_upgrade_count(self: CollectionState, player: int) -> int:
return self.item_count("Health Up", player) + self.item_count("Mana Up", player) + \ return self.item_count("Health Up", player) + self.item_count("Mana Up", player) + \

View File

@ -44,7 +44,7 @@ class RLWorld(World):
prefill_items: List[RLItem] = [] prefill_items: List[RLItem] = []
def setting(self, name: str): def setting(self, name: str):
return getattr(self.world, name)[self.player] return getattr(self.multiworld, name)[self.player]
def fill_slot_data(self) -> dict: def fill_slot_data(self) -> dict:
return {option_name: self.setting(option_name).value for option_name in rl_options} return {option_name: self.setting(option_name).value for option_name in rl_options}
@ -86,13 +86,13 @@ class RLWorld(World):
if self.setting("architect") == "disabled" or self.setting("architect") == "early": if self.setting("architect") == "disabled" or self.setting("architect") == "early":
continue continue
if self.setting("architect") == "start_unlocked": if self.setting("architect") == "start_unlocked":
self.world.push_precollected(self.create_item(name)) self.multiworld.push_precollected(self.create_item(name))
continue continue
# Blacksmith and Enchantress # Blacksmith and Enchantress
if name == "Blacksmith" or name == "Enchantress": if name == "Blacksmith" or name == "Enchantress":
if self.setting("vendors") == "start_unlocked": if self.setting("vendors") == "start_unlocked":
self.world.push_precollected(self.create_item(name)) self.multiworld.push_precollected(self.create_item(name))
continue continue
if self.setting("vendors") == "early": if self.setting("vendors") == "early":
continue continue
@ -195,11 +195,11 @@ class RLWorld(World):
while len(self.item_pool) + len(self.prefill_items) < total_locations: while len(self.item_pool) + len(self.prefill_items) < total_locations:
self.item_pool.append(self.create_item(self.get_filler_item_name())) self.item_pool.append(self.create_item(self.get_filler_item_name()))
self.world.itempool += self.item_pool self.multiworld.itempool += self.item_pool
def pre_fill(self) -> None: def pre_fill(self) -> None:
reachable = [loc for loc in self.world.get_reachable_locations(player=self.player) if not loc.item] reachable = [loc for loc in self.multiworld.get_reachable_locations(player=self.player) if not loc.item]
self.world.random.shuffle(reachable) self.multiworld.random.shuffle(reachable)
items = self.prefill_items.copy() items = self.prefill_items.copy()
for item in items: for item in items:
reachable.pop().place_locked_item(item) reachable.pop().place_locked_item(item)
@ -210,7 +210,7 @@ class RLWorld(World):
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
fillers = get_items_by_category("Filler") fillers = get_items_by_category("Filler")
weights = [data.weight for data in fillers.values()] weights = [data.weight for data in fillers.values()]
return self.world.random.choices([filler for filler in fillers.keys()], weights, k=1)[0] return self.multiworld.random.choices([filler for filler in fillers.keys()], weights, k=1)[0]
def create_item(self, name: str) -> RLItem: def create_item(self, name: str) -> RLItem:
data = item_table[name] data = item_table[name]
@ -221,45 +221,45 @@ class RLWorld(World):
return RLItem(name, data.classification, data.code, self.player) return RLItem(name, data.classification, data.code, self.player)
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)
def create_regions(self): def create_regions(self):
create_regions(self.world, self.player) create_regions(self.multiworld, self.player)
self._place_events() self._place_events()
def _place_events(self): def _place_events(self):
# Fountain # Fountain
self.world.get_location("Fountain Room", self.player).place_locked_item( self.multiworld.get_location("Fountain Room", self.player).place_locked_item(
self.create_event("Defeat The Fountain")) self.create_event("Defeat The Fountain"))
# Khidr / Neo Khidr # Khidr / Neo Khidr
if self.setting("khidr") == "vanilla": if self.setting("khidr") == "vanilla":
self.world.get_location("Castle Hamson Boss Room", self.player).place_locked_item( self.multiworld.get_location("Castle Hamson Boss Room", self.player).place_locked_item(
self.create_event("Defeat Khidr")) self.create_event("Defeat Khidr"))
else: else:
self.world.get_location("Castle Hamson Boss Room", self.player).place_locked_item( self.multiworld.get_location("Castle Hamson Boss Room", self.player).place_locked_item(
self.create_event("Defeat Neo Khidr")) self.create_event("Defeat Neo Khidr"))
# Alexander / Alexander IV # Alexander / Alexander IV
if self.setting("alexander") == "vanilla": if self.setting("alexander") == "vanilla":
self.world.get_location("Forest Abkhazia Boss Room", self.player).place_locked_item( self.multiworld.get_location("Forest Abkhazia Boss Room", self.player).place_locked_item(
self.create_event("Defeat Alexander")) self.create_event("Defeat Alexander"))
else: else:
self.world.get_location("Forest Abkhazia Boss Room", self.player).place_locked_item( self.multiworld.get_location("Forest Abkhazia Boss Room", self.player).place_locked_item(
self.create_event("Defeat Alexander IV")) self.create_event("Defeat Alexander IV"))
# Ponce de Leon / Ponce de Freon # Ponce de Leon / Ponce de Freon
if self.setting("leon") == "vanilla": if self.setting("leon") == "vanilla":
self.world.get_location("The Maya Boss Room", self.player).place_locked_item( self.multiworld.get_location("The Maya Boss Room", self.player).place_locked_item(
self.create_event("Defeat Ponce de Leon")) self.create_event("Defeat Ponce de Leon"))
else: else:
self.world.get_location("The Maya Boss Room", self.player).place_locked_item( self.multiworld.get_location("The Maya Boss Room", self.player).place_locked_item(
self.create_event("Defeat Ponce de Freon")) self.create_event("Defeat Ponce de Freon"))
# Herodotus / Astrodotus # Herodotus / Astrodotus
if self.setting("herodotus") == "vanilla": if self.setting("herodotus") == "vanilla":
self.world.get_location("Land of Darkness Boss Room", self.player).place_locked_item( self.multiworld.get_location("Land of Darkness Boss Room", self.player).place_locked_item(
self.create_event("Defeat Herodotus")) self.create_event("Defeat Herodotus"))
else: else:
self.world.get_location("Land of Darkness Boss Room", self.player).place_locked_item( self.multiworld.get_location("Land of Darkness Boss Room", self.player).place_locked_item(
self.create_event("Defeat Astrodotus")) self.create_event("Defeat Astrodotus"))

View File

@ -42,40 +42,40 @@ class RiskOfRainWorld(World):
def generate_early(self) -> None: def generate_early(self) -> None:
# figure out how many revivals should exist in the pool # figure out how many revivals should exist in the pool
self.total_revivals = int(self.world.total_revivals[self.player].value / 100 * self.total_revivals = int(self.multiworld.total_revivals[self.player].value / 100 *
self.world.total_locations[self.player].value) self.multiworld.total_locations[self.player].value)
def generate_basic(self) -> None: def generate_basic(self) -> None:
# shortcut for starting_inventory... The start_with_revive option lets you start with a Dio's Best Friend # shortcut for starting_inventory... The start_with_revive option lets you start with a Dio's Best Friend
if self.world.start_with_revive[self.player].value: if self.multiworld.start_with_revive[self.player].value:
self.world.push_precollected(self.world.create_item("Dio's Best Friend", self.player)) self.multiworld.push_precollected(self.multiworld.create_item("Dio's Best Friend", self.player))
# if presets are enabled generate junk_pool from the selected preset # if presets are enabled generate junk_pool from the selected preset
pool_option = self.world.item_weights[self.player].value pool_option = self.multiworld.item_weights[self.player].value
junk_pool: Dict[str, int] = {} junk_pool: Dict[str, int] = {}
if self.world.item_pool_presets[self.player]: if self.multiworld.item_pool_presets[self.player]:
# generate chaos weights if the preset is chosen # generate chaos weights if the preset is chosen
if pool_option == ItemWeights.option_chaos: if pool_option == ItemWeights.option_chaos:
for name, max_value in item_pool_weights[pool_option].items(): for name, max_value in item_pool_weights[pool_option].items():
junk_pool[name] = self.world.random.randint(0, max_value) junk_pool[name] = self.multiworld.random.randint(0, max_value)
else: else:
junk_pool = item_pool_weights[pool_option].copy() junk_pool = item_pool_weights[pool_option].copy()
else: # generate junk pool from user created presets else: # generate junk pool from user created presets
junk_pool = { junk_pool = {
"Item Scrap, Green": self.world.green_scrap[self.player].value, "Item Scrap, Green": self.multiworld.green_scrap[self.player].value,
"Item Scrap, Red": self.world.red_scrap[self.player].value, "Item Scrap, Red": self.multiworld.red_scrap[self.player].value,
"Item Scrap, Yellow": self.world.yellow_scrap[self.player].value, "Item Scrap, Yellow": self.multiworld.yellow_scrap[self.player].value,
"Item Scrap, White": self.world.white_scrap[self.player].value, "Item Scrap, White": self.multiworld.white_scrap[self.player].value,
"Common Item": self.world.common_item[self.player].value, "Common Item": self.multiworld.common_item[self.player].value,
"Uncommon Item": self.world.uncommon_item[self.player].value, "Uncommon Item": self.multiworld.uncommon_item[self.player].value,
"Legendary Item": self.world.legendary_item[self.player].value, "Legendary Item": self.multiworld.legendary_item[self.player].value,
"Boss Item": self.world.boss_item[self.player].value, "Boss Item": self.multiworld.boss_item[self.player].value,
"Lunar Item": self.world.lunar_item[self.player].value, "Lunar Item": self.multiworld.lunar_item[self.player].value,
"Equipment": self.world.equipment[self.player].value "Equipment": self.multiworld.equipment[self.player].value
} }
# remove lunar items from the pool if they're disabled in the yaml unless lunartic is rolled # remove lunar items from the pool if they're disabled in the yaml unless lunartic is rolled
if not (self.world.enable_lunar[self.player] or pool_option == ItemWeights.option_lunartic): if not (self.multiworld.enable_lunar[self.player] or pool_option == ItemWeights.option_lunartic):
junk_pool.pop("Lunar Item") junk_pool.pop("Lunar Item")
# Generate item pool # Generate item pool
@ -84,38 +84,38 @@ class RiskOfRainWorld(World):
itempool += ["Dio's Best Friend"] * self.total_revivals itempool += ["Dio's Best Friend"] * self.total_revivals
# Fill remaining items with randomly generated junk # Fill remaining items with randomly generated junk
itempool += self.world.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()), itempool += self.multiworld.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()),
k=self.world.total_locations[self.player].value - self.total_revivals) k=self.multiworld.total_locations[self.player].value - self.total_revivals)
# Convert itempool into real items # Convert itempool into real items
itempool = list(map(lambda name: self.create_item(name), itempool)) itempool = list(map(lambda name: self.create_item(name), itempool))
self.world.itempool += itempool self.multiworld.itempool += itempool
def set_rules(self) -> None: def set_rules(self) -> None:
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)
def create_regions(self) -> None: def create_regions(self) -> None:
menu = create_region(self.world, self.player, "Menu") menu = create_region(self.multiworld, self.player, "Menu")
petrichor = create_region(self.world, self.player, "Petrichor V", petrichor = create_region(self.multiworld, self.player, "Petrichor V",
[f"ItemPickup{i + 1}" for i in range(self.world.total_locations[self.player].value)]) [f"ItemPickup{i + 1}" for i in range(self.multiworld.total_locations[self.player].value)])
connection = Entrance(self.player, "Lobby", menu) connection = Entrance(self.player, "Lobby", menu)
menu.exits.append(connection) menu.exits.append(connection)
connection.connect(petrichor) connection.connect(petrichor)
self.world.regions += [menu, petrichor] self.multiworld.regions += [menu, petrichor]
create_events(self.world, self.player) create_events(self.multiworld, self.player)
def fill_slot_data(self): def fill_slot_data(self):
return { return {
"itemPickupStep": self.world.item_pickup_step[self.player].value, "itemPickupStep": self.multiworld.item_pickup_step[self.player].value,
"seed": "".join(self.world.slot_seeds[self.player].choice(string.digits) for _ in range(16)), "seed": "".join(self.multiworld.slot_seeds[self.player].choice(string.digits) for _ in range(16)),
"totalLocations": self.world.total_locations[self.player].value, "totalLocations": self.multiworld.total_locations[self.player].value,
"totalRevivals": self.world.total_revivals[self.player].value, "totalRevivals": self.multiworld.total_revivals[self.player].value,
"startWithDio": self.world.start_with_revive[self.player].value, "startWithDio": self.multiworld.start_with_revive[self.player].value,
"FinalStageDeath": self.world.final_stage_death[self.player].value "FinalStageDeath": self.multiworld.final_stage_death[self.player].value
} }
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:

View File

@ -690,7 +690,7 @@ def connect_regions(world, player, gates: typing.List[LevelGate], cannon_core_em
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None): def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None):
# Shamelessly stolen from the ROR2 definition # Shamelessly stolen from the ROR2 definition
ret = Region(name, None, name, player) ret = Region(name, None, name, player)
ret.world = world ret.multiworld = world
if locations: if locations:
for location in locations: for location in locations:
loc_id = active_locations.get(location, 0) loc_id = active_locations.get(location, 0)

View File

@ -69,15 +69,15 @@ class SA2BWorld(World):
return { return {
"ModVersion": 101, "ModVersion": 101,
"MusicMap": self.music_map, "MusicMap": self.music_map,
"MusicShuffle": self.world.music_shuffle[self.player].value, "MusicShuffle": self.multiworld.music_shuffle[self.player].value,
"RequiredRank": self.world.required_rank[self.player].value, "RequiredRank": self.multiworld.required_rank[self.player].value,
"ChaoRaceChecks": self.world.chao_race_checks[self.player].value, "ChaoRaceChecks": self.multiworld.chao_race_checks[self.player].value,
"ChaoGardenDifficulty": self.world.chao_garden_difficulty[self.player].value, "ChaoGardenDifficulty": self.multiworld.chao_garden_difficulty[self.player].value,
"DeathLink": self.world.death_link[self.player].value, "DeathLink": self.multiworld.death_link[self.player].value,
"IncludeMissions": self.world.include_missions[self.player].value, "IncludeMissions": self.multiworld.include_missions[self.player].value,
"EmblemPercentageForCannonsCore": self.world.emblem_percentage_for_cannons_core[self.player].value, "EmblemPercentageForCannonsCore": self.multiworld.emblem_percentage_for_cannons_core[self.player].value,
"NumberOfLevelGates": self.world.number_of_level_gates[self.player].value, "NumberOfLevelGates": self.multiworld.number_of_level_gates[self.player].value,
"LevelGateDistribution": self.world.level_gate_distribution[self.player].value, "LevelGateDistribution": self.multiworld.level_gate_distribution[self.player].value,
"EmblemsForCannonsCore": self.emblems_for_cannons_core, "EmblemsForCannonsCore": self.emblems_for_cannons_core,
"RegionEmblemMap": self.region_emblem_map, "RegionEmblemMap": self.region_emblem_map,
"GateCosts": self.gate_costs, "GateCosts": self.gate_costs,
@ -92,14 +92,14 @@ class SA2BWorld(World):
slot_data = self._get_slot_data() slot_data = self._get_slot_data()
slot_data["MusicMap"] = self.music_map slot_data["MusicMap"] = self.music_map
for option_name in sa2b_options: for option_name in sa2b_options:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
slot_data[option_name] = option.value slot_data[option_name] = option.value
return slot_data return slot_data
def get_levels_per_gate(self) -> list: def get_levels_per_gate(self) -> list:
levels_per_gate = list() levels_per_gate = list()
max_gate_index = self.world.number_of_level_gates[self.player] max_gate_index = self.multiworld.number_of_level_gates[self.player]
average_level_count = 30 / (max_gate_index + 1) average_level_count = 30 / (max_gate_index + 1)
levels_added = 0 levels_added = 0
@ -112,8 +112,8 @@ class SA2BWorld(World):
levels_added += 1 levels_added += 1
additional_count_iterator += 1 if additional_count_iterator < max_gate_index else -max_gate_index additional_count_iterator += 1 if additional_count_iterator < max_gate_index else -max_gate_index
if self.world.level_gate_distribution[self.player] == 0 or self.world.level_gate_distribution[self.player] == 2: if self.multiworld.level_gate_distribution[self.player] == 0 or self.multiworld.level_gate_distribution[self.player] == 2:
early_distribution = self.world.level_gate_distribution[self.player] == 0 early_distribution = self.multiworld.level_gate_distribution[self.player] == 0
levels_to_distribute = 5 levels_to_distribute = 5
gate_index_offset = 0 gate_index_offset = 0
while levels_to_distribute > 0: while levels_to_distribute > 0:
@ -134,10 +134,10 @@ class SA2BWorld(World):
return levels_per_gate return levels_per_gate
def generate_early(self): def generate_early(self):
self.gate_bosses = get_gate_bosses(self.world, self.player) self.gate_bosses = get_gate_bosses(self.multiworld, self.player)
def generate_basic(self): def generate_basic(self):
self.world.get_location(LocationName.biolizard, self.player).place_locked_item(self.create_item(ItemName.maria)) self.multiworld.get_location(LocationName.biolizard, self.player).place_locked_item(self.create_item(ItemName.maria))
itempool: typing.List[SA2BItem] = [] itempool: typing.List[SA2BItem] = []
@ -155,20 +155,20 @@ class SA2BWorld(World):
extra_junk_count = raw_emblem_count - total_emblem_count extra_junk_count = raw_emblem_count - total_emblem_count
self.emblems_for_cannons_core = math.floor( self.emblems_for_cannons_core = math.floor(
total_emblem_count * (self.world.emblem_percentage_for_cannons_core[self.player].value / 100.0)) total_emblem_count * (self.multiworld.emblem_percentage_for_cannons_core[self.player].value / 100.0))
gate_cost_mult = 1.0 gate_cost_mult = 1.0
if self.world.level_gate_costs[self.player].value == 0: if self.multiworld.level_gate_costs[self.player].value == 0:
gate_cost_mult = 0.6 gate_cost_mult = 0.6
elif self.world.level_gate_costs[self.player].value == 1: elif self.multiworld.level_gate_costs[self.player].value == 1:
gate_cost_mult = 0.8 gate_cost_mult = 0.8
shuffled_region_list = list(range(30)) shuffled_region_list = list(range(30))
emblem_requirement_list = list() emblem_requirement_list = list()
self.world.random.shuffle(shuffled_region_list) self.multiworld.random.shuffle(shuffled_region_list)
levels_per_gate = self.get_levels_per_gate() levels_per_gate = self.get_levels_per_gate()
check_for_impossible_shuffle(shuffled_region_list, math.ceil(levels_per_gate[0]), self.world) check_for_impossible_shuffle(shuffled_region_list, math.ceil(levels_per_gate[0]), self.multiworld)
levels_added_to_gate = 0 levels_added_to_gate = 0
total_levels_added = 0 total_levels_added = 0
current_gate = 0 current_gate = 0
@ -184,8 +184,8 @@ class SA2BWorld(World):
total_levels_added += 1 total_levels_added += 1
if levels_added_to_gate >= levels_per_gate[current_gate]: if levels_added_to_gate >= levels_per_gate[current_gate]:
current_gate += 1 current_gate += 1
if current_gate > self.world.number_of_level_gates[self.player].value: if current_gate > self.multiworld.number_of_level_gates[self.player].value:
current_gate = self.world.number_of_level_gates[self.player].value current_gate = self.multiworld.number_of_level_gates[self.player].value
else: else:
current_gate_emblems = max( current_gate_emblems = max(
math.floor(total_emblem_count * math.pow(total_levels_added / 30.0, 2.0) * gate_cost_mult), current_gate) math.floor(total_emblem_count * math.pow(total_levels_added / 30.0, 2.0) * gate_cost_mult), current_gate)
@ -195,60 +195,60 @@ class SA2BWorld(World):
self.region_emblem_map = dict(zip(shuffled_region_list, emblem_requirement_list)) self.region_emblem_map = dict(zip(shuffled_region_list, emblem_requirement_list))
connect_regions(self.world, self.player, gates, self.emblems_for_cannons_core, self.gate_bosses) connect_regions(self.multiworld, self.player, gates, self.emblems_for_cannons_core, self.gate_bosses)
max_required_emblems = max(max(emblem_requirement_list), self.emblems_for_cannons_core) max_required_emblems = max(max(emblem_requirement_list), self.emblems_for_cannons_core)
itempool += [self.create_item(ItemName.emblem) for _ in range(max_required_emblems)] itempool += [self.create_item(ItemName.emblem) for _ in range(max_required_emblems)]
non_required_emblems = (total_emblem_count - max_required_emblems) non_required_emblems = (total_emblem_count - max_required_emblems)
junk_count = math.floor(non_required_emblems * (self.world.junk_fill_percentage[self.player].value / 100.0)) junk_count = math.floor(non_required_emblems * (self.multiworld.junk_fill_percentage[self.player].value / 100.0))
itempool += [self.create_item(ItemName.emblem, True) for _ in range(non_required_emblems - junk_count)] itempool += [self.create_item(ItemName.emblem, True) for _ in range(non_required_emblems - junk_count)]
# Carve Traps out of junk_count # Carve Traps out of junk_count
trap_weights = [] trap_weights = []
trap_weights += ([ItemName.omochao_trap] * self.world.omochao_trap_weight[self.player].value) trap_weights += ([ItemName.omochao_trap] * self.multiworld.omochao_trap_weight[self.player].value)
trap_weights += ([ItemName.timestop_trap] * self.world.timestop_trap_weight[self.player].value) trap_weights += ([ItemName.timestop_trap] * self.multiworld.timestop_trap_weight[self.player].value)
trap_weights += ([ItemName.confuse_trap] * self.world.confusion_trap_weight[self.player].value) trap_weights += ([ItemName.confuse_trap] * self.multiworld.confusion_trap_weight[self.player].value)
trap_weights += ([ItemName.tiny_trap] * self.world.tiny_trap_weight[self.player].value) trap_weights += ([ItemName.tiny_trap] * self.multiworld.tiny_trap_weight[self.player].value)
junk_count += extra_junk_count junk_count += extra_junk_count
trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.world.trap_fill_percentage[self.player].value / 100.0)) trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.multiworld.trap_fill_percentage[self.player].value / 100.0))
junk_count -= trap_count junk_count -= trap_count
junk_pool = [] junk_pool = []
junk_keys = list(junk_table.keys()) junk_keys = list(junk_table.keys())
for i in range(junk_count): for i in range(junk_count):
junk_item = self.world.random.choice(junk_keys) junk_item = self.multiworld.random.choice(junk_keys)
junk_pool.append(self.create_item(junk_item)) junk_pool.append(self.create_item(junk_item))
itempool += junk_pool itempool += junk_pool
trap_pool = [] trap_pool = []
for i in range(trap_count): for i in range(trap_count):
trap_item = self.world.random.choice(trap_weights) trap_item = self.multiworld.random.choice(trap_weights)
trap_pool.append(self.create_item(trap_item)) trap_pool.append(self.create_item(trap_item))
itempool += trap_pool itempool += trap_pool
self.world.itempool += itempool self.multiworld.itempool += itempool
# Music Shuffle # Music Shuffle
if self.world.music_shuffle[self.player] == "levels": if self.multiworld.music_shuffle[self.player] == "levels":
musiclist_o = list(range(0, 47)) musiclist_o = list(range(0, 47))
musiclist_s = musiclist_o.copy() musiclist_s = musiclist_o.copy()
self.world.random.shuffle(musiclist_s) self.multiworld.random.shuffle(musiclist_s)
self.music_map = dict(zip(musiclist_o, musiclist_s)) self.music_map = dict(zip(musiclist_o, musiclist_s))
elif self.world.music_shuffle[self.player] == "full": elif self.multiworld.music_shuffle[self.player] == "full":
musiclist_o = list(range(0, 78)) musiclist_o = list(range(0, 78))
musiclist_s = musiclist_o.copy() musiclist_s = musiclist_o.copy()
self.world.random.shuffle(musiclist_s) self.multiworld.random.shuffle(musiclist_s)
self.music_map = dict(zip(musiclist_o, musiclist_s)) self.music_map = dict(zip(musiclist_o, musiclist_s))
else: else:
self.music_map = dict() self.music_map = dict()
def create_regions(self): def create_regions(self):
self.location_table = setup_locations(self.world, self.player) self.location_table = setup_locations(self.multiworld, self.player)
create_regions(self.world, self.player, self.location_table) create_regions(self.multiworld, self.player, self.location_table)
def create_item(self, name: str, force_non_progression=False) -> Item: def create_item(self, name: str, force_non_progression=False) -> Item:
data = item_table[name] data = item_table[name]
@ -269,12 +269,12 @@ class SA2BWorld(World):
return created_item return created_item
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player, self.gate_bosses) set_rules(self.multiworld, self.player, self.gate_bosses)
def write_spoiler(self, spoiler_handle: typing.TextIO): def write_spoiler(self, spoiler_handle: typing.TextIO):
spoiler_handle.write("\n") spoiler_handle.write("\n")
header_text = "Sonic Adventure 2 Bosses for {}:\n" header_text = "Sonic Adventure 2 Bosses for {}:\n"
header_text = header_text.format(self.world.player_name[self.player]) header_text = header_text.format(self.multiworld.player_name[self.player])
spoiler_handle.write(header_text) spoiler_handle.write(header_text)
for x in range(len(self.gate_bosses.values())): for x in range(len(self.gate_bosses.values())):
text = "Gate {0} Boss: {1}\n" text = "Gate {0} Boss: {1}\n"

View File

@ -236,7 +236,7 @@ def create_location(player: int, location_data: LocationData, region: Region,
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]],
location_cache: List[Location], name: str) -> Region: location_cache: List[Location], name: str) -> Region:
region = Region(name, RegionType.Generic, name, player) region = Region(name, RegionType.Generic, name, player)
region.world = world region.multiworld = world
if name in locations_per_region: if name in locations_per_region:
for location_data in locations_per_region[name]: for location_data in locations_per_region[name]:

View File

@ -59,31 +59,31 @@ class SC2WoLWorld(World):
def create_regions(self): def create_regions(self):
self.mission_req_table, self.final_mission_id, self.victory_item = create_regions( self.mission_req_table, self.final_mission_id, self.victory_item = create_regions(
self.world, self.player, get_locations(self.world, self.player), self.location_cache self.multiworld, self.player, get_locations(self.multiworld, self.player), self.location_cache
) )
def generate_basic(self): def generate_basic(self):
excluded_items = get_excluded_items(self, self.world, self.player) excluded_items = get_excluded_items(self, self.multiworld, self.player)
starter_items = assign_starter_items(self.world, self.player, excluded_items, self.locked_locations) starter_items = assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations)
pool = get_item_pool(self.world, self.player, self.mission_req_table, starter_items, excluded_items, self.location_cache) pool = get_item_pool(self.multiworld, self.player, self.mission_req_table, starter_items, excluded_items, self.location_cache)
fill_item_pool_with_dummy_items(self, self.world, self.player, self.locked_locations, self.location_cache, pool) fill_item_pool_with_dummy_items(self, self.multiworld, self.player, self.locked_locations, self.location_cache, pool)
self.world.itempool += pool self.multiworld.itempool += pool
def set_rules(self): def set_rules(self):
setup_events(self.world, self.player, self.locked_locations, self.location_cache) setup_events(self.multiworld, self.player, self.locked_locations, self.location_cache)
self.world.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player) self.multiworld.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player)
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return self.world.random.choice(filler_items) return self.multiworld.random.choice(filler_items)
def fill_slot_data(self): def fill_slot_data(self):
slot_data = {} slot_data = {}
for option_name in sc2wol_options: for option_name in sc2wol_options:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
if type(option.value) in {str, int}: if type(option.value) in {str, int}:
slot_data[option_name] = int(option.value) slot_data[option_name] = int(option.value)
slot_req_table = {} slot_req_table = {}

View File

@ -111,27 +111,27 @@ class SMWorld(World):
def generate_early(self): def generate_early(self):
Logic.factory('vanilla') Logic.factory('vanilla')
self.variaRando = VariaRandomizer(self.world, get_base_rom_path(), self.player) self.variaRando = VariaRandomizer(self.multiworld, get_base_rom_path(), self.player)
self.world.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty) self.multiworld.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty)
# keeps Nothing items local so no player will ever pickup Nothing # keeps Nothing items local so no player will ever pickup Nothing
# doing so reduces contribution of this world to the Multiworld the more Nothing there is though # doing so reduces contribution of this world to the Multiworld the more Nothing there is though
self.world.local_items[self.player].value.add('Nothing') self.multiworld.local_items[self.player].value.add('Nothing')
self.world.local_items[self.player].value.add('No Energy') self.multiworld.local_items[self.player].value.add('No Energy')
if (self.variaRando.args.morphPlacement == "early"): if (self.variaRando.args.morphPlacement == "early"):
self.world.local_items[self.player].value.add('Morph') self.multiworld.local_items[self.player].value.add('Morph')
self.remote_items = self.world.remote_items[self.player] self.remote_items = self.multiworld.remote_items[self.player]
if (len(self.variaRando.randoExec.setup.restrictedLocs) > 0): if (len(self.variaRando.randoExec.setup.restrictedLocs) > 0):
self.world.accessibility[self.player] = self.world.accessibility[self.player].from_text("minimal") self.multiworld.accessibility[self.player] = self.multiworld.accessibility[self.player].from_text("minimal")
logger.warning(f"accessibility forced to 'minimal' for player {self.world.get_player_name(self.player)} because of 'fun' settings") logger.warning(f"accessibility forced to 'minimal' for player {self.multiworld.get_player_name(self.player)} because of 'fun' settings")
def generate_basic(self): def generate_basic(self):
itemPool = self.variaRando.container.itemPool itemPool = self.variaRando.container.itemPool
self.startItems = [variaItem for item in self.world.precollected_items[self.player] for variaItem in ItemManager.Items.values() if variaItem.Name == item.name] self.startItems = [variaItem for item in self.multiworld.precollected_items[self.player] for variaItem in ItemManager.Items.values() if variaItem.Name == item.name]
if self.world.start_inventory_removes_from_pool[self.player]: if self.multiworld.start_inventory_removes_from_pool[self.player]:
for item in self.startItems: for item in self.startItems:
if (item in itemPool): if (item in itemPool):
itemPool.remove(item) itemPool.remove(item)
@ -175,31 +175,31 @@ class SMWorld(World):
else: else:
pool.append(smitem) pool.append(smitem)
self.world.itempool += pool self.multiworld.itempool += pool
for (location, item) in self.locked_items.items(): for (location, item) in self.locked_items.items():
self.world.get_location(location, self.player).place_locked_item(item) self.multiworld.get_location(location, self.player).place_locked_item(item)
self.world.get_location(location, self.player).address = None self.multiworld.get_location(location, self.player).address = None
startAP = self.world.get_entrance('StartAP', self.player) startAP = self.multiworld.get_entrance('StartAP', self.player)
startAP.connect(self.world.get_region(self.variaRando.args.startLocation, self.player)) startAP.connect(self.multiworld.get_region(self.variaRando.args.startLocation, self.player))
for src, dest in self.variaRando.randoExec.areaGraph.InterAreaTransitions: for src, dest in self.variaRando.randoExec.areaGraph.InterAreaTransitions:
src_region = self.world.get_region(src.Name, self.player) src_region = self.multiworld.get_region(src.Name, self.player)
dest_region = self.world.get_region(dest.Name, self.player) dest_region = self.multiworld.get_region(dest.Name, self.player)
if ((src.Name + "->" + dest.Name, self.player) not in self.world._entrance_cache): if ((src.Name + "->" + dest.Name, self.player) not in self.multiworld._entrance_cache):
src_region.exits.append(Entrance(self.player, src.Name + "->" + dest.Name, src_region)) src_region.exits.append(Entrance(self.player, src.Name + "->" + dest.Name, src_region))
srcDestEntrance = self.world.get_entrance(src.Name + "->" + dest.Name, self.player) srcDestEntrance = self.multiworld.get_entrance(src.Name + "->" + dest.Name, self.player)
srcDestEntrance.connect(dest_region) srcDestEntrance.connect(dest_region)
add_entrance_rule(self.world.get_entrance(src.Name + "->" + dest.Name, self.player), self.player, getAccessPoint(src.Name).traverse) add_entrance_rule(self.multiworld.get_entrance(src.Name + "->" + dest.Name, self.player), self.player, getAccessPoint(src.Name).traverse)
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)
def create_regions(self): def create_regions(self):
create_locations(self, self.player) create_locations(self, self.player)
create_regions(self, self.world, self.player) create_regions(self, self.multiworld, self.player)
def getWordArray(self, w): # little-endian convert a 16-bit number to an array of numbers <= 255 each def getWordArray(self, w): # little-endian convert a 16-bit number to an array of numbers <= 255 each
return [w & 0x00FF, (w & 0xFF00) >> 8] return [w & 0x00FF, (w & 0xFF00) >> 8]
@ -289,7 +289,7 @@ class SMWorld(World):
self.playerIDMap = {} self.playerIDMap = {}
playerIDCount = 0 # 0 is for "Archipelago" server; highest possible = 200 (201 entries) playerIDCount = 0 # 0 is for "Archipelago" server; highest possible = 200 (201 entries)
vanillaItemTypesCount = 21 vanillaItemTypesCount = 21
for itemLoc in self.world.get_locations(): for itemLoc in self.multiworld.get_locations():
if itemLoc.player == self.player and locationsDict[itemLoc.name].Id != None: if itemLoc.player == self.player and locationsDict[itemLoc.name].Id != None:
# this SM world can find this item: write full item data to tables and assign player data for writing # this SM world can find this item: write full item data to tables and assign player data for writing
romPlayerID = itemLoc.item.player if itemLoc.item.player <= ROM_PLAYER_LIMIT else 0 romPlayerID = itemLoc.item.player if itemLoc.item.player <= ROM_PLAYER_LIMIT else 0
@ -345,7 +345,7 @@ class SMWorld(World):
deathLink = [{"sym": symbols["config_deathlink"], deathLink = [{"sym": symbols["config_deathlink"],
"offset": 0, "offset": 0,
"values": [self.world.death_link[self.player].value]}] "values": [self.multiworld.death_link[self.player].value]}]
remoteItem = [{"sym": symbols["config_remote_items"], remoteItem = [{"sym": symbols["config_remote_items"],
"offset": 0, "offset": 0,
"values": self.getWordArray(0b001 + (0b010 if self.remote_items else 0b000))}] "values": self.getWordArray(0b001 + (0b010 if self.remote_items else 0b000))}]
@ -364,7 +364,7 @@ class SMWorld(World):
for key,value in self.playerIDMap.items(): for key,value in self.playerIDMap.items():
playerNames.append({"sym": symbols["rando_player_table"], playerNames.append({"sym": symbols["rando_player_table"],
"offset": value * 16, "offset": value * 16,
"values": self.world.player_name[key][:16].upper().center(16).encode()}) "values": self.multiworld.player_name[key][:16].upper().center(16).encode()})
playerNameIDMap.append({"sym": symbols["rando_player_id_table"], playerNameIDMap.append({"sym": symbols["rando_player_id_table"],
"offset": value * 2, "offset": value * 2,
"values": self.getWordArray(key)}) "values": self.getWordArray(key)})
@ -399,7 +399,7 @@ class SMWorld(World):
# set rom name # set rom name
# 21 bytes # 21 bytes
from Main import __version__ from Main import __version__
self.romName = bytearray(f'SM{__version__.replace(".", "")[0:3]}_{self.player}_{self.world.seed:11}', 'utf8')[:21] self.romName = bytearray(f'SM{__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed:11}', 'utf8')[:21]
self.romName.extend([0] * (21 - len(self.romName))) self.romName.extend([0] * (21 - len(self.romName)))
# clients should read from 0x7FC0, the location of the rom title in the SNES header. # clients should read from 0x7FC0, the location of the rom title in the SNES header.
# duplicative ROM name at 0x1C4F00 is still written here for now, since people with archipelago pre-0.3.0 client installed will still be depending on this location for connecting to SM # duplicative ROM name at 0x1C4F00 is still written here for now, since people with archipelago pre-0.3.0 client installed will still be depending on this location for connecting to SM
@ -493,12 +493,12 @@ class SMWorld(World):
if isinstance(itemLoc.item, SMItem) and itemLoc.item.type in ItemManager.Items else if isinstance(itemLoc.item, SMItem) and itemLoc.item.type in ItemManager.Items else
'ArchipelagoItem'], 'ArchipelagoItem'],
locationsDict[itemLoc.name], True) locationsDict[itemLoc.name], True)
for itemLoc in self.world.get_locations() if itemLoc.player == self.player for itemLoc in self.multiworld.get_locations() if itemLoc.player == self.player
] ]
romPatcher.writeItemsLocs(itemLocs) romPatcher.writeItemsLocs(itemLocs)
itemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.world.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.world.get_locations() if itemLoc.item.player == self.player] itemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.multiworld.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.multiworld.get_locations() if itemLoc.item.player == self.player]
progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.world.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.world.get_locations() if itemLoc.item.player == self.player and itemLoc.item.advancement == True] progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.multiworld.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.multiworld.get_locations() if itemLoc.item.player == self.player and itemLoc.item.advancement == True]
# progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type if itemLoc.item.type in ItemManager.Items else 'ArchipelagoItem'], locationsDict[itemLoc.name], True) for itemLoc in self.world.get_locations() if itemLoc.player == self.player and itemLoc.item.player == self.player and itemLoc.item.advancement == True] # progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type if itemLoc.item.type in ItemManager.Items else 'ArchipelagoItem'], locationsDict[itemLoc.name], True) for itemLoc in self.world.get_locations() if itemLoc.player == self.player and itemLoc.item.player == self.player and itemLoc.item.advancement == True]
# romPatcher.writeSplitLocs(self.variaRando.args.majorsSplit, itemLocs, progItemLocs) # romPatcher.writeSplitLocs(self.variaRando.args.majorsSplit, itemLocs, progItemLocs)
@ -506,7 +506,7 @@ class SMWorld(World):
romPatcher.writeRandoSettings(self.variaRando.randoExec.randoSettings, itemLocs) romPatcher.writeRandoSettings(self.variaRando.randoExec.randoSettings, itemLocs)
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
outfilebase = self.world.get_out_file_name_base(self.player) outfilebase = self.multiworld.get_out_file_name_base(self.player)
outputFilename = os.path.join(output_directory, f"{outfilebase}.sfc") outputFilename = os.path.join(output_directory, f"{outfilebase}.sfc")
try: try:
@ -516,8 +516,8 @@ class SMWorld(World):
except: except:
raise raise
else: else:
patch = SMDeltaPatch(os.path.splitext(outputFilename)[0]+SMDeltaPatch.patch_file_ending, player=self.player, patch = SMDeltaPatch(os.path.splitext(outputFilename)[0] + SMDeltaPatch.patch_file_ending, player=self.player,
player_name=self.world.player_name[self.player], patched_path=outputFilename) player_name=self.multiworld.player_name[self.player], patched_path=outputFilename)
patch.write() patch.write()
finally: finally:
if os.path.exists(outputFilename): if os.path.exists(outputFilename):
@ -560,13 +560,13 @@ class SMWorld(World):
# we skip in case of error, so that the original error in the output thread is the one that gets raised # we skip in case of error, so that the original error in the output thread is the one that gets raised
if rom_name: if rom_name:
new_name = base64.b64encode(bytes(self.rom_name)).decode() new_name = base64.b64encode(bytes(self.rom_name)).decode()
multidata["connect_names"][new_name] = multidata["connect_names"][self.world.player_name[self.player]] multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
def fill_slot_data(self): def fill_slot_data(self):
slot_data = {} slot_data = {}
if not self.world.is_race: if not self.multiworld.is_race:
for option_name in self.option_definitions: for option_name in self.option_definitions:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
slot_data[option_name] = option.value slot_data[option_name] = option.value
slot_data["Preset"] = { "Knows": {}, slot_data["Preset"] = { "Knows": {},
@ -606,11 +606,11 @@ class SMWorld(World):
player=self.player) player=self.player)
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
if self.world.random.randint(0, 100) < self.world.minor_qty[self.player].value: if self.multiworld.random.randint(0, 100) < self.multiworld.minor_qty[self.player].value:
power_bombs = self.world.power_bomb_qty[self.player].value power_bombs = self.multiworld.power_bomb_qty[self.player].value
missiles = self.world.missile_qty[self.player].value missiles = self.multiworld.missile_qty[self.player].value
super_missiles = self.world.super_qty[self.player].value super_missiles = self.multiworld.super_qty[self.player].value
roll = self.world.random.randint(1, power_bombs + missiles + super_missiles) roll = self.multiworld.random.randint(1, power_bombs + missiles + super_missiles)
if roll <= power_bombs: if roll <= power_bombs:
return "Power Bomb" return "Power Bomb"
elif roll <= power_bombs + missiles: elif roll <= power_bombs + missiles:
@ -621,20 +621,20 @@ class SMWorld(World):
return "Nothing" return "Nothing"
def pre_fill(self): def pre_fill(self):
if (self.variaRando.args.morphPlacement == "early") and next((item for item in self.world.itempool if item.player == self.player and item.name == "Morph Ball"), False): if (self.variaRando.args.morphPlacement == "early") and next((item for item in self.multiworld.itempool if item.player == self.player and item.name == "Morph Ball"), False):
viable = [] viable = []
for location in self.world.get_locations(): for location in self.multiworld.get_locations():
if location.player == self.player \ if location.player == self.player \
and location.item is None \ and location.item is None \
and location.can_reach(self.world.state): and location.can_reach(self.multiworld.state):
viable.append(location) viable.append(location)
self.world.random.shuffle(viable) self.multiworld.random.shuffle(viable)
key = self.world.create_item("Morph Ball", self.player) key = self.multiworld.create_item("Morph Ball", self.player)
loc = viable.pop() loc = viable.pop()
loc.place_locked_item(key) loc.place_locked_item(key)
self.world.itempool[:] = [item for item in self.world.itempool if self.multiworld.itempool[:] = [item for item in self.multiworld.itempool if
item.player != self.player or item.player != self.player or
item.name != "Morph Ball"] item.name != "Morph Ball"]
if len(self.NothingPool) > 0: if len(self.NothingPool) > 0:
nonChozoLoc = [] nonChozoLoc = []
@ -647,8 +647,8 @@ class SMWorld(World):
else: else:
nonChozoLoc.append(loc) nonChozoLoc.append(loc)
self.world.random.shuffle(nonChozoLoc) self.multiworld.random.shuffle(nonChozoLoc)
self.world.random.shuffle(chozoLoc) self.multiworld.random.shuffle(chozoLoc)
missingCount = len(self.NothingPool) - len(nonChozoLoc) missingCount = len(self.NothingPool) - len(nonChozoLoc)
locations = nonChozoLoc locations = nonChozoLoc
if (missingCount > 0): if (missingCount > 0):
@ -677,17 +677,17 @@ class SMWorld(World):
break break
def write_spoiler(self, spoiler_handle: TextIO): def write_spoiler(self, spoiler_handle: TextIO):
if self.world.area_randomization[self.player].value != 0: if self.multiworld.area_randomization[self.player].value != 0:
spoiler_handle.write('\n\nArea Transitions:\n\n') spoiler_handle.write('\n\nArea Transitions:\n\n')
spoiler_handle.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_name(self.player)}: ' spoiler_handle.write('\n'.join(['%s%s %s %s' % (f'{self.multiworld.get_player_name(self.player)}: '
if self.world.players > 1 else '', src.Name, if self.multiworld.players > 1 else '', src.Name,
'<=>', '<=>',
dest.Name) for src, dest in self.variaRando.randoExec.areaGraph.InterAreaTransitions if not src.Boss])) dest.Name) for src, dest in self.variaRando.randoExec.areaGraph.InterAreaTransitions if not src.Boss]))
if self.world.boss_randomization[self.player].value != 0: if self.multiworld.boss_randomization[self.player].value != 0:
spoiler_handle.write('\n\nBoss Transitions:\n\n') spoiler_handle.write('\n\nBoss Transitions:\n\n')
spoiler_handle.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_name(self.player)}: ' spoiler_handle.write('\n'.join(['%s%s %s %s' % (f'{self.multiworld.get_player_name(self.player)}: '
if self.world.players > 1 else '', src.Name, if self.multiworld.players > 1 else '', src.Name,
'<=>', '<=>',
dest.Name) for src, dest in self.variaRando.randoExec.areaGraph.InterAreaTransitions if src.Boss])) dest.Name) for src, dest in self.variaRando.randoExec.areaGraph.InterAreaTransitions if src.Boss]))
@ -698,7 +698,7 @@ def create_locations(self, player: int):
def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None): def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None):
ret = Region(name, RegionType.LightWorld, name, player) ret = Region(name, RegionType.LightWorld, name, player)
ret.world = world ret.multiworld = world
if locations: if locations:
for loc in locations: for loc in locations:
location = self.locations[loc] location = self.locations[loc]
@ -720,7 +720,7 @@ class SMLocation(Location):
return self.always_allow(state, item) or (self.item_rule(item) and (not check_access or (self.can_reach(state) and self.can_comeback(state, item)))) return self.always_allow(state, item) or (self.item_rule(item) and (not check_access or (self.can_reach(state) and self.can_comeback(state, item))))
def can_comeback(self, state: CollectionState, item: Item): def can_comeback(self, state: CollectionState, item: Item):
randoExec = state.world.worlds[self.player].variaRando.randoExec randoExec = state.multiworld.worlds[self.player].variaRando.randoExec
for key in locationsDict[self.name].AccessFrom.keys(): for key in locationsDict[self.name].AccessFrom.keys():
if (randoExec.areaGraph.canAccessList( state.smbm[self.player], if (randoExec.areaGraph.canAccessList( state.smbm[self.player],
key, key,

View File

@ -43,18 +43,18 @@ class SM64World(World):
option_definitions = sm64_options option_definitions = sm64_options
def generate_early(self): def generate_early(self):
self.topology_present = self.world.AreaRandomizer[self.player].value self.topology_present = self.multiworld.AreaRandomizer[self.player].value
def create_regions(self): def create_regions(self):
create_regions(self.world,self.player) create_regions(self.multiworld, self.player)
def set_rules(self): def set_rules(self):
self.area_connections = {} self.area_connections = {}
set_rules(self.world, self.player, self.area_connections) set_rules(self.multiworld, self.player, self.area_connections)
if self.topology_present: if self.topology_present:
# Write area_connections to spoiler log # Write area_connections to spoiler log
for entrance, destination in self.area_connections.items(): for entrance, destination in self.area_connections.items():
self.world.spoiler.set_entrance( self.multiworld.spoiler.set_entrance(
sm64_internalloc_to_string[entrance] + " Entrance", sm64_internalloc_to_string[entrance] + " Entrance",
sm64_internalloc_to_string[destination], sm64_internalloc_to_string[destination],
'entrance', self.player) 'entrance', self.player)
@ -72,74 +72,74 @@ class SM64World(World):
return item return item
def generate_basic(self): def generate_basic(self):
starcount = self.world.AmountOfStars[self.player].value starcount = self.multiworld.AmountOfStars[self.player].value
if (not self.world.EnableCoinStars[self.player].value): if (not self.multiworld.EnableCoinStars[self.player].value):
starcount = max(35,self.world.AmountOfStars[self.player].value-15) starcount = max(35,self.multiworld.AmountOfStars[self.player].value-15)
starcount = max(starcount, self.world.FirstBowserStarDoorCost[self.player].value, starcount = max(starcount, self.multiworld.FirstBowserStarDoorCost[self.player].value,
self.world.BasementStarDoorCost[self.player].value, self.world.SecondFloorStarDoorCost[self.player].value, self.multiworld.BasementStarDoorCost[self.player].value, self.multiworld.SecondFloorStarDoorCost[self.player].value,
self.world.MIPS1Cost[self.player].value, self.world.MIPS2Cost[self.player].value, self.multiworld.MIPS1Cost[self.player].value, self.multiworld.MIPS2Cost[self.player].value,
self.world.StarsToFinish[self.player].value) self.multiworld.StarsToFinish[self.player].value)
self.world.itempool += [self.create_item("Power Star") for i in range(0,starcount)] self.multiworld.itempool += [self.create_item("Power Star") for i in range(0,starcount)]
self.world.itempool += [self.create_item("1Up Mushroom") for i in range(starcount,120 - (15 if not self.world.EnableCoinStars[self.player].value else 0))] self.multiworld.itempool += [self.create_item("1Up Mushroom") for i in range(starcount,120 - (15 if not self.multiworld.EnableCoinStars[self.player].value else 0))]
if (not self.world.ProgressiveKeys[self.player].value): if (not self.multiworld.ProgressiveKeys[self.player].value):
key1 = self.create_item("Basement Key") key1 = self.create_item("Basement Key")
key2 = self.create_item("Second Floor Key") key2 = self.create_item("Second Floor Key")
self.world.itempool += [key1,key2] self.multiworld.itempool += [key1, key2]
else: else:
self.world.itempool += [self.create_item("Progressive Key") for i in range(0,2)] self.multiworld.itempool += [self.create_item("Progressive Key") for i in range(0,2)]
wingcap = self.create_item("Wing Cap") wingcap = self.create_item("Wing Cap")
metalcap = self.create_item("Metal Cap") metalcap = self.create_item("Metal Cap")
vanishcap = self.create_item("Vanish Cap") vanishcap = self.create_item("Vanish Cap")
self.world.itempool += [wingcap,metalcap,vanishcap] self.multiworld.itempool += [wingcap, metalcap, vanishcap]
if (self.world.BuddyChecks[self.player].value): if (self.multiworld.BuddyChecks[self.player].value):
self.world.itempool += [self.create_item(name) for name, id in cannon_item_table.items()] self.multiworld.itempool += [self.create_item(name) for name, id in cannon_item_table.items()]
else: else:
self.world.get_location("BoB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock BoB")) self.multiworld.get_location("BoB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock BoB"))
self.world.get_location("WF: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock WF")) self.multiworld.get_location("WF: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock WF"))
self.world.get_location("JRB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock JRB")) self.multiworld.get_location("JRB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock JRB"))
self.world.get_location("CCM: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock CCM")) self.multiworld.get_location("CCM: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock CCM"))
self.world.get_location("SSL: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock SSL")) self.multiworld.get_location("SSL: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock SSL"))
self.world.get_location("SL: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock SL")) self.multiworld.get_location("SL: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock SL"))
self.world.get_location("WDW: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock WDW")) self.multiworld.get_location("WDW: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock WDW"))
self.world.get_location("TTM: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock TTM")) self.multiworld.get_location("TTM: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock TTM"))
self.world.get_location("THI: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock THI")) self.multiworld.get_location("THI: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock THI"))
self.world.get_location("RR: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock RR")) self.multiworld.get_location("RR: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock RR"))
if (self.world.ExclamationBoxes[self.player].value > 0): if (self.multiworld.ExclamationBoxes[self.player].value > 0):
self.world.itempool += [self.create_item("1Up Mushroom") for i in range(0,29)] self.multiworld.itempool += [self.create_item("1Up Mushroom") for i in range(0,29)]
else: else:
self.world.get_location("CCM: 1Up Block Near Snowman", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("CCM: 1Up Block Near Snowman", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("CCM: 1Up Block Ice Pillar", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("CCM: 1Up Block Ice Pillar", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("CCM: 1Up Block Secret Slide", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("CCM: 1Up Block Secret Slide", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("BBH: 1Up Block Top of Mansion", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("BBH: 1Up Block Top of Mansion", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("HMC: 1Up Block above Pit", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("HMC: 1Up Block above Pit", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("HMC: 1Up Block Past Rolling Rocks", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("HMC: 1Up Block Past Rolling Rocks", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("SSL: 1Up Block Outside Pyramid", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("SSL: 1Up Block Outside Pyramid", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("SSL: 1Up Block Pyramid Left Path", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("SSL: 1Up Block Pyramid Left Path", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("SSL: 1Up Block Pyramid Back", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("SSL: 1Up Block Pyramid Back", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("SL: 1Up Block Near Moneybags", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("SL: 1Up Block Near Moneybags", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("SL: 1Up Block inside Igloo", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("SL: 1Up Block inside Igloo", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("WDW: 1Up Block in Downtown", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("WDW: 1Up Block in Downtown", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("TTM: 1Up Block on Red Mushroom", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("TTM: 1Up Block on Red Mushroom", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("THI: 1Up Block THI Small near Start", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("THI: 1Up Block THI Small near Start", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("THI: 1Up Block THI Large near Start", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("THI: 1Up Block THI Large near Start", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("THI: 1Up Block Windy Area", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("THI: 1Up Block Windy Area", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("TTC: 1Up Block Midway Up", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("TTC: 1Up Block Midway Up", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("TTC: 1Up Block at the Top", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("TTC: 1Up Block at the Top", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("RR: 1Up Block Top of Red Coin Maze", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("RR: 1Up Block Top of Red Coin Maze", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("RR: 1Up Block Under Fly Guy", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("RR: 1Up Block Under Fly Guy", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("RR: 1Up Block On House in the Sky", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("RR: 1Up Block On House in the Sky", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("Bowser in the Dark World 1Up Block on Tower", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("Bowser in the Dark World 1Up Block on Tower", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("Bowser in the Dark World 1Up Block near Goombas", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("Bowser in the Dark World 1Up Block near Goombas", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("Cavern of the Metal Cap 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("Cavern of the Metal Cap 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("Vanish Cap Under the Moat 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("Vanish Cap Under the Moat 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("Bowser in the Fire Sea 1Up Block Swaying Stairs", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("Bowser in the Fire Sea 1Up Block Swaying Stairs", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("Bowser in the Fire Sea 1Up Block Near Poles", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("Bowser in the Fire Sea 1Up Block Near Poles", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("Wing Mario Over the Rainbow 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("Wing Mario Over the Rainbow 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.world.get_location("Bowser in the Sky 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom")) self.multiworld.get_location("Bowser in the Sky 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom"))
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return "1Up Mushroom" return "1Up Mushroom"
@ -147,21 +147,21 @@ class SM64World(World):
def fill_slot_data(self): def fill_slot_data(self):
return { return {
"AreaRando": self.area_connections, "AreaRando": self.area_connections,
"FirstBowserDoorCost": self.world.FirstBowserStarDoorCost[self.player].value, "FirstBowserDoorCost": self.multiworld.FirstBowserStarDoorCost[self.player].value,
"BasementDoorCost": self.world.BasementStarDoorCost[self.player].value, "BasementDoorCost": self.multiworld.BasementStarDoorCost[self.player].value,
"SecondFloorDoorCost": self.world.SecondFloorStarDoorCost[self.player].value, "SecondFloorDoorCost": self.multiworld.SecondFloorStarDoorCost[self.player].value,
"MIPS1Cost": self.world.MIPS1Cost[self.player].value, "MIPS1Cost": self.multiworld.MIPS1Cost[self.player].value,
"MIPS2Cost": self.world.MIPS2Cost[self.player].value, "MIPS2Cost": self.multiworld.MIPS2Cost[self.player].value,
"StarsToFinish": self.world.StarsToFinish[self.player].value, "StarsToFinish": self.multiworld.StarsToFinish[self.player].value,
"DeathLink": self.world.death_link[self.player].value, "DeathLink": self.multiworld.death_link[self.player].value,
} }
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
if self.world.players != 1: if self.multiworld.players != 1:
return return
data = { data = {
"slot_data": self.fill_slot_data(), "slot_data": self.fill_slot_data(),
"location_to_item": {self.location_name_to_id[i.name] : item_table[i.item.name] for i in self.world.get_locations()}, "location_to_item": {self.location_name_to_id[i.name] : item_table[i.item.name] for i in self.multiworld.get_locations()},
"data_package": { "data_package": {
"data": { "data": {
"games": { "games": {
@ -173,7 +173,7 @@ class SM64World(World):
} }
} }
} }
filename = f"{self.world.get_out_file_name_base(self.player)}.apsm64ex" filename = f"{self.multiworld.get_out_file_name_base(self.player)}.apsm64ex"
with open(os.path.join(output_directory, filename), 'w') as f: with open(os.path.join(output_directory, filename), 'w') as f:
json.dump(data, f) json.dump(data, f)
@ -182,7 +182,7 @@ class SM64World(World):
er_hint_data = {} er_hint_data = {}
for entrance, destination in self.area_connections.items(): for entrance, destination in self.area_connections.items():
regionid = sm64_internalloc_to_regionid[destination] regionid = sm64_internalloc_to_regionid[destination]
region = self.world.get_region(sm64courses[regionid], self.player) region = self.multiworld.get_region(sm64courses[regionid], self.player)
for location in region.locations: for location in region.locations:
er_hint_data[location.address] = sm64_internalloc_to_string[entrance] er_hint_data[location.address] = sm64_internalloc_to_string[entrance]
multidata['er_hint_data'][self.player] = er_hint_data multidata['er_hint_data'][self.player] = er_hint_data

View File

@ -62,14 +62,14 @@ class SMWWorld(World):
def _get_slot_data(self): def _get_slot_data(self):
return { return {
#"death_link": self.world.death_link[self.player].value, #"death_link": self.multiworld.death_link[self.player].value,
"active_levels": self.active_level_dict, "active_levels": self.active_level_dict,
} }
def fill_slot_data(self) -> dict: def fill_slot_data(self) -> dict:
slot_data = self._get_slot_data() slot_data = self._get_slot_data()
for option_name in smw_options: for option_name in smw_options:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
slot_data[option_name] = option.value slot_data[option_name] = option.value
return slot_data return slot_data
@ -77,20 +77,20 @@ class SMWWorld(World):
def generate_basic(self): def generate_basic(self):
itempool: typing.List[SMWItem] = [] itempool: typing.List[SMWItem] = []
self.active_level_dict = dict(zip(generate_level_list(self.world, self.player), full_level_list)) self.active_level_dict = dict(zip(generate_level_list(self.multiworld, self.player), full_level_list))
self.topology_present = self.world.level_shuffle[self.player] self.topology_present = self.multiworld.level_shuffle[self.player]
connect_regions(self.world, self.player, self.active_level_dict) connect_regions(self.multiworld, self.player, self.active_level_dict)
# Add Boss Token amount requirements for Worlds # Add Boss Token amount requirements for Worlds
add_rule(self.world.get_region(LocationName.donut_plains_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 1)) add_rule(self.multiworld.get_region(LocationName.donut_plains_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 1))
add_rule(self.world.get_region(LocationName.vanilla_dome_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 2)) add_rule(self.multiworld.get_region(LocationName.vanilla_dome_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 2))
add_rule(self.world.get_region(LocationName.forest_of_illusion_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 4)) add_rule(self.multiworld.get_region(LocationName.forest_of_illusion_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 4))
add_rule(self.world.get_region(LocationName.chocolate_island_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 5)) add_rule(self.multiworld.get_region(LocationName.chocolate_island_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 5))
add_rule(self.world.get_region(LocationName.valley_of_bowser_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 6)) add_rule(self.multiworld.get_region(LocationName.valley_of_bowser_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 6))
total_required_locations = 96 total_required_locations = 96
if self.world.dragon_coin_checks[self.player]: if self.multiworld.dragon_coin_checks[self.player]:
total_required_locations += 49 total_required_locations += 49
itempool += [self.create_item(ItemName.mario_run)] itempool += [self.create_item(ItemName.mario_run)]
@ -108,24 +108,24 @@ class SMWWorld(World):
itempool += [self.create_item(ItemName.red_switch_palace)] itempool += [self.create_item(ItemName.red_switch_palace)]
itempool += [self.create_item(ItemName.blue_switch_palace)] itempool += [self.create_item(ItemName.blue_switch_palace)]
if self.world.goal[self.player] == "yoshi_egg_hunt": if self.multiworld.goal[self.player] == "yoshi_egg_hunt":
itempool += [self.create_item(ItemName.yoshi_egg) itempool += [self.create_item(ItemName.yoshi_egg)
for _ in range(self.world.number_of_yoshi_eggs[self.player])] for _ in range(self.multiworld.number_of_yoshi_eggs[self.player])]
self.world.get_location(LocationName.yoshis_house, self.player).place_locked_item(self.create_item(ItemName.victory)) self.multiworld.get_location(LocationName.yoshis_house, self.player).place_locked_item(self.create_item(ItemName.victory))
else: else:
self.world.get_location(LocationName.bowser, self.player).place_locked_item(self.create_item(ItemName.victory)) self.multiworld.get_location(LocationName.bowser, self.player).place_locked_item(self.create_item(ItemName.victory))
junk_count = total_required_locations - len(itempool) junk_count = total_required_locations - len(itempool)
trap_weights = [] trap_weights = []
trap_weights += ([ItemName.ice_trap] * self.world.ice_trap_weight[self.player].value) trap_weights += ([ItemName.ice_trap] * self.multiworld.ice_trap_weight[self.player].value)
trap_weights += ([ItemName.stun_trap] * self.world.stun_trap_weight[self.player].value) trap_weights += ([ItemName.stun_trap] * self.multiworld.stun_trap_weight[self.player].value)
trap_weights += ([ItemName.literature_trap] * self.world.literature_trap_weight[self.player].value) trap_weights += ([ItemName.literature_trap] * self.multiworld.literature_trap_weight[self.player].value)
trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.world.trap_fill_percentage[self.player].value / 100.0)) trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.multiworld.trap_fill_percentage[self.player].value / 100.0))
junk_count -= trap_count junk_count -= trap_count
trap_pool = [] trap_pool = []
for i in range(trap_count): for i in range(trap_count):
trap_item = self.world.random.choice(trap_weights) trap_item = self.multiworld.random.choice(trap_weights)
trap_pool.append(self.create_item(trap_item)) trap_pool.append(self.create_item(trap_item))
itempool += trap_pool itempool += trap_pool
@ -137,21 +137,21 @@ class SMWWorld(World):
LocationName.valley_koopaling, LocationName.vanilla_reznor, LocationName.forest_reznor, LocationName.chocolate_reznor, LocationName.valley_reznor] LocationName.valley_koopaling, LocationName.vanilla_reznor, LocationName.forest_reznor, LocationName.chocolate_reznor, LocationName.valley_reznor]
for location_name in boss_location_names: for location_name in boss_location_names:
self.world.get_location(location_name, self.player).place_locked_item(self.create_item(ItemName.koopaling)) self.multiworld.get_location(location_name, self.player).place_locked_item(self.create_item(ItemName.koopaling))
self.world.itempool += itempool self.multiworld.itempool += itempool
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
rompath = "" # if variable is not declared finally clause may fail rompath = "" # if variable is not declared finally clause may fail
try: try:
world = self.world world = self.multiworld
player = self.player player = self.player
rom = LocalRom(get_base_rom_path()) rom = LocalRom(get_base_rom_path())
patch_rom(self.world, rom, self.player, self.active_level_dict) patch_rom(self.multiworld, rom, self.player, self.active_level_dict)
rompath = os.path.join(output_directory, f"{self.world.get_out_file_name_base(self.player)}.sfc") rompath = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.sfc")
rom.write_to_file(rompath) rom.write_to_file(rompath)
self.rom_name = rom.name self.rom_name = rom.name
@ -173,7 +173,7 @@ class SMWWorld(World):
# we skip in case of error, so that the original error in the output thread is the one that gets raised # we skip in case of error, so that the original error in the output thread is the one that gets raised
if rom_name: if rom_name:
new_name = base64.b64encode(bytes(self.rom_name)).decode() new_name = base64.b64encode(bytes(self.rom_name)).decode()
multidata["connect_names"][new_name] = multidata["connect_names"][self.world.player_name[self.player]] multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
def extend_hint_information(self, hint_data: typing.Dict[int, typing.Dict[int, str]]): def extend_hint_information(self, hint_data: typing.Dict[int, typing.Dict[int, str]]):
if self.topology_present: if self.topology_present:
@ -212,18 +212,18 @@ class SMWWorld(World):
if level_index >= world_cutoffs[i]: if level_index >= world_cutoffs[i]:
continue continue
if self.world.dragon_coin_checks[self.player].value == 0 and "Dragon Coins" in loc_name: if self.multiworld.dragon_coin_checks[self.player].value == 0 and "Dragon Coins" in loc_name:
continue continue
location = self.world.get_location(loc_name, self.player) location = self.multiworld.get_location(loc_name, self.player)
er_hint_data[location.address] = world_names[i] er_hint_data[location.address] = world_names[i]
break break
hint_data[self.player] = er_hint_data hint_data[self.player] = er_hint_data
def create_regions(self): def create_regions(self):
location_table = setup_locations(self.world, self.player) location_table = setup_locations(self.multiworld, self.player)
create_regions(self.world, self.player, location_table) create_regions(self.multiworld, self.player, location_table)
def create_item(self, name: str, force_non_progression=False) -> Item: def create_item(self, name: str, force_non_progression=False) -> Item:
data = item_table[name] data = item_table[name]
@ -244,4 +244,4 @@ class SMWWorld(World):
return created_item return created_item
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)

View File

@ -190,24 +190,24 @@ class SMZ3World(World):
self.config = Config() self.config = Config()
self.config.GameMode = GameMode.Multiworld self.config.GameMode = GameMode.Multiworld
self.config.Z3Logic = Z3Logic.Normal self.config.Z3Logic = Z3Logic.Normal
self.config.SMLogic = SMLogic(self.world.sm_logic[self.player].value) self.config.SMLogic = SMLogic(self.multiworld.sm_logic[self.player].value)
self.config.SwordLocation = SwordLocation(self.world.sword_location[self.player].value) self.config.SwordLocation = SwordLocation(self.multiworld.sword_location[self.player].value)
self.config.MorphLocation = MorphLocation(self.world.morph_location[self.player].value) self.config.MorphLocation = MorphLocation(self.multiworld.morph_location[self.player].value)
self.config.Goal = Goal(self.world.goal[self.player].value) self.config.Goal = Goal(self.multiworld.goal[self.player].value)
self.config.KeyShuffle = KeyShuffle(self.world.key_shuffle[self.player].value) self.config.KeyShuffle = KeyShuffle(self.multiworld.key_shuffle[self.player].value)
self.config.OpenTower = OpenTower(self.world.open_tower[self.player].value) self.config.OpenTower = OpenTower(self.multiworld.open_tower[self.player].value)
self.config.GanonVulnerable = GanonVulnerable(self.world.ganon_vulnerable[self.player].value) self.config.GanonVulnerable = GanonVulnerable(self.multiworld.ganon_vulnerable[self.player].value)
self.config.OpenTourian = OpenTourian(self.world.open_tourian[self.player].value) self.config.OpenTourian = OpenTourian(self.multiworld.open_tourian[self.player].value)
self.local_random = random.Random(self.world.random.randint(0, 1000)) self.local_random = random.Random(self.multiworld.random.randint(0, 1000))
self.smz3World = TotalSMZ3World(self.config, self.world.get_player_name(self.player), self.player, self.world.seed_name) self.smz3World = TotalSMZ3World(self.config, self.multiworld.get_player_name(self.player), self.player, self.multiworld.seed_name)
self.smz3DungeonItems = [] self.smz3DungeonItems = []
SMZ3World.location_names = frozenset(self.smz3World.locationLookup.keys()) SMZ3World.location_names = frozenset(self.smz3World.locationLookup.keys())
self.world.state.smz3state[self.player] = TotalSMZ3Item.Progression([]) self.multiworld.state.smz3state[self.player] = TotalSMZ3Item.Progression([])
def generate_basic(self): def generate_basic(self):
self.smz3World.Setup(WorldState.Generate(self.config, self.world.random)) self.smz3World.Setup(WorldState.Generate(self.config, self.multiworld.random))
self.dungeon = TotalSMZ3Item.Item.CreateDungeonPool(self.smz3World) self.dungeon = TotalSMZ3Item.Item.CreateDungeonPool(self.smz3World)
self.dungeon.reverse() self.dungeon.reverse()
self.progression = TotalSMZ3Item.Item.CreateProgressionPool(self.smz3World) self.progression = TotalSMZ3Item.Item.CreateProgressionPool(self.smz3World)
@ -224,25 +224,25 @@ class SMZ3World(World):
else: else:
progressionItems = self.progression progressionItems = self.progression
for item in self.keyCardsItems: for item in self.keyCardsItems:
self.world.push_precollected(SMZ3Item(item.Type.name, ItemClassification.filler, item.Type, self.item_name_to_id[item.Type.name], self.player, item)) self.multiworld.push_precollected(SMZ3Item(item.Type.name, ItemClassification.filler, item.Type, self.item_name_to_id[item.Type.name], self.player, item))
itemPool = [SMZ3Item(item.Type.name, ItemClassification.progression, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in progressionItems] + \ itemPool = [SMZ3Item(item.Type.name, ItemClassification.progression, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in progressionItems] + \
[SMZ3Item(item.Type.name, ItemClassification.filler, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in allJunkItems] [SMZ3Item(item.Type.name, ItemClassification.filler, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in allJunkItems]
self.smz3DungeonItems = [SMZ3Item(item.Type.name, ItemClassification.progression, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in self.dungeon] self.smz3DungeonItems = [SMZ3Item(item.Type.name, ItemClassification.progression, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in self.dungeon]
self.world.itempool += itemPool self.multiworld.itempool += itemPool
def set_rules(self): def set_rules(self):
# SM G4 is logically required to access Ganon's Tower in SMZ3 # SM G4 is logically required to access Ganon's Tower in SMZ3
self.world.completion_condition[self.player] = lambda state: \ self.multiworld.completion_condition[self.player] = lambda state: \
self.smz3World.GetRegion("Ganon's Tower").CanEnter(state.smz3state[self.player]) and \ self.smz3World.GetRegion("Ganon's Tower").CanEnter(state.smz3state[self.player]) and \
self.smz3World.GetRegion("Ganon's Tower").TowerAscend(state.smz3state[self.player]) self.smz3World.GetRegion("Ganon's Tower").TowerAscend(state.smz3state[self.player])
for region in self.smz3World.Regions: for region in self.smz3World.Regions:
entrance = self.world.get_entrance('Menu' + "->" + region.Name, self.player) entrance = self.multiworld.get_entrance('Menu' + "->" + region.Name, self.player)
set_rule(entrance, lambda state, region=region: region.CanEnter(state.smz3state[self.player])) set_rule(entrance, lambda state, region=region: region.CanEnter(state.smz3state[self.player]))
for loc in region.Locations: for loc in region.Locations:
l = self.locations[loc.Name] l = self.locations[loc.Name]
if self.world.accessibility[self.player] != 'locations': if self.multiworld.accessibility[self.player] != 'locations':
l.always_allow = lambda state, item, loc=loc: \ l.always_allow = lambda state, item, loc=loc: \
item.game == "SMZ3" and \ item.game == "SMZ3" and \
loc.alwaysAllow(TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World), state.smz3state[self.player]) loc.alwaysAllow(TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World), state.smz3state[self.player])
@ -255,13 +255,13 @@ class SMZ3World(World):
def create_regions(self): def create_regions(self):
self.create_locations(self.player) self.create_locations(self.player)
startRegion = self.create_region(self.world, self.player, 'Menu') startRegion = self.create_region(self.multiworld, self.player, 'Menu')
self.world.regions.append(startRegion) self.multiworld.regions.append(startRegion)
for region in self.smz3World.Regions: for region in self.smz3World.Regions:
currentRegion = self.create_region(self.world, self.player, region.Name, region.locationLookup.keys(), [region.Name + "->" + 'Menu']) currentRegion = self.create_region(self.multiworld, self.player, region.Name, region.locationLookup.keys(), [region.Name + "->" + 'Menu'])
self.world.regions.append(currentRegion) self.multiworld.regions.append(currentRegion)
entrance = self.world.get_entrance(region.Name + "->" + 'Menu', self.player) entrance = self.multiworld.get_entrance(region.Name + "->" + 'Menu', self.player)
entrance.connect(startRegion) entrance.connect(startRegion)
exit = Entrance(self.player, 'Menu' + "->" + region.Name, startRegion) exit = Entrance(self.player, 'Menu' + "->" + region.Name, startRegion)
startRegion.exits.append(exit) startRegion.exits.append(exit)
@ -347,7 +347,7 @@ class SMZ3World(World):
sm_remote_idx = 0 sm_remote_idx = 0
lttp_remote_idx = 0 lttp_remote_idx = 0
for location in self.smz3World.Locations: for location in self.smz3World.Locations:
if self.world.worlds[location.APLocation.item.player].game != self.game: if self.multiworld.worlds[location.APLocation.item.player].game != self.game:
if location.Type == LocationType.Visible or location.Type == LocationType.Chozo or location.Type == LocationType.Hidden: if location.Type == LocationType.Visible or location.Type == LocationType.Chozo or location.Type == LocationType.Hidden:
patch[0x390000 + sm_remote_idx*64] = self.convert_to_sm_item_name(location.APLocation.item.name) patch[0x390000 + sm_remote_idx*64] = self.convert_to_sm_item_name(location.APLocation.item.name)
sm_remote_idx += 1 sm_remote_idx += 1
@ -369,12 +369,12 @@ class SMZ3World(World):
patch = {} patch = {}
# smSpinjumps # smSpinjumps
if (self.world.spin_jumps_animation[self.player].value == 1): if (self.multiworld.spin_jumps_animation[self.player].value == 1):
patch[self.SnesCustomization(0x9B93FE)] = bytearray([0x01]) patch[self.SnesCustomization(0x9B93FE)] = bytearray([0x01])
# z3HeartBeep # z3HeartBeep
values = [ 0x00, 0x80, 0x40, 0x20, 0x10] values = [ 0x00, 0x80, 0x40, 0x20, 0x10]
index = self.world.heart_beep_speed[self.player].value index = self.multiworld.heart_beep_speed[self.player].value
patch[0x400033] = bytearray([values[index if index < len(values) else 2]]) patch[0x400033] = bytearray([values[index if index < len(values) else 2]])
# z3HeartColor # z3HeartColor
@ -384,17 +384,17 @@ class SMZ3World(World):
[0x2C, [0xC9, 0x69]], [0x2C, [0xC9, 0x69]],
[0x28, [0xBC, 0x02]] [0x28, [0xBC, 0x02]]
] ]
index = self.world.heart_color[self.player].value index = self.multiworld.heart_color[self.player].value
(hud, fileSelect) = values[index if index < len(values) else 0] (hud, fileSelect) = values[index if index < len(values) else 0]
for i in range(0, 20, 2): for i in range(0, 20, 2):
patch[self.SnesCustomization(0xDFA1E + i)] = bytearray([hud]) patch[self.SnesCustomization(0xDFA1E + i)] = bytearray([hud])
patch[self.SnesCustomization(0x1BD6AA)] = bytearray(fileSelect) patch[self.SnesCustomization(0x1BD6AA)] = bytearray(fileSelect)
# z3QuickSwap # z3QuickSwap
patch[0x40004B] = bytearray([0x01 if self.world.quick_swap[self.player].value else 0x00]) patch[0x40004B] = bytearray([0x01 if self.multiworld.quick_swap[self.player].value else 0x00])
# smEnergyBeepOff # smEnergyBeepOff
if (self.world.energy_beep[self.player].value == 0): if (self.multiworld.energy_beep[self.player].value == 0):
for ([addr, value]) in [ for ([addr, value]) in [
[0x90EA9B, 0x80], [0x90EA9B, 0x80],
[0x90F337, 0x80], [0x90F337, 0x80],
@ -411,12 +411,12 @@ class SMZ3World(World):
base_combined_rom = basepatch.apply(base_combined_rom) base_combined_rom = basepatch.apply(base_combined_rom)
patcher = TotalSMZ3Patch(self.smz3World, patcher = TotalSMZ3Patch(self.smz3World,
[world.smz3World for key, world in self.world.worlds.items() if isinstance(world, SMZ3World) and hasattr(world, "smz3World")], [world.smz3World for key, world in self.multiworld.worlds.items() if isinstance(world, SMZ3World) and hasattr(world, "smz3World")],
self.world.seed_name, self.multiworld.seed_name,
self.world.seed, self.multiworld.seed,
self.local_random, self.local_random,
{v: k for k, v in self.world.player_name.items()}, {v: k for k, v in self.multiworld.player_name.items()},
next(iter(loc.player for loc in self.world.get_locations() if (loc.item.name == "SilverArrows" and loc.item.player == self.player)))) next(iter(loc.player for loc in self.multiworld.get_locations() if (loc.item.name == "SilverArrows" and loc.item.player == self.player))))
patches = patcher.Create(self.smz3World.Config) patches = patcher.Create(self.smz3World.Config)
patches.update(self.apply_sm_custom_sprite()) patches.update(self.apply_sm_custom_sprite())
patches.update(self.apply_item_names()) patches.update(self.apply_item_names())
@ -427,13 +427,13 @@ class SMZ3World(World):
base_combined_rom[addr + offset] = byte base_combined_rom[addr + offset] = byte
offset += 1 offset += 1
outfilebase = self.world.get_out_file_name_base(self.player) outfilebase = self.multiworld.get_out_file_name_base(self.player)
filename = os.path.join(output_directory, f"{outfilebase}.sfc") filename = os.path.join(output_directory, f"{outfilebase}.sfc")
with open(filename, "wb") as binary_file: with open(filename, "wb") as binary_file:
binary_file.write(base_combined_rom) binary_file.write(base_combined_rom)
patch = SMZ3DeltaPatch(os.path.splitext(filename)[0]+SMZ3DeltaPatch.patch_file_ending, player=self.player, patch = SMZ3DeltaPatch(os.path.splitext(filename)[0] + SMZ3DeltaPatch.patch_file_ending, player=self.player,
player_name=self.world.player_name[self.player], patched_path=filename) player_name=self.multiworld.player_name[self.player], patched_path=filename)
patch.write() patch.write()
os.remove(filename) os.remove(filename)
self.rom_name = bytearray(patcher.title, 'utf8') self.rom_name = bytearray(patcher.title, 'utf8')
@ -458,7 +458,7 @@ class SMZ3World(World):
# we skip in case of error, so that the original error in the output thread is the one that gets raised # we skip in case of error, so that the original error in the output thread is the one that gets raised
if rom_name: if rom_name:
new_name = base64.b64encode(bytes(self.rom_name)).decode() new_name = base64.b64encode(bytes(self.rom_name)).decode()
payload = multidata["connect_names"][self.world.player_name[self.player]] payload = multidata["connect_names"][self.multiworld.player_name[self.player]]
multidata["connect_names"][new_name] = payload multidata["connect_names"][new_name] = payload
def fill_slot_data(self): def fill_slot_data(self):
@ -495,18 +495,18 @@ class SMZ3World(World):
if (not self.smz3World.Config.Keysanity): if (not self.smz3World.Config.Keysanity):
locations = [loc for loc in self.locations.values() if loc.item is None] locations = [loc for loc in self.locations.values() if loc.item is None]
self.world.random.shuffle(locations) self.multiworld.random.shuffle(locations)
all_state = self.world.get_all_state(False) all_state = self.multiworld.get_all_state(False)
for item in self.smz3DungeonItems: for item in self.smz3DungeonItems:
all_state.remove(item) all_state.remove(item)
all_dungeonItems = self.smz3DungeonItems[:] all_dungeonItems = self.smz3DungeonItems[:]
fill_restrictive(self.world, all_state, locations, all_dungeonItems, True, True) fill_restrictive(self.multiworld, all_state, locations, all_dungeonItems, True, True)
# some small or big keys (those always_allow) can be unreachable in-game # some small or big keys (those always_allow) can be unreachable in-game
# while logic still collects some of them (probably to simulate the player collecting pot keys in the logic), some others don't # while logic still collects some of them (probably to simulate the player collecting pot keys in the logic), some others don't
# so we need to remove those exceptions as progression items # so we need to remove those exceptions as progression items
if self.world.accessibility[self.player] != 'locations': if self.multiworld.accessibility[self.player] != 'locations':
exception_item = [TotalSMZ3Item.ItemType.BigKeySW, TotalSMZ3Item.ItemType.BigKeySP, TotalSMZ3Item.ItemType.KeyTH] exception_item = [TotalSMZ3Item.ItemType.BigKeySW, TotalSMZ3Item.ItemType.BigKeySP, TotalSMZ3Item.ItemType.KeyTH]
for item in self.smz3DungeonItems: for item in self.smz3DungeonItems:
if item.item.Type in exception_item and item.location.always_allow(all_state, item) and not all_state.can_reach(item.location): if item.item.Type in exception_item and item.location.always_allow(all_state, item) and not all_state.can_reach(item.location):
@ -523,18 +523,18 @@ class SMZ3World(World):
return [] return []
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return self.world.random.choice(self.junkItemsNames) return self.multiworld.random.choice(self.junkItemsNames)
def write_spoiler(self, spoiler_handle: TextIO): def write_spoiler(self, spoiler_handle: TextIO):
self.world.spoiler.unreachables.update(self.unreachable) self.multiworld.spoiler.unreachables.update(self.unreachable)
def JunkFillGT(self, factor): def JunkFillGT(self, factor):
poolLength = len(self.world.itempool) poolLength = len(self.multiworld.itempool)
playerGroups = self.world.get_player_groups(self.player) playerGroups = self.multiworld.get_player_groups(self.player)
playerGroups.add(self.player) playerGroups.add(self.player)
junkPoolIdx = [i for i in range(0, poolLength) junkPoolIdx = [i for i in range(0, poolLength)
if self.world.itempool[i].classification in (ItemClassification.filler, ItemClassification.trap) and if self.multiworld.itempool[i].classification in (ItemClassification.filler, ItemClassification.trap) and
self.world.itempool[i].player in playerGroups] self.multiworld.itempool[i].player in playerGroups]
toRemove = [] toRemove = []
for loc in self.locations.values(): for loc in self.locations.values():
# commenting this for now since doing a partial GT pre fill would allow for non SMZ3 progression in GT # commenting this for now since doing a partial GT pre fill would allow for non SMZ3 progression in GT
@ -544,19 +544,19 @@ class SMZ3World(World):
if loc.name in self.locationNamesGT and loc.item is None: if loc.name in self.locationNamesGT and loc.item is None:
poolLength = len(junkPoolIdx) poolLength = len(junkPoolIdx)
# start looking at a random starting index and loop at start if no match found # start looking at a random starting index and loop at start if no match found
start = self.world.random.randint(0, poolLength) start = self.multiworld.random.randint(0, poolLength)
for off in range(0, poolLength): for off in range(0, poolLength):
i = (start + off) % poolLength i = (start + off) % poolLength
candidate = self.world.itempool[junkPoolIdx[i]] candidate = self.multiworld.itempool[junkPoolIdx[i]]
if junkPoolIdx[i] not in toRemove and loc.can_fill(self.world.state, candidate, False): if junkPoolIdx[i] not in toRemove and loc.can_fill(self.multiworld.state, candidate, False):
itemFromPool = candidate itemFromPool = candidate
toRemove.append(junkPoolIdx[i]) toRemove.append(junkPoolIdx[i])
break break
self.world.push_item(loc, itemFromPool, False) self.multiworld.push_item(loc, itemFromPool, False)
loc.event = False loc.event = False
toRemove.sort(reverse = True) toRemove.sort(reverse = True)
for i in toRemove: for i in toRemove:
self.world.itempool.pop(i) self.multiworld.itempool.pop(i)
def FillItemAtLocation(self, itemPool, itemType, location): def FillItemAtLocation(self, itemPool, itemType, location):
itemToPlace = TotalSMZ3Item.Item.Get(itemPool, itemType, self.smz3World) itemToPlace = TotalSMZ3Item.Item.Get(itemPool, itemType, self.smz3World)
@ -564,28 +564,28 @@ class SMZ3World(World):
raise Exception(f"Tried to place item {itemType} at {location.Name}, but there is no such item in the item pool") raise Exception(f"Tried to place item {itemType} at {location.Name}, but there is no such item in the item pool")
else: else:
location.Item = itemToPlace location.Item = itemToPlace
itemFromPool = next((i for i in self.world.itempool if i.player == self.player and i.name == itemToPlace.Type.name), None) itemFromPool = next((i for i in self.multiworld.itempool if i.player == self.player and i.name == itemToPlace.Type.name), None)
if itemFromPool is not None: if itemFromPool is not None:
self.world.get_location(location.Name, self.player).place_locked_item(itemFromPool) self.multiworld.get_location(location.Name, self.player).place_locked_item(itemFromPool)
self.world.itempool.remove(itemFromPool) self.multiworld.itempool.remove(itemFromPool)
else: else:
itemFromPool = next((i for i in self.smz3DungeonItems if i.player == self.player and i.name == itemToPlace.Type.name), None) itemFromPool = next((i for i in self.smz3DungeonItems if i.player == self.player and i.name == itemToPlace.Type.name), None)
if itemFromPool is not None: if itemFromPool is not None:
self.world.get_location(location.Name, self.player).place_locked_item(itemFromPool) self.multiworld.get_location(location.Name, self.player).place_locked_item(itemFromPool)
self.smz3DungeonItems.remove(itemFromPool) self.smz3DungeonItems.remove(itemFromPool)
itemPool.remove(itemToPlace) itemPool.remove(itemToPlace)
def FrontFillItemInOwnWorld(self, itemPool, itemType): def FrontFillItemInOwnWorld(self, itemPool, itemType):
item = TotalSMZ3Item.Item.Get(itemPool, itemType, self.smz3World) item = TotalSMZ3Item.Item.Get(itemPool, itemType, self.smz3World)
location = next(iter(self.world.random.sample(TotalSMZ3Location.AvailableGlobal(TotalSMZ3Location.Empty(self.smz3World.Locations), self.smz3World.Items()), 1)), None) location = next(iter(self.multiworld.random.sample(TotalSMZ3Location.AvailableGlobal(TotalSMZ3Location.Empty(self.smz3World.Locations), self.smz3World.Items()), 1)), None)
if (location == None): if (location == None):
raise Exception(f"Tried to front fill {item.Name} in, but no location was available") raise Exception(f"Tried to front fill {item.Name} in, but no location was available")
location.Item = item location.Item = item
itemFromPool = next((i for i in self.world.itempool if i.player == self.player and i.name == item.Type.name and i.advancement == item.Progression), None) itemFromPool = next((i for i in self.multiworld.itempool if i.player == self.player and i.name == item.Type.name and i.advancement == item.Progression), None)
if itemFromPool is not None: if itemFromPool is not None:
self.world.get_location(location.Name, self.player).place_locked_item(itemFromPool) self.multiworld.get_location(location.Name, self.player).place_locked_item(itemFromPool)
self.world.itempool.remove(itemFromPool) self.multiworld.itempool.remove(itemFromPool)
itemPool.remove(item) itemPool.remove(item)
def InitialFillInOwnWorld(self): def InitialFillInOwnWorld(self):
@ -621,7 +621,7 @@ class SMZ3World(World):
def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None): def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None):
ret = Region(name, RegionType.LightWorld, name, player) ret = Region(name, RegionType.LightWorld, name, player)
ret.world = world ret.multiworld = world
if locations: if locations:
for loc in locations: for loc in locations:
location = self.locations[loc] location = self.locations[loc]

View File

@ -178,11 +178,11 @@ class SoEWorld(World):
def generate_early(self) -> None: def generate_early(self) -> None:
# store option values that change logic # store option values that change logic
self.energy_core = self.world.energy_core[self.player].value self.energy_core = self.multiworld.energy_core[self.player].value
self.required_fragments = self.world.required_fragments[self.player].value self.required_fragments = self.multiworld.required_fragments[self.player].value
if self.required_fragments > self.world.available_fragments[self.player].value: if self.required_fragments > self.multiworld.available_fragments[self.player].value:
self.world.available_fragments[self.player].value = self.required_fragments self.multiworld.available_fragments[self.player].value = self.required_fragments
self.available_fragments = self.world.available_fragments[self.player].value self.available_fragments = self.multiworld.available_fragments[self.player].value
def create_event(self, event: str) -> Item: def create_event(self, event: str) -> Item:
return SoEItem(event, ItemClassification.progression, None, self.player) return SoEItem(event, ItemClassification.progression, None, self.player)
@ -209,12 +209,12 @@ class SoEWorld(World):
def create_regions(self): def create_regions(self):
# exclude 'hidden' on easy # exclude 'hidden' on easy
max_difficulty = 1 if self.world.difficulty[self.player] == Difficulty.option_easy else 256 max_difficulty = 1 if self.multiworld.difficulty[self.player] == Difficulty.option_easy else 256
# TODO: generate *some* regions from locations' requirements? # TODO: generate *some* regions from locations' requirements?
r = Region('Menu', RegionType.Generic, 'Menu', self.player, self.world) r = Region('Menu', RegionType.Generic, 'Menu', self.player, self.multiworld)
r.exits = [Entrance(self.player, 'New Game', r)] r.exits = [Entrance(self.player, 'New Game', r)]
self.world.regions += [r] self.multiworld.regions += [r]
# group locations into spheres (1, 2, 3+ at index 0, 1, 2) # group locations into spheres (1, 2, 3+ at index 0, 1, 2)
spheres: typing.Dict[int, typing.Dict[int, typing.List[SoELocation]]] = {} spheres: typing.Dict[int, typing.Dict[int, typing.List[SoELocation]]] = {}
@ -232,8 +232,8 @@ class SoEWorld(World):
# mark some as excluded based on numbers above # mark some as excluded based on numbers above
for trash_sphere, fills in trash_fills.items(): for trash_sphere, fills in trash_fills.items():
for typ, counts in fills.items(): for typ, counts in fills.items():
count = counts[self.world.difficulty[self.player].value] count = counts[self.multiworld.difficulty[self.player].value]
for location in self.world.random.sample(spheres[trash_sphere][typ], count): for location in self.multiworld.random.sample(spheres[trash_sphere][typ], count):
location.progress_type = LocationProgressType.EXCLUDED location.progress_type = LocationProgressType.EXCLUDED
# TODO: do we need to set an item rule? # TODO: do we need to set an item rule?
@ -243,7 +243,7 @@ class SoEWorld(World):
if item.name in {"Gauge", "Wheel"}: if item.name in {"Gauge", "Wheel"}:
return False return False
# and some more for non-easy, non-mystery # and some more for non-easy, non-mystery
if self.world.difficulty[item.player] not in (Difficulty.option_easy, Difficulty.option_mystery): if self.multiworld.difficulty[item.player] not in (Difficulty.option_easy, Difficulty.option_mystery):
if item.name in {"Laser Lance", "Atom Smasher", "Diamond Eye"}: if item.name in {"Laser Lance", "Atom Smasher", "Diamond Eye"}:
return False return False
return True return True
@ -253,16 +253,16 @@ class SoEWorld(World):
add_item_rule(location, sphere1_blocked_items_rule) add_item_rule(location, sphere1_blocked_items_rule)
# make some logically late(r) bosses priority locations to increase complexity # make some logically late(r) bosses priority locations to increase complexity
if self.world.difficulty[self.player] == Difficulty.option_mystery: if self.multiworld.difficulty[self.player] == Difficulty.option_mystery:
late_count = self.world.random.randint(0, 2) late_count = self.multiworld.random.randint(0, 2)
else: else:
late_count = self.world.difficulty[self.player].value late_count = self.multiworld.difficulty[self.player].value
late_bosses = ("Tiny", "Aquagoth", "Megataur", "Rimsala", late_bosses = ("Tiny", "Aquagoth", "Megataur", "Rimsala",
"Mungola", "Lightning Storm", "Magmar", "Volcano Viper") "Mungola", "Lightning Storm", "Magmar", "Volcano Viper")
late_locations = self.world.random.sample(late_bosses, late_count) late_locations = self.multiworld.random.sample(late_bosses, late_count)
# add locations to the world # add locations to the world
r = Region('Ingame', RegionType.Generic, 'Ingame', self.player, self.world) r = Region('Ingame', RegionType.Generic, 'Ingame', self.player, self.multiworld)
for sphere in spheres.values(): for sphere in spheres.values():
for locations in sphere.values(): for locations in sphere.values():
for location in locations: for location in locations:
@ -271,9 +271,9 @@ class SoEWorld(World):
location.progress_type = LocationProgressType.PRIORITY location.progress_type = LocationProgressType.PRIORITY
r.locations.append(SoELocation(self.player, 'Done', None, r)) r.locations.append(SoELocation(self.player, 'Done', None, r))
self.world.regions += [r] self.multiworld.regions += [r]
self.world.get_entrance('New Game', self.player).connect(self.world.get_region('Ingame', self.player)) self.multiworld.get_entrance('New Game', self.player).connect(self.multiworld.get_region('Ingame', self.player))
def create_items(self): def create_items(self):
# add regular items to the pool # add regular items to the pool
@ -298,17 +298,17 @@ class SoEWorld(World):
for _ in range(self.available_fragments - 1): for _ in range(self.available_fragments - 1):
if len(ingredients) < 1: if len(ingredients) < 1:
break # out of ingredients to replace break # out of ingredients to replace
r = self.world.random.choice(ingredients) r = self.multiworld.random.choice(ingredients)
ingredients.remove(r) ingredients.remove(r)
items[r] = self.create_item("Energy Core Fragment") items[r] = self.create_item("Energy Core Fragment")
# add traps to the pool # add traps to the pool
trap_count = self.world.trap_count[self.player].value trap_count = self.multiworld.trap_count[self.player].value
trap_chances = {} trap_chances = {}
trap_names = {} trap_names = {}
if trap_count > 0: if trap_count > 0:
for trap_type in self.trap_types: for trap_type in self.trap_types:
trap_option = getattr(self.world, f'trap_chance_{trap_type}')[self.player] trap_option = getattr(self.multiworld, f'trap_chance_{trap_type}')[self.player]
trap_chances[trap_type] = trap_option.value trap_chances[trap_type] = trap_option.value
trap_names[trap_type] = trap_option.item_name trap_names[trap_type] = trap_option.item_name
trap_chances_total = sum(trap_chances.values()) trap_chances_total = sum(trap_chances.values())
@ -318,7 +318,7 @@ class SoEWorld(World):
trap_chances_total = len(trap_chances) trap_chances_total = len(trap_chances)
def create_trap() -> Item: def create_trap() -> Item:
v = self.world.random.randrange(trap_chances_total) v = self.multiworld.random.randrange(trap_chances_total)
for t, c in trap_chances.items(): for t, c in trap_chances.items():
if v < c: if v < c:
return self.create_item(trap_names[t]) return self.create_item(trap_names[t])
@ -328,26 +328,26 @@ class SoEWorld(World):
for _ in range(trap_count): for _ in range(trap_count):
if len(ingredients) < 1: if len(ingredients) < 1:
break # out of ingredients to replace break # out of ingredients to replace
r = self.world.random.choice(ingredients) r = self.multiworld.random.choice(ingredients)
ingredients.remove(r) ingredients.remove(r)
items[r] = create_trap() items[r] = create_trap()
self.world.itempool += items self.multiworld.itempool += items
def set_rules(self): def set_rules(self):
self.world.completion_condition[self.player] = lambda state: state.has('Victory', self.player) self.multiworld.completion_condition[self.player] = lambda state: state.has('Victory', self.player)
# set Done from goal option once we have multiple goals # set Done from goal option once we have multiple goals
set_rule(self.world.get_location('Done', self.player), set_rule(self.multiworld.get_location('Done', self.player),
lambda state: state.soe_has(pyevermizer.P_FINAL_BOSS, self.world, self.player)) lambda state: state.soe_has(pyevermizer.P_FINAL_BOSS, self.multiworld, self.player))
set_rule(self.world.get_entrance('New Game', self.player), lambda state: True) set_rule(self.multiworld.get_entrance('New Game', self.player), lambda state: True)
for loc in _locations: for loc in _locations:
location = self.world.get_location(loc.name, self.player) location = self.multiworld.get_location(loc.name, self.player)
set_rule(location, self.make_rule(loc.requires)) set_rule(location, self.make_rule(loc.requires))
def make_rule(self, requires: typing.List[typing.Tuple[int, int]]) -> typing.Callable[[typing.Any], bool]: def make_rule(self, requires: typing.List[typing.Tuple[int, int]]) -> typing.Callable[[typing.Any], bool]:
def rule(state) -> bool: def rule(state) -> bool:
for count, progress in requires: for count, progress in requires:
if not state.soe_has(progress, self.world, self.player, count): if not state.soe_has(progress, self.multiworld, self.player, count):
return False return False
return True return True
@ -358,20 +358,20 @@ class SoEWorld(World):
def generate_basic(self): def generate_basic(self):
# place Victory event # place Victory event
self.world.get_location('Done', self.player).place_locked_item(self.create_event('Victory')) self.multiworld.get_location('Done', self.player).place_locked_item(self.create_event('Victory'))
# place wings in halls NE to avoid softlock # place wings in halls NE to avoid softlock
wings_location = self.world.random.choice(self._halls_ne_chest_names) wings_location = self.multiworld.random.choice(self._halls_ne_chest_names)
wings_item = self.create_item('Wings') wings_item = self.create_item('Wings')
self.world.get_location(wings_location, self.player).place_locked_item(wings_item) self.multiworld.get_location(wings_location, self.player).place_locked_item(wings_item)
# place energy core at vanilla location for vanilla mode # place energy core at vanilla location for vanilla mode
if self.energy_core == EnergyCore.option_vanilla: if self.energy_core == EnergyCore.option_vanilla:
energy_core = self.create_item('Energy Core') energy_core = self.create_item('Energy Core')
self.world.get_location('Energy Core #285', self.player).place_locked_item(energy_core) self.multiworld.get_location('Energy Core #285', self.player).place_locked_item(energy_core)
# generate stuff for later # generate stuff for later
self.evermizer_seed = self.world.random.randint(0, 2 ** 16 - 1) # TODO: make this an option for "full" plando? self.evermizer_seed = self.multiworld.random.randint(0, 2 ** 16 - 1) # TODO: make this an option for "full" plando?
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
player_name = self.world.get_player_name(self.player) player_name = self.multiworld.get_player_name(self.player)
self.connect_name = player_name[:32] self.connect_name = player_name[:32]
while len(self.connect_name.encode('utf-8')) > 32: while len(self.connect_name.encode('utf-8')) > 32:
self.connect_name = self.connect_name[:-1] self.connect_name = self.connect_name[:-1]
@ -379,27 +379,27 @@ class SoEWorld(World):
placement_file = "" placement_file = ""
out_file = "" out_file = ""
try: try:
money = self.world.money_modifier[self.player].value money = self.multiworld.money_modifier[self.player].value
exp = self.world.exp_modifier[self.player].value exp = self.multiworld.exp_modifier[self.player].value
switches: typing.List[str] = [] switches: typing.List[str] = []
if self.world.death_link[self.player].value: if self.multiworld.death_link[self.player].value:
switches.append("--death-link") switches.append("--death-link")
if self.energy_core == EnergyCore.option_fragments: if self.energy_core == EnergyCore.option_fragments:
switches.extend(('--available-fragments', str(self.available_fragments), switches.extend(('--available-fragments', str(self.available_fragments),
'--required-fragments', str(self.required_fragments))) '--required-fragments', str(self.required_fragments)))
rom_file = get_base_rom_path() rom_file = get_base_rom_path()
out_base = output_path(output_directory, self.world.get_out_file_name_base(self.player)) out_base = output_path(output_directory, self.multiworld.get_out_file_name_base(self.player))
out_file = out_base + '.sfc' out_file = out_base + '.sfc'
placement_file = out_base + '.txt' placement_file = out_base + '.txt'
patch_file = out_base + '.apsoe' patch_file = out_base + '.apsoe'
flags = 'l' # spoiler log flags = 'l' # spoiler log
for option_name in self.option_definitions: for option_name in self.option_definitions:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
if hasattr(option, 'to_flag'): if hasattr(option, 'to_flag'):
flags += option.to_flag() flags += option.to_flag()
with open(placement_file, "wb") as f: # generate placement file with open(placement_file, "wb") as f: # generate placement file
for location in filter(lambda l: l.player == self.player, self.world.get_locations()): for location in filter(lambda l: l.player == self.player, self.multiworld.get_locations()):
item = location.item item = location.item
assert item is not None, "Can't handle unfilled location" assert item is not None, "Can't handle unfilled location"
if item.code is None or location.address is None: if item.code is None or location.address is None:
@ -414,7 +414,7 @@ class SoEWorld(World):
if not os.path.exists(rom_file): if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file) raise FileNotFoundError(rom_file)
if (pyevermizer.main(rom_file, out_file, placement_file, self.world.seed_name, self.connect_name, if (pyevermizer.main(rom_file, out_file, placement_file, self.multiworld.seed_name, self.connect_name,
self.evermizer_seed, flags, money, exp, switches)): self.evermizer_seed, flags, money, exp, switches)):
raise RuntimeError() raise RuntimeError()
patch = SoEDeltaPatch(patch_file, player=self.player, patch = SoEDeltaPatch(patch_file, player=self.player,
@ -434,12 +434,12 @@ class SoEWorld(World):
# wait for self.connect_name to be available. # wait for self.connect_name to be available.
self.connect_name_available_event.wait() self.connect_name_available_event.wait()
# we skip in case of error, so that the original error in the output thread is the one that gets raised # we skip in case of error, so that the original error in the output thread is the one that gets raised
if self.connect_name and self.connect_name != self.world.player_name[self.player]: if self.connect_name and self.connect_name != self.multiworld.player_name[self.player]:
payload = multidata["connect_names"][self.world.player_name[self.player]] payload = multidata["connect_names"][self.multiworld.player_name[self.player]]
multidata["connect_names"][self.connect_name] = payload multidata["connect_names"][self.connect_name] = payload
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return self.world.random.choice(list(self.item_name_groups["Ingredients"])) return self.multiworld.random.choice(list(self.item_name_groups["Ingredients"]))
class SoEItem(Item): class SoEItem(Item):

View File

@ -40,10 +40,10 @@ class SpireWorld(World):
def _get_slot_data(self): def _get_slot_data(self):
return { return {
'seed': "".join(self.world.slot_seeds[self.player].choice(string.ascii_letters) for i in range(16)), 'seed': "".join(self.multiworld.slot_seeds[self.player].choice(string.ascii_letters) for i in range(16)),
'character': self.world.character[self.player], 'character': self.multiworld.character[self.player],
'ascension': self.world.ascension[self.player], 'ascension': self.multiworld.ascension[self.player],
'heart_run': self.world.heart_run[self.player] 'heart_run': self.multiworld.heart_run[self.player]
} }
def generate_basic(self): def generate_basic(self):
@ -55,40 +55,40 @@ class SpireWorld(World):
item = SpireItem(name, self.player) item = SpireItem(name, self.player)
pool.append(item) pool.append(item)
self.world.itempool += pool self.multiworld.itempool += pool
# Pair up our event locations with our event items # Pair up our event locations with our event items
for event, item in event_item_pairs.items(): for event, item in event_item_pairs.items():
event_item = SpireItem(item, self.player) event_item = SpireItem(item, self.player)
self.world.get_location(event, self.player).place_locked_item(event_item) self.multiworld.get_location(event, self.player).place_locked_item(event_item)
if self.world.logic[self.player] != 'no logic': if self.multiworld.logic[self.player] != 'no logic':
self.world.completion_condition[self.player] = lambda state: state.has("Victory", self.player) self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player) set_rules(self.multiworld, self.player)
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
return SpireItem(name, self.player) return SpireItem(name, self.player)
def create_regions(self): def create_regions(self):
create_regions(self.world, self.player) create_regions(self.multiworld, self.player)
def fill_slot_data(self) -> dict: def fill_slot_data(self) -> dict:
slot_data = self._get_slot_data() slot_data = self._get_slot_data()
for option_name in spire_options: for option_name in spire_options:
option = getattr(self.world, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
slot_data[option_name] = int(option.value) slot_data[option_name] = int(option.value)
return slot_data return slot_data
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return self.world.random.choice(["Card Draw", "Card Draw", "Card Draw", "Relic", "Relic"]) return self.multiworld.random.choice(["Card Draw", "Card Draw", "Card Draw", "Relic", "Relic"])
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
ret = Region(name, RegionType.Generic, name, player) ret = Region(name, RegionType.Generic, name, player)
ret.world = world ret.multiworld = world
if locations: if locations:
for location in locations: for location in locations:
loc_id = location_table.get(location, 0) loc_id = location_table.get(location, 0)

View File

@ -277,7 +277,7 @@ aggression_rules: Dict[int, Callable[["CollectionState", int], bool]] = {
def set_rules(subnautica_world: "SubnauticaWorld"): def set_rules(subnautica_world: "SubnauticaWorld"):
player = subnautica_world.player player = subnautica_world.player
world = subnautica_world.world world = subnautica_world.multiworld
for loc in location_table.values(): for loc in location_table.values():
set_location_rule(world, player, loc) set_location_rule(world, player, loc)

View File

@ -47,22 +47,22 @@ class SubnauticaWorld(World):
creatures_to_scan: List[str] creatures_to_scan: List[str]
def generate_early(self) -> None: def generate_early(self) -> None:
if "Seaglide Fragment" not in self.world.early_items[self.player]: if "Seaglide Fragment" not in self.multiworld.early_items[self.player]:
self.world.early_items[self.player].value["Seaglide Fragment"] = 2 self.multiworld.early_items[self.player].value["Seaglide Fragment"] = 2
scan_option: Options.AggressiveScanLogic = self.world.creature_scan_logic[self.player] scan_option: Options.AggressiveScanLogic = self.multiworld.creature_scan_logic[self.player]
creature_pool = scan_option.get_pool() creature_pool = scan_option.get_pool()
self.world.creature_scans[self.player].value = min( self.multiworld.creature_scans[self.player].value = min(
len(creature_pool), len(creature_pool),
self.world.creature_scans[self.player].value self.multiworld.creature_scans[self.player].value
) )
self.creatures_to_scan = self.world.random.sample(creature_pool, self.creatures_to_scan = self.multiworld.random.sample(creature_pool,
self.world.creature_scans[self.player].value) self.multiworld.creature_scans[self.player].value)
def create_regions(self): def create_regions(self):
self.world.regions += [ self.multiworld.regions += [
self.create_region("Menu", None, ["Lifepod 5"]), self.create_region("Menu", None, ["Lifepod 5"]),
self.create_region("Planet 4546B", self.create_region("Planet 4546B",
Locations.events + Locations.events +
@ -75,13 +75,13 @@ class SubnauticaWorld(World):
def generate_basic(self): def generate_basic(self):
# Link regions # Link regions
self.world.get_entrance("Lifepod 5", self.player).connect(self.world.get_region("Planet 4546B", self.player)) self.multiworld.get_entrance("Lifepod 5", self.player).connect(self.multiworld.get_region("Planet 4546B", self.player))
# Generate item pool # Generate item pool
pool = [] pool = []
neptune_launch_platform = None neptune_launch_platform = None
extras = self.world.creature_scans[self.player].value extras = self.multiworld.creature_scans[self.player].value
valuable = self.world.item_pool[self.player] == Options.ItemPool.option_valuable valuable = self.multiworld.item_pool[self.player] == Options.ItemPool.option_valuable
for item in item_table.values(): for item in item_table.values():
for i in range(item["count"]): for i in range(item["count"]):
subnautica_item = self.create_item(item["name"]) subnautica_item = self.create_item(item["name"])
@ -92,26 +92,26 @@ class SubnauticaWorld(World):
else: else:
pool.append(subnautica_item) pool.append(subnautica_item)
for item_name in self.world.random.choices(sorted(Items.advancement_item_names - {"Neptune Launch Platform"}), for item_name in self.multiworld.random.choices(sorted(Items.advancement_item_names - {"Neptune Launch Platform"}),
k=extras): k=extras):
item = self.create_item(item_name) item = self.create_item(item_name)
item.classification = ItemClassification.filler # as it's an extra, just fast-fill it somewhere item.classification = ItemClassification.filler # as it's an extra, just fast-fill it somewhere
pool.append(item) pool.append(item)
self.world.itempool += pool self.multiworld.itempool += pool
# Victory item # Victory item
self.world.get_location("Aurora - Captain Data Terminal", self.player).place_locked_item( self.multiworld.get_location("Aurora - Captain Data Terminal", self.player).place_locked_item(
neptune_launch_platform) neptune_launch_platform)
for event in Locations.events: for event in Locations.events:
self.world.get_location(event, self.player).place_locked_item( self.multiworld.get_location(event, self.player).place_locked_item(
SubnauticaItem(event, ItemClassification.progression, None, player=self.player)) SubnauticaItem(event, ItemClassification.progression, None, player=self.player))
# make the goal event the victory "item" # make the goal event the victory "item"
self.world.get_location(self.world.goal[self.player].get_event_name(), self.player).item.name = "Victory" self.multiworld.get_location(self.multiworld.goal[self.player].get_event_name(), self.player).item.name = "Victory"
def fill_slot_data(self) -> Dict[str, Any]: def fill_slot_data(self) -> Dict[str, Any]:
goal: Options.Goal = self.world.goal[self.player] goal: Options.Goal = self.multiworld.goal[self.player]
item_pool: Options.ItemPool = self.world.item_pool[self.player] item_pool: Options.ItemPool = self.multiworld.item_pool[self.player]
vanilla_tech: List[str] = [] vanilla_tech: List[str] = []
if item_pool == Options.ItemPool.option_valuable: if item_pool == Options.ItemPool.option_valuable:
for item in Items.item_table.values(): for item in Items.item_table.values():
@ -122,7 +122,7 @@ class SubnauticaWorld(World):
"goal": goal.current_key, "goal": goal.current_key,
"vanilla_tech": vanilla_tech, "vanilla_tech": vanilla_tech,
"creatures_to_scan": self.creatures_to_scan, "creatures_to_scan": self.creatures_to_scan,
"death_link": self.world.death_link[self.player].value, "death_link": self.multiworld.death_link[self.player].value,
} }
return slot_data return slot_data
@ -136,7 +136,7 @@ class SubnauticaWorld(World):
def create_region(self, name: str, locations=None, exits=None): def create_region(self, name: str, locations=None, exits=None):
ret = Region(name, RegionType.Generic, name, self.player) ret = Region(name, RegionType.Generic, name, self.player)
ret.world = self.world ret.multiworld = self.multiworld
if locations: if locations:
for location in locations: for location in locations:
loc_id = self.location_name_to_id.get(location, None) loc_id = self.location_name_to_id.get(location, None)

View File

@ -197,7 +197,7 @@ def create_location(player: int, location_data: LocationData, region: Region, lo
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], location_cache: List[Location], name: str) -> Region: def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], location_cache: List[Location], name: str) -> Region:
region = Region(name, RegionType.Generic, name, player) region = Region(name, RegionType.Generic, name, player)
region.world = world region.multiworld = world
if name in locations_per_region: if name in locations_per_region:
for location_data in locations_per_region[name]: for location_data in locations_per_region[name]:

View File

@ -64,47 +64,47 @@ class TimespinnerWorld(World):
def generate_early(self): def generate_early(self):
# in generate_early the start_inventory isnt copied over to precollected_items yet, so we can still modify the options directly # in generate_early the start_inventory isnt copied over to precollected_items yet, so we can still modify the options directly
if self.world.start_inventory[self.player].value.pop('Meyef', 0) > 0: if self.multiworld.start_inventory[self.player].value.pop('Meyef', 0) > 0:
self.world.StartWithMeyef[self.player].value = self.world.StartWithMeyef[self.player].option_true self.multiworld.StartWithMeyef[self.player].value = self.multiworld.StartWithMeyef[self.player].option_true
if self.world.start_inventory[self.player].value.pop('Talaria Attachment', 0) > 0: if self.multiworld.start_inventory[self.player].value.pop('Talaria Attachment', 0) > 0:
self.world.QuickSeed[self.player].value = self.world.QuickSeed[self.player].option_true self.multiworld.QuickSeed[self.player].value = self.multiworld.QuickSeed[self.player].option_true
if self.world.start_inventory[self.player].value.pop('Jewelry Box', 0) > 0: if self.multiworld.start_inventory[self.player].value.pop('Jewelry Box', 0) > 0:
self.world.StartWithJewelryBox[self.player].value = self.world.StartWithJewelryBox[self.player].option_true self.multiworld.StartWithJewelryBox[self.player].value = self.multiworld.StartWithJewelryBox[self.player].option_true
def create_regions(self): def create_regions(self):
create_regions(self.world, self.player, get_locations(self.world, self.player), create_regions(self.multiworld, self.player, get_locations(self.multiworld, self.player),
self.location_cache, self.pyramid_keys_unlock) self.location_cache, self.pyramid_keys_unlock)
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
return create_item_with_correct_settings(self.world, self.player, name) return create_item_with_correct_settings(self.multiworld, self.player, name)
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return self.world.random.choice(filler_items) return self.multiworld.random.choice(filler_items)
def set_rules(self): def set_rules(self):
setup_events(self.player, self.locked_locations, self.location_cache) setup_events(self.player, self.locked_locations, self.location_cache)
self.world.completion_condition[self.player] = lambda state: state.has('Killed Nightmare', self.player) self.multiworld.completion_condition[self.player] = lambda state: state.has('Killed Nightmare', self.player)
def generate_basic(self): def generate_basic(self):
excluded_items = get_excluded_items(self, self.world, self.player) excluded_items = get_excluded_items(self, self.multiworld, self.player)
assign_starter_items(self.world, self.player, excluded_items, self.locked_locations) assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations)
if not is_option_enabled(self.world, self.player, "QuickSeed") and not is_option_enabled(self.world, self.player, "Inverted"): if not is_option_enabled(self.multiworld, self.player, "QuickSeed") and not is_option_enabled(self.multiworld, self.player, "Inverted"):
place_first_progression_item(self.world, self.player, excluded_items, self.locked_locations) place_first_progression_item(self.multiworld, self.player, excluded_items, self.locked_locations)
pool = get_item_pool(self.world, self.player, excluded_items) pool = get_item_pool(self.multiworld, self.player, excluded_items)
fill_item_pool_with_dummy_items(self, self.world, self.player, self.locked_locations, self.location_cache, pool) fill_item_pool_with_dummy_items(self, self.multiworld, self.player, self.locked_locations, self.location_cache, pool)
self.world.itempool += pool self.multiworld.itempool += pool
def fill_slot_data(self) -> Dict[str, object]: def fill_slot_data(self) -> Dict[str, object]:
slot_data: Dict[str, object] = {} slot_data: Dict[str, object] = {}
for option_name in timespinner_options: for option_name in timespinner_options:
slot_data[option_name] = get_option_value(self.world, self.player, option_name) slot_data[option_name] = get_option_value(self.multiworld, self.player, option_name)
slot_data["StinkyMaw"] = True slot_data["StinkyMaw"] = True
slot_data["ProgressiveVerticalMovement"] = False slot_data["ProgressiveVerticalMovement"] = False

View File

@ -44,42 +44,42 @@ class V6World(World):
option_definitions = v6_options option_definitions = v6_options
def create_regions(self): def create_regions(self):
create_regions(self.world,self.player) create_regions(self.multiworld, self.player)
def set_rules(self): def set_rules(self):
self.area_connections = {} self.area_connections = {}
self.area_cost_map = {} self.area_cost_map = {}
set_rules(self.world, self.player, self.area_connections, self.area_cost_map) set_rules(self.multiworld, self.player, self.area_connections, self.area_cost_map)
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
return V6Item(name, ItemClassification.progression, item_table[name], self.player) return V6Item(name, ItemClassification.progression, item_table[name], self.player)
def generate_basic(self): def generate_basic(self):
trinkets = [self.create_item("Trinket " + str(i+1).zfill(2)) for i in range(0,20)] trinkets = [self.create_item("Trinket " + str(i+1).zfill(2)) for i in range(0,20)]
self.world.itempool += trinkets self.multiworld.itempool += trinkets
musiclist_o = [1,2,3,4,9,12] musiclist_o = [1,2,3,4,9,12]
musiclist_s = musiclist_o.copy() musiclist_s = musiclist_o.copy()
if self.world.MusicRandomizer[self.player].value: if self.multiworld.MusicRandomizer[self.player].value:
self.world.random.shuffle(musiclist_s) self.multiworld.random.shuffle(musiclist_s)
self.music_map = dict(zip(musiclist_o, musiclist_s)) self.music_map = dict(zip(musiclist_o, musiclist_s))
def fill_slot_data(self): def fill_slot_data(self):
return { return {
"MusicRando": self.music_map, "MusicRando": self.music_map,
"AreaRando": self.area_connections, "AreaRando": self.area_connections,
"DoorCost": self.world.DoorCost[self.player].value, "DoorCost": self.multiworld.DoorCost[self.player].value,
"AreaCostRando": self.area_cost_map, "AreaCostRando": self.area_cost_map,
"DeathLink": self.world.death_link[self.player].value, "DeathLink": self.multiworld.death_link[self.player].value,
"DeathLink_Amnesty": self.world.DeathLinkAmnesty[self.player].value "DeathLink_Amnesty": self.multiworld.DeathLinkAmnesty[self.player].value
} }
def generate_output(self, output_directory: str): def generate_output(self, output_directory: str):
if self.world.players != 1: if self.multiworld.players != 1:
return return
data = { data = {
"slot_data": self.fill_slot_data(), "slot_data": self.fill_slot_data(),
"location_to_item": {self.location_name_to_id[i.name] : item_table[i.item.name] for i in self.world.get_locations()}, "location_to_item": {self.location_name_to_id[i.name] : item_table[i.item.name] for i in self.multiworld.get_locations()},
"data_package": { "data_package": {
"data": { "data": {
"games": { "games": {
@ -91,6 +91,6 @@ class V6World(World):
} }
} }
} }
filename = f"{self.world.get_out_file_name_base(self.player)}.apv6" filename = f"{self.multiworld.get_out_file_name_base(self.player)}.apv6"
with open(os.path.join(output_directory, filename), 'w') as f: with open(os.path.join(output_directory, filename), 'w') as f:
json.dump(data, f) json.dump(data, f)

View File

@ -54,7 +54,7 @@ class WitnessWorld(World):
def _get_slot_data(self): def _get_slot_data(self):
return { return {
'seed': self.world.random.randint(0, 1000000), 'seed': self.multiworld.random.randint(0, 1000000),
'victory_location': int(self.player_logic.VICTORY_LOCATION, 16), 'victory_location': int(self.player_logic.VICTORY_LOCATION, 16),
'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID, 'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID,
'item_id_to_door_hexes': self.items.ITEM_ID_TO_DOOR_HEX, 'item_id_to_door_hexes': self.items.ITEM_ID_TO_DOOR_HEX,
@ -66,19 +66,19 @@ class WitnessWorld(World):
} }
def generate_early(self): def generate_early(self):
if not (is_option_enabled(self.world, self.player, "shuffle_symbols") if not (is_option_enabled(self.multiworld, self.player, "shuffle_symbols")
or get_option_value(self.world, self.player, "shuffle_doors") or get_option_value(self.multiworld, self.player, "shuffle_doors")
or is_option_enabled(self.world, self.player, "shuffle_lasers")): or is_option_enabled(self.multiworld, self.player, "shuffle_lasers")):
if self.world.players == 1: if self.multiworld.players == 1:
warning("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle, Door" warning("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle, Door"
" Shuffle or Laser Shuffle if that doesn't seem right.") " Shuffle or Laser Shuffle if that doesn't seem right.")
else: else:
raise Exception("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle," raise Exception("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle,"
" Door Shuffle or Laser Shuffle.") " Door Shuffle or Laser Shuffle.")
self.player_logic = WitnessPlayerLogic(self.world, self.player) self.player_logic = WitnessPlayerLogic(self.multiworld, self.player)
self.locat = WitnessPlayerLocations(self.world, self.player, self.player_logic) self.locat = WitnessPlayerLocations(self.multiworld, self.player, self.player_logic)
self.items = WitnessPlayerItems(self.locat, self.world, self.player, self.player_logic) self.items = WitnessPlayerItems(self.locat, self.multiworld, self.player, self.player_logic)
self.regio = WitnessRegions(self.locat) self.regio = WitnessRegions(self.locat)
self.log_ids_to_hints = dict() self.log_ids_to_hints = dict()
@ -99,12 +99,12 @@ class WitnessWorld(World):
less_junk = 0 less_junk = 0
# Put good item on first check if symbol shuffle is on # Put good item on first check if symbol shuffle is on
symbols = is_option_enabled(self.world, self.player, "shuffle_symbols") symbols = is_option_enabled(self.multiworld, self.player, "shuffle_symbols")
if symbols and get_option_value(self.world, self.player, "puzzle_randomization") != 1: if symbols and get_option_value(self.multiworld, self.player, "puzzle_randomization") != 1:
random_good_item = self.world.random.choice(self.items.GOOD_ITEMS) random_good_item = self.multiworld.random.choice(self.items.GOOD_ITEMS)
first_check = self.world.get_location( first_check = self.multiworld.get_location(
"Tutorial Gate Open", self.player "Tutorial Gate Open", self.player
) )
first_check.place_locked_item(items_by_name[random_good_item]) first_check.place_locked_item(items_by_name[random_good_item])
@ -113,7 +113,7 @@ class WitnessWorld(World):
less_junk = 1 less_junk = 1
for item in self.player_logic.STARTING_INVENTORY: for item in self.player_logic.STARTING_INVENTORY:
self.world.push_precollected(items_by_name[item]) self.multiworld.push_precollected(items_by_name[item])
pool.remove(items_by_name[item]) pool.remove(items_by_name[item])
for item in self.items.EXTRA_AMOUNTS: for item in self.items.EXTRA_AMOUNTS:
@ -133,28 +133,28 @@ class WitnessWorld(World):
item_obj = self.create_item( item_obj = self.create_item(
self.player_logic.EVENT_ITEM_PAIRS[event_location] self.player_logic.EVENT_ITEM_PAIRS[event_location]
) )
location_obj = self.world.get_location(event_location, self.player) location_obj = self.multiworld.get_location(event_location, self.player)
location_obj.place_locked_item(item_obj) location_obj.place_locked_item(item_obj)
self.world.itempool += pool self.multiworld.itempool += pool
def create_regions(self): def create_regions(self):
self.regio.create_regions(self.world, self.player, self.player_logic) self.regio.create_regions(self.multiworld, self.player, self.player_logic)
def set_rules(self): def set_rules(self):
set_rules(self.world, self.player, self.player_logic, self.locat) set_rules(self.multiworld, self.player, self.player_logic, self.locat)
def fill_slot_data(self) -> dict: def fill_slot_data(self) -> dict:
hint_amount = get_option_value(self.world, self.player, "hint_amount") hint_amount = get_option_value(self.multiworld, self.player, "hint_amount")
credits_hint = ("This Randomizer", "is brought to you by", "NewSoupVi, Jarno, jbzdarkid, sigma144", -1) credits_hint = ("This Randomizer", "is brought to you by", "NewSoupVi, Jarno, jbzdarkid, sigma144", -1)
audio_logs = get_audio_logs().copy() audio_logs = get_audio_logs().copy()
if hint_amount != 0: if hint_amount != 0:
generated_hints = make_hints(self.world, self.player, hint_amount) generated_hints = make_hints(self.multiworld, self.player, hint_amount)
self.world.random.shuffle(audio_logs) self.multiworld.random.shuffle(audio_logs)
duplicates = len(audio_logs) // hint_amount duplicates = len(audio_logs) // hint_amount
@ -169,7 +169,7 @@ class WitnessWorld(World):
audio_log = audio_logs.pop() audio_log = audio_logs.pop()
self.log_ids_to_hints[int(audio_log, 16)] = credits_hint self.log_ids_to_hints[int(audio_log, 16)] = credits_hint
joke_hints = generate_joke_hints(self.world, len(audio_logs)) joke_hints = generate_joke_hints(self.multiworld, len(audio_logs))
while audio_logs: while audio_logs:
audio_log = audio_logs.pop() audio_log = audio_logs.pop()
@ -181,7 +181,7 @@ class WitnessWorld(World):
for option_name in the_witness_options: for option_name in the_witness_options:
slot_data[option_name] = get_option_value( slot_data[option_name] = get_option_value(
self.world, self.player, option_name self.multiworld, self.player, option_name
) )
return slot_data return slot_data
@ -234,7 +234,7 @@ def create_region(world: MultiWorld, player: int, name: str,
""" """
ret = Region(name, RegionType.Generic, name, player) ret = Region(name, RegionType.Generic, name, player)
ret.world = world ret.multiworld = world
if region_locations: if region_locations:
for location in region_locations: for location in region_locations:
loc_id = locat.CHECK_LOCATION_TABLE[location] loc_id = locat.CHECK_LOCATION_TABLE[location]

View File

@ -121,10 +121,10 @@ class ZillionWorld(World):
raise FileNotFoundError(rom_file) raise FileNotFoundError(rom_file)
def generate_early(self) -> None: def generate_early(self) -> None:
if not hasattr(self.world, "zillion_logic_cache"): if not hasattr(self.multiworld, "zillion_logic_cache"):
setattr(self.world, "zillion_logic_cache", {}) setattr(self.multiworld, "zillion_logic_cache", {})
zz_op, item_counts = validate(self.world, self.player) zz_op, item_counts = validate(self.multiworld, self.player)
self._item_counts = item_counts self._item_counts = item_counts
@ -148,7 +148,7 @@ class ZillionWorld(World):
assert self.zz_system.randomizer, "generate_early hasn't been called" assert self.zz_system.randomizer, "generate_early hasn't been called"
assert self.id_to_zz_item, "generate_early hasn't been called" assert self.id_to_zz_item, "generate_early hasn't been called"
p = self.player p = self.player
w = self.world w = self.multiworld
self.my_locations = [] self.my_locations = []
self.zz_system.randomizer.place_canister_gun_reqs() self.zz_system.randomizer.place_canister_gun_reqs()
@ -159,7 +159,7 @@ class ZillionWorld(World):
for here_zz_name, zz_r in self.zz_system.randomizer.regions.items(): for here_zz_name, zz_r in self.zz_system.randomizer.regions.items():
here_name = "Menu" if here_zz_name == "start" else zz_reg_name_to_reg_name(here_zz_name) here_name = "Menu" if here_zz_name == "start" else zz_reg_name_to_reg_name(here_zz_name)
all[here_name] = ZillionRegion(zz_r, here_name, RegionType.Generic, here_name, p, w) all[here_name] = ZillionRegion(zz_r, here_name, RegionType.Generic, here_name, p, w)
self.world.regions.append(all[here_name]) self.multiworld.regions.append(all[here_name])
limited_skill = Req(gun=3, jump=3, skill=self.zz_system.randomizer.options.skill, hp=940, red=1, floppy=126) limited_skill = Req(gun=3, jump=3, skill=self.zz_system.randomizer.options.skill, hp=940, red=1, floppy=126)
queue = deque([start]) queue = deque([start])
@ -191,7 +191,7 @@ class ZillionWorld(World):
loc.access_rule = access_rule loc.access_rule = access_rule
if not (limited_skill >= zz_loc.req): if not (limited_skill >= zz_loc.req):
loc.progress_type = LocationProgressType.EXCLUDED loc.progress_type = LocationProgressType.EXCLUDED
self.world.exclude_locations[p].value.add(loc.name) self.multiworld.exclude_locations[p].value.add(loc.name)
here.locations.append(loc) here.locations.append(loc)
self.my_locations.append(loc) self.my_locations.append(loc)
@ -222,11 +222,11 @@ class ZillionWorld(World):
if item_name in item_counts: if item_name in item_counts:
count = item_counts[item_name] count = item_counts[item_name]
self.logger.debug(f"Zillion Items: {item_name} {count}") self.logger.debug(f"Zillion Items: {item_name} {count}")
self.world.itempool += [self.create_item(item_name) for _ in range(count)] self.multiworld.itempool += [self.create_item(item_name) for _ in range(count)]
elif item_id < (3 + base_id) and zz_item.code == RESCUE: elif item_id < (3 + base_id) and zz_item.code == RESCUE:
# One of the 3 rescues will not be in the pool and its zz_item will be 'empty'. # One of the 3 rescues will not be in the pool and its zz_item will be 'empty'.
self.logger.debug(f"Zillion Items: {item_name} 1") self.logger.debug(f"Zillion Items: {item_name} 1")
self.world.itempool.append(self.create_item(item_name)) self.multiworld.itempool.append(self.create_item(item_name))
def set_rules(self) -> None: def set_rules(self) -> None:
# logic for this game is in create_regions # logic for this game is in create_regions
@ -237,9 +237,9 @@ class ZillionWorld(World):
# main location name is an alias # main location name is an alias
main_loc_name = self.zz_system.randomizer.loc_name_2_pretty[self.zz_system.randomizer.locations['main'].name] main_loc_name = self.zz_system.randomizer.loc_name_2_pretty[self.zz_system.randomizer.locations['main'].name]
self.world.get_location(main_loc_name, self.player)\ self.multiworld.get_location(main_loc_name, self.player)\
.place_locked_item(self.create_item("Win")) .place_locked_item(self.create_item("Win"))
self.world.completion_condition[self.player] = \ self.multiworld.completion_condition[self.player] = \
lambda state: state.has("Win", self.player) lambda state: state.has("Win", self.player)
@staticmethod @staticmethod
@ -295,7 +295,7 @@ class ZillionWorld(World):
empty = zz_items[4] empty = zz_items[4]
multi_item = empty # a different patcher method differentiates empty from ap multi item multi_item = empty # a different patcher method differentiates empty from ap multi item
multi_items: Dict[str, Tuple[str, str]] = {} # zz_loc_name to (item_name, player_name) multi_items: Dict[str, Tuple[str, str]] = {} # zz_loc_name to (item_name, player_name)
for loc in self.world.get_locations(): for loc in self.multiworld.get_locations():
if loc.player == self.player: if loc.player == self.player:
z_loc = cast(ZillionLocation, loc) z_loc = cast(ZillionLocation, loc)
# debug_zz_loc_ids[z_loc.zz_loc.name] = id(z_loc.zz_loc) # debug_zz_loc_ids[z_loc.zz_loc.name] = id(z_loc.zz_loc)
@ -310,7 +310,7 @@ class ZillionWorld(World):
z_loc.zz_loc.item = multi_item z_loc.zz_loc.item = multi_item
multi_items[z_loc.zz_loc.name] = ( multi_items[z_loc.zz_loc.name] = (
z_loc.item.name, z_loc.item.name,
self.world.get_player_name(z_loc.item.player) self.multiworld.get_player_name(z_loc.item.player)
) )
# debug_zz_loc_ids.sort() # debug_zz_loc_ids.sort()
# for name, id_ in debug_zz_loc_ids.items(): # for name, id_ in debug_zz_loc_ids.items():
@ -340,7 +340,7 @@ class ZillionWorld(World):
zz_patcher.all_fixes_and_options(zz_options) zz_patcher.all_fixes_and_options(zz_options)
zz_patcher.set_external_item_interface(zz_options.start_char, zz_options.max_level) zz_patcher.set_external_item_interface(zz_options.start_char, zz_options.max_level)
zz_patcher.set_multiworld_items(multi_items) zz_patcher.set_multiworld_items(multi_items)
game_id = self.world.player_name[self.player].encode() + b'\x00' + self.world.seed_name[-6:].encode() game_id = self.multiworld.player_name[self.player].encode() + b'\x00' + self.multiworld.seed_name[-6:].encode()
zz_patcher.set_rom_to_ram_data(game_id) zz_patcher.set_rom_to_ram_data(game_id)
def generate_output(self, output_directory: str) -> None: def generate_output(self, output_directory: str) -> None:
@ -352,7 +352,7 @@ class ZillionWorld(World):
# original_rom_bytes = self.zz_patcher.rom # original_rom_bytes = self.zz_patcher.rom
patched_rom_bytes = self.zz_system.patcher.get_patched_bytes() patched_rom_bytes = self.zz_system.patcher.get_patched_bytes()
out_file_base = self.world.get_out_file_name_base(self.player) out_file_base = self.multiworld.get_out_file_name_base(self.player)
filename = os.path.join( filename = os.path.join(
output_directory, output_directory,
@ -363,7 +363,7 @@ class ZillionWorld(World):
patch = ZillionDeltaPatch( patch = ZillionDeltaPatch(
os.path.splitext(filename)[0] + ZillionDeltaPatch.patch_file_ending, os.path.splitext(filename)[0] + ZillionDeltaPatch.patch_file_ending,
player=self.player, player=self.player,
player_name=self.world.player_name[self.player], player_name=self.multiworld.player_name[self.player],
patched_path=filename patched_path=filename
) )
patch.write() patch.write()
@ -401,7 +401,7 @@ class ZillionWorld(World):
# def modify_multidata(self, multidata: Dict[str, Any]) -> None: # def modify_multidata(self, multidata: Dict[str, Any]) -> None:
# """For deeper modification of server multidata.""" # """For deeper modification of server multidata."""
# # not modifying multidata, just want to call this at the end of the generation process # # not modifying multidata, just want to call this at the end of the generation process
# cache = getattr(self.world, "zillion_logic_cache") # cache = getattr(self.multiworld, "zillion_logic_cache")
# import sys # import sys
# print(sys.getsizeof(cache)) # print(sys.getsizeof(cache))
@ -409,7 +409,7 @@ class ZillionWorld(World):
def create_item(self, name: str) -> Item: def create_item(self, name: str) -> Item:
"""Create an item for this world type and player. """Create an item for this world type and player.
Warning: this may be called with self.world = None, for example by MultiServer""" Warning: this may be called with self.multiworld = None, for example by MultiServer"""
item_id = _item_name_to_id[name] item_id = _item_name_to_id[name]
if not self.id_to_zz_item: if not self.id_to_zz_item:

View File

@ -18,7 +18,7 @@ def set_randomizer_locs(cs: CollectionState, p: int, zz_r: Randomizer) -> int:
returns a hash of the player and of the set locations with their items returns a hash of the player and of the set locations with their items
""" """
z_world = cs.world.worlds[p] z_world = cs.multiworld.worlds[p]
my_locations = cast(List[ZillionLocation], getattr(z_world, "my_locations")) my_locations = cast(List[ZillionLocation], getattr(z_world, "my_locations"))
_hash = p _hash = p
@ -50,7 +50,7 @@ def cs_to_zz_locs(cs: CollectionState, p: int, zz_r: Randomizer, id_to_zz_item:
returns frozenset of accessible zilliandomizer locations returns frozenset of accessible zilliandomizer locations
""" """
# caching this function because it would be slow # caching this function because it would be slow
logic_cache: LogicCacheType = getattr(cs.world, "zillion_logic_cache", {}) logic_cache: LogicCacheType = getattr(cs.multiworld, "zillion_logic_cache", {})
_hash = set_randomizer_locs(cs, p, zz_r) _hash = set_randomizer_locs(cs, p, zz_r)
counts = item_counts(cs, p) counts = item_counts(cs, p)
_hash += hash(counts) _hash += hash(counts)