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:
parent
87f4a97f1e
commit
2af510328e
BaseClasses.py
docs
test
TestBase.py
dungeons
general
inverted
inverted_minor_glitches
inverted_owg
minecraft
minor_glitches
owg
vanilla
worlds
worlds
AutoWorld.py
alttp
archipidle
checksfinder
dark_souls_3
dkc3
factorio
ff1
generic
hk
hylics2
meritous
minecraft
oot
Cosmetics.pyDungeon.pyEntrance.pyEntranceShuffle.pyHints.pyItemPool.pyMessages.pyPatches.pyRuleParser.pyRules.py__init__.py
oribf
overcooked2
pokemon_rb
raft
rogue_legacy
ror2
sa2b
sc2wol
sm
sm64ex
smw
smz3
soe
spire
subnautica
timespinner
v6
witness
zillion
230
BaseClasses.py
230
BaseClasses.py
|
@ -312,7 +312,7 @@ class MultiWorld():
|
|||
|
||||
def initialize_regions(self, regions=None):
|
||||
for region in regions if regions else self.regions:
|
||||
region.world = self
|
||||
region.multiworld = self
|
||||
self._region_cache[region.player][region.name] = region
|
||||
|
||||
@functools.cached_property
|
||||
|
@ -602,7 +602,7 @@ PathValue = Tuple[str, Optional["PathValue"]]
|
|||
|
||||
class CollectionState():
|
||||
prog_items: typing.Counter[Tuple[str, int]]
|
||||
world: MultiWorld
|
||||
multiworld: MultiWorld
|
||||
reachable_regions: Dict[int, Set[Region]]
|
||||
blocked_connections: Dict[int, Set[Entrance]]
|
||||
events: Set[Location]
|
||||
|
@ -614,7 +614,7 @@ class CollectionState():
|
|||
|
||||
def __init__(self, parent: MultiWorld):
|
||||
self.prog_items = Counter()
|
||||
self.world = parent
|
||||
self.multiworld = parent
|
||||
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.events = set()
|
||||
|
@ -632,7 +632,7 @@ class CollectionState():
|
|||
rrp = self.reachable_regions[player]
|
||||
bc = 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
|
||||
if start not in rrp:
|
||||
|
@ -655,12 +655,12 @@ class CollectionState():
|
|||
self.path[new_region] = (new_region.name, self.path.get(connection, None))
|
||||
|
||||
# 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:
|
||||
queue.append(new_entrance)
|
||||
|
||||
def copy(self) -> CollectionState:
|
||||
ret = CollectionState(self.world)
|
||||
ret = CollectionState(self.multiworld)
|
||||
ret.prog_items = self.prog_items.copy()
|
||||
ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in
|
||||
self.reachable_regions}
|
||||
|
@ -681,17 +681,17 @@ class CollectionState():
|
|||
assert isinstance(player, int), "can_reach: player is required if spot is str"
|
||||
# try to resolve a name
|
||||
if resolution_hint == 'Location':
|
||||
spot = self.world.get_location(spot, player)
|
||||
spot = self.multiworld.get_location(spot, player)
|
||||
elif resolution_hint == 'Entrance':
|
||||
spot = self.world.get_entrance(spot, player)
|
||||
spot = self.multiworld.get_entrance(spot, player)
|
||||
else:
|
||||
# default to Region
|
||||
spot = self.world.get_region(spot, player)
|
||||
spot = self.multiworld.get_region(spot, player)
|
||||
return spot.can_reach(self)
|
||||
|
||||
def sweep_for_events(self, key_only: bool = False, locations: Optional[Iterable[Location]] = None) -> None:
|
||||
if locations is None:
|
||||
locations = self.world.get_filled_locations()
|
||||
locations = self.multiworld.get_filled_locations()
|
||||
reachable_events = True
|
||||
# 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
|
||||
|
@ -718,7 +718,7 @@ class CollectionState():
|
|||
|
||||
def has_group(self, item_name_group: str, player: int, count: int = 1) -> bool:
|
||||
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]
|
||||
if found >= count:
|
||||
return True
|
||||
|
@ -726,17 +726,17 @@ class CollectionState():
|
|||
|
||||
def count_group(self, item_name_group: str, player: int) -> int:
|
||||
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]
|
||||
return found
|
||||
|
||||
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
|
||||
shop in self.world.shops)
|
||||
shop in self.multiworld.shops)
|
||||
|
||||
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
|
||||
shop in self.world.shops)
|
||||
shop in self.multiworld.shops)
|
||||
|
||||
def item_count(self, item: str, player: int) -> int:
|
||||
return self.prog_items[item, player]
|
||||
|
@ -756,7 +756,7 @@ class CollectionState():
|
|||
return self.has('Power Glove', player) or self.has('Titans Mitts', player)
|
||||
|
||||
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))
|
||||
|
||||
def has_hearts(self, player: int, count: int) -> int:
|
||||
|
@ -765,7 +765,7 @@ class CollectionState():
|
|||
|
||||
def heart_count(self, player: int) -> int:
|
||||
# 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) \
|
||||
+ self.item_count('Sanctuary Heart Container', player) \
|
||||
+ 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):
|
||||
basemagic = 16
|
||||
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))
|
||||
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))
|
||||
else:
|
||||
basemagic = basemagic + basemagic * self.bottle_count(player)
|
||||
|
@ -799,12 +799,12 @@ class CollectionState():
|
|||
or (self.has('Bombs (10)', player) and enemies < 6))
|
||||
|
||||
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)
|
||||
|
||||
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 (
|
||||
self.has_group("Bottles", player) and
|
||||
self.has('Bug Catching Net', player) and
|
||||
|
@ -815,7 +815,7 @@ class CollectionState():
|
|||
|
||||
def can_retrieve_tablet(self, player: int) -> bool:
|
||||
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)))
|
||||
|
||||
def has_sword(self, player: int) -> bool:
|
||||
|
@ -837,7 +837,7 @@ class CollectionState():
|
|||
def can_melt_things(self, player: int) -> bool:
|
||||
return self.has('Fire Rod', player) or \
|
||||
(self.has('Bombos', player) and
|
||||
(self.world.swordless[player] or
|
||||
(self.multiworld.swordless[player] or
|
||||
self.has_sword(player)))
|
||||
|
||||
def can_avoid_lasers(self, player: int) -> bool:
|
||||
|
@ -847,7 +847,7 @@ class CollectionState():
|
|||
if self.has('Moon Pearl', player):
|
||||
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:
|
||||
if True in [i.is_light_world for i in self.reachable_regions[player]]:
|
||||
|
@ -860,24 +860,24 @@ class CollectionState():
|
|||
return False
|
||||
|
||||
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:
|
||||
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:
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)])]
|
||||
if self.world.mode[player] == 'inverted':
|
||||
if self.multiworld.mode[player] == 'inverted':
|
||||
rules.append(self.has('Moon Pearl', player))
|
||||
return all(rules)
|
||||
|
||||
|
@ -886,7 +886,7 @@ class CollectionState():
|
|||
|
||||
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)])]
|
||||
if self.world.mode[player] != 'inverted':
|
||||
if self.multiworld.mode[player] != 'inverted':
|
||||
rules.append(self.has('Moon Pearl', player))
|
||||
return all(rules)
|
||||
|
||||
|
@ -897,7 +897,7 @@ class CollectionState():
|
|||
if 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:
|
||||
self.prog_items[item.name, item.player] += 1
|
||||
|
@ -911,7 +911,7 @@ class CollectionState():
|
|||
return changed
|
||||
|
||||
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:
|
||||
# invalidate caches, nothing can be trusted anymore now
|
||||
self.reachable_regions[item.player] = set()
|
||||
|
@ -938,7 +938,7 @@ class Region:
|
|||
type: RegionType
|
||||
hint_text: str
|
||||
player: int
|
||||
world: Optional[MultiWorld]
|
||||
multiworld: Optional[MultiWorld]
|
||||
entrances: List[Entrance]
|
||||
exits: List[Entrance]
|
||||
locations: List[Location]
|
||||
|
@ -956,7 +956,7 @@ class Region:
|
|||
self.entrances = []
|
||||
self.exits = []
|
||||
self.locations = []
|
||||
self.world = world
|
||||
self.multiworld = world
|
||||
self.hint_text = hint
|
||||
self.player = player
|
||||
|
||||
|
@ -984,7 +984,7 @@ class Region:
|
|||
return self.__str__()
|
||||
|
||||
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:
|
||||
|
@ -1021,7 +1021,7 @@ class Entrance:
|
|||
return self.__str__()
|
||||
|
||||
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})'
|
||||
|
||||
|
||||
|
@ -1035,7 +1035,7 @@ class Dungeon(object):
|
|||
self.dungeon_items = dungeon_items
|
||||
self.bosses = dict()
|
||||
self.player = player
|
||||
self.world = None
|
||||
self.multiworld = None
|
||||
|
||||
@property
|
||||
def boss(self) -> Optional[Boss]:
|
||||
|
@ -1065,7 +1065,7 @@ class Dungeon(object):
|
|||
return self.__str__()
|
||||
|
||||
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():
|
||||
|
@ -1130,7 +1130,7 @@ class Location:
|
|||
return self.__str__()
|
||||
|
||||
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})'
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -1227,17 +1227,17 @@ class Item:
|
|||
return self.__str__()
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.location and self.location.parent_region and self.location.parent_region.world:
|
||||
return self.location.parent_region.world.get_name_string_for_object(self)
|
||||
if self.location and self.location.parent_region and self.location.parent_region.multiworld:
|
||||
return self.location.parent_region.multiworld.get_name_string_for_object(self)
|
||||
return f"{self.name} (Player {self.player})"
|
||||
|
||||
|
||||
class Spoiler():
|
||||
world: MultiWorld
|
||||
multiworld: MultiWorld
|
||||
unreachables: Set[Location]
|
||||
|
||||
def __init__(self, world):
|
||||
self.world = world
|
||||
self.multiworld = world
|
||||
self.hashes = {}
|
||||
self.entrances = OrderedDict()
|
||||
self.medallions = {}
|
||||
|
@ -1249,7 +1249,7 @@ class Spoiler():
|
|||
self.bosses = OrderedDict()
|
||||
|
||||
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(
|
||||
[('entrance', entrance), ('exit', exit_), ('direction', direction)])
|
||||
else:
|
||||
|
@ -1258,45 +1258,45 @@ class Spoiler():
|
|||
|
||||
def parse_data(self):
|
||||
self.medallions = OrderedDict()
|
||||
for player in self.world.get_game_players("A Link to the Past"):
|
||||
self.medallions[f'Misery Mire ({self.world.get_player_name(player)})'] = \
|
||||
self.world.required_medallions[player][0]
|
||||
self.medallions[f'Turtle Rock ({self.world.get_player_name(player)})'] = \
|
||||
self.world.required_medallions[player][1]
|
||||
for player in self.multiworld.get_game_players("A Link to the Past"):
|
||||
self.medallions[f'Misery Mire ({self.multiworld.get_player_name(player)})'] = \
|
||||
self.multiworld.required_medallions[player][0]
|
||||
self.medallions[f'Turtle Rock ({self.multiworld.get_player_name(player)})'] = \
|
||||
self.multiworld.required_medallions[player][1]
|
||||
|
||||
self.locations = OrderedDict()
|
||||
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]
|
||||
self.locations['Light World'] = OrderedDict(
|
||||
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
|
||||
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]
|
||||
self.locations['Dark World'] = OrderedDict(
|
||||
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
|
||||
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]
|
||||
self.locations['Caves'] = OrderedDict(
|
||||
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
|
||||
cave_locations])
|
||||
listed_locations.update(cave_locations)
|
||||
|
||||
for dungeon in self.world.dungeons.values():
|
||||
dungeon_locations = [loc for loc in self.world.get_locations() if
|
||||
for dungeon in self.multiworld.dungeons.values():
|
||||
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]
|
||||
self.locations[str(dungeon)] = OrderedDict(
|
||||
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
|
||||
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]
|
||||
if other_locations:
|
||||
self.locations['Other Locations'] = OrderedDict(
|
||||
|
@ -1306,7 +1306,7 @@ class Spoiler():
|
|||
|
||||
self.shops = []
|
||||
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:
|
||||
continue
|
||||
shopdata = {
|
||||
|
@ -1335,34 +1335,34 @@ class Spoiler():
|
|||
index)] += f", {item['replacement']} - {item['replacement_price']} {price_type_display_name[item['replacement_price_type']]}"
|
||||
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)]["Eastern Palace"] = self.world.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)]["Tower Of Hera"] = self.world.get_dungeon("Tower of Hera", 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.multiworld.get_dungeon("Desert Palace", 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)]["Palace Of Darkness"] = self.world.get_dungeon("Palace of Darkness",
|
||||
player).boss.name
|
||||
self.bosses[str(player)]["Swamp Palace"] = self.world.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)]["Thieves Town"] = self.world.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)]["Misery Mire"] = self.world.get_dungeon("Misery Mire", player).boss.name
|
||||
self.bosses[str(player)]["Turtle Rock"] = self.world.get_dungeon("Turtle Rock", player).boss.name
|
||||
if self.world.mode[player] != 'inverted':
|
||||
self.bosses[str(player)]["Palace Of Darkness"] = self.multiworld.get_dungeon("Palace of Darkness",
|
||||
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.multiworld.get_dungeon("Skull Woods", 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.multiworld.get_dungeon("Ice Palace", 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.multiworld.get_dungeon("Turtle Rock", player).boss.name
|
||||
if self.multiworld.mode[player] != 'inverted':
|
||||
self.bosses[str(player)]["Ganons Tower Basement"] = \
|
||||
self.world.get_dungeon('Ganons Tower', player).bosses['bottom'].name
|
||||
self.bosses[str(player)]["Ganons Tower Middle"] = self.world.get_dungeon('Ganons Tower', player).bosses[
|
||||
self.multiworld.get_dungeon('Ganons Tower', player).bosses['bottom'].name
|
||||
self.bosses[str(player)]["Ganons Tower Middle"] = self.multiworld.get_dungeon('Ganons Tower', player).bosses[
|
||||
'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
|
||||
else:
|
||||
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.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.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)]["Ganon"] = "Ganon"
|
||||
|
@ -1392,7 +1392,7 @@ class Spoiler():
|
|||
return 'Yes' if variable else 'No'
|
||||
|
||||
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)
|
||||
try:
|
||||
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:
|
||||
outfile.write(
|
||||
'Archipelago Version %s - Seed: %s\n\n' % (
|
||||
Utils.__version__, self.world.seed))
|
||||
outfile.write('Filling Algorithm: %s\n' % self.world.algorithm)
|
||||
outfile.write('Players: %d\n' % self.world.players)
|
||||
AutoWorld.call_stage(self.world, "write_spoiler_header", outfile)
|
||||
Utils.__version__, self.multiworld.seed))
|
||||
outfile.write('Filling Algorithm: %s\n' % self.multiworld.algorithm)
|
||||
outfile.write('Players: %d\n' % self.multiworld.players)
|
||||
AutoWorld.call_stage(self.multiworld, "write_spoiler_header", outfile)
|
||||
|
||||
for player in range(1, self.world.players + 1):
|
||||
if self.world.players > 1:
|
||||
outfile.write('\nPlayer %d: %s\n' % (player, self.world.get_player_name(player)))
|
||||
outfile.write('Game: %s\n' % self.world.game[player])
|
||||
for player in range(1, self.multiworld.players + 1):
|
||||
if self.multiworld.players > 1:
|
||||
outfile.write('\nPlayer %d: %s\n' % (player, self.multiworld.get_player_name(player)))
|
||||
outfile.write('Game: %s\n' % self.multiworld.game[player])
|
||||
for f_option, option in Options.per_game_common_options.items():
|
||||
write_option(f_option, option)
|
||||
options = self.world.worlds[player].option_definitions
|
||||
options = self.multiworld.worlds[player].option_definitions
|
||||
if options:
|
||||
for f_option, option in options.items():
|
||||
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('Logic: %s\n' % self.world.logic[player])
|
||||
outfile.write('Dark Room Logic: %s\n' % self.world.dark_room_logic[player])
|
||||
outfile.write('Mode: %s\n' % self.world.mode[player])
|
||||
outfile.write('Goal: %s\n' % self.world.goal[player])
|
||||
if "triforce" in self.world.goal[player]: # triforce hunt
|
||||
outfile.write('Logic: %s\n' % self.multiworld.logic[player])
|
||||
outfile.write('Dark Room Logic: %s\n' % self.multiworld.dark_room_logic[player])
|
||||
outfile.write('Mode: %s\n' % self.multiworld.mode[player])
|
||||
outfile.write('Goal: %s\n' % self.multiworld.goal[player])
|
||||
if "triforce" in self.multiworld.goal[player]: # triforce hunt
|
||||
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" %
|
||||
self.world.triforce_pieces_required[player])
|
||||
outfile.write('Difficulty: %s\n' % self.world.difficulty[player])
|
||||
outfile.write('Item Functionality: %s\n' % self.world.item_functionality[player])
|
||||
outfile.write('Entrance Shuffle: %s\n' % self.world.shuffle[player])
|
||||
if self.world.shuffle[player] != "vanilla":
|
||||
outfile.write('Entrance Shuffle Seed %s\n' % self.world.worlds[player].er_seed)
|
||||
self.multiworld.triforce_pieces_required[player])
|
||||
outfile.write('Difficulty: %s\n' % self.multiworld.difficulty[player])
|
||||
outfile.write('Item Functionality: %s\n' % self.multiworld.item_functionality[player])
|
||||
outfile.write('Entrance Shuffle: %s\n' % self.multiworld.shuffle[player])
|
||||
if self.multiworld.shuffle[player] != "vanilla":
|
||||
outfile.write('Entrance Shuffle Seed %s\n' % self.multiworld.worlds[player].er_seed)
|
||||
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' %
|
||||
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' %
|
||||
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' %
|
||||
bool_to_text("g" in self.world.shop_shuffle[player] or
|
||||
"f" in self.world.shop_shuffle[player]))
|
||||
bool_to_text("g" in self.multiworld.shop_shuffle[player] or
|
||||
"f" in self.multiworld.shop_shuffle[player]))
|
||||
outfile.write('Custom Potion Shop: %s\n' %
|
||||
bool_to_text("w" in self.world.shop_shuffle[player]))
|
||||
outfile.write('Enemy health: %s\n' % self.world.enemy_health[player])
|
||||
outfile.write('Enemy damage: %s\n' % self.world.enemy_damage[player])
|
||||
bool_to_text("w" in self.multiworld.shop_shuffle[player]))
|
||||
outfile.write('Enemy health: %s\n' % self.multiworld.enemy_health[player])
|
||||
outfile.write('Enemy damage: %s\n' % self.multiworld.enemy_damage[player])
|
||||
outfile.write('Prize shuffle %s\n' %
|
||||
self.world.shuffle_prizes[player])
|
||||
self.multiworld.shuffle_prizes[player])
|
||||
if self.entrances:
|
||||
outfile.write('\n\nEntrances:\n\n')
|
||||
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_name(entry["player"])}: '
|
||||
if self.world.players > 1 else '', entry['entrance'],
|
||||
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.multiworld.get_player_name(entry["player"])}: '
|
||||
if self.multiworld.players > 1 else '', entry['entrance'],
|
||||
'<=>' if entry['direction'] == 'both' else
|
||||
'<=' if entry['direction'] == 'exit' else '=>',
|
||||
entry['exit']) for entry in self.entrances.values()]))
|
||||
|
@ -1464,7 +1464,7 @@ class Spoiler():
|
|||
for dungeon, medallion in self.medallions.items():
|
||||
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'.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 shop in self.shops))
|
||||
|
||||
for player in self.world.get_game_players("A Link to the Past"):
|
||||
if self.world.boss_shuffle[player] != 'none':
|
||||
bossmap = self.bosses[str(player)] if self.world.players > 1 else self.bosses
|
||||
for player in self.multiworld.get_game_players("A Link to the Past"):
|
||||
if self.multiworld.boss_shuffle[player] != 'none':
|
||||
bossmap = self.bosses[str(player)] if self.multiworld.players > 1 else self.bosses
|
||||
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\nPlaythrough:\n\n')
|
||||
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)))
|
||||
|
||||
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):
|
||||
|
|
|
@ -455,7 +455,7 @@ In addition, the following methods can be implemented and attributes can be set
|
|||
```python
|
||||
def generate_early(self) -> None:
|
||||
# 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
|
||||
|
@ -490,19 +490,19 @@ def create_items(self) -> None:
|
|||
# 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
|
||||
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):
|
||||
if item in exclude:
|
||||
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:
|
||||
self.world.itempool.append(item)
|
||||
self.multiworld.itempool.append(item)
|
||||
|
||||
# itempool and number of locations should match up.
|
||||
# If this is not the case we want to fill the itempool with junk.
|
||||
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
|
||||
|
@ -511,30 +511,30 @@ def create_items(self) -> None:
|
|||
def create_regions(self) -> None:
|
||||
# Add regions to the multiworld. "Menu" is the required starting point.
|
||||
# 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
|
||||
r.exits = [Entrance(self.player, "New game", r)] # or use r.exits.append
|
||||
# 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)
|
||||
r.locations = [MyGameLocation(self.player, location.name,
|
||||
self.location_name_to_id[location.name], 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
|
||||
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
|
||||
# they can also be connected at a later stage.
|
||||
self.world.get_entrance("New Game", self.player)\
|
||||
.connect(self.world.get_region("Main Area", self.player))
|
||||
self.world.get_entrance("Boss Door", self.player)\
|
||||
.connect(self.world.get_region("Boss Room", self.player))
|
||||
self.multiworld.get_entrance("New Game", self.player)
|
||||
.connect(self.multiworld.get_region("Main Area", self.player))
|
||||
self.multiworld.get_entrance("Boss Door", self.player)
|
||||
.connect(self.multiworld.get_region("Boss Room", self.player))
|
||||
|
||||
# If setting location access rules from data is easier here, set_rules can
|
||||
# possibly omitted.
|
||||
|
@ -545,14 +545,14 @@ def create_regions(self) -> None:
|
|||
```python
|
||||
def generate_basic(self) -> None:
|
||||
# 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"))
|
||||
self.world.completion_condition[self.player] = \
|
||||
self.multiworld.completion_condition[self.player] =
|
||||
lambda state: state.has("Victory", self.player)
|
||||
|
||||
# place item Herb into location Chest1 for some reason
|
||||
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
|
||||
# filled to avoid accidental duplicates:
|
||||
# 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 Items import get_item_type
|
||||
|
||||
|
||||
def set_rules(self) -> None:
|
||||
# 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
|
||||
# location generation or everything is in generate_basic
|
||||
|
||||
# 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))
|
||||
# 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))
|
||||
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))
|
||||
# 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
|
||||
state.has("Shield", self.player))
|
||||
# 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))
|
||||
# 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))
|
||||
# state also has .item_count() for items, .has_any() and.has_all() for sets
|
||||
# and .count_group() for groups
|
||||
# set_rule is likely to be a bit faster than add_rule
|
||||
|
||||
# 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
|
||||
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")
|
||||
# get_item_type needs to take player/world into account
|
||||
# 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\
|
||||
item.my_type == "weapon")
|
||||
# location.item_rule = ... is likely to be a bit faster
|
||||
|
@ -659,32 +660,33 @@ class MyGameWorld(World):
|
|||
```python
|
||||
from .Mod import generate_mod
|
||||
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
# 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 reads a json file, `json.dump()` can be used to generate that
|
||||
# code below is a dummy
|
||||
data = {
|
||||
"seed": self.world.seed_name, # to verify the server's multiworld
|
||||
"slot": self.world.player_name[self.player], # to connect to server
|
||||
"seed": self.multiworld.seed_name, # to verify the server's multiworld
|
||||
"slot": self.multiworld.player_name[self.player], # to connect to server
|
||||
"items": {location.name: location.item.name
|
||||
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
|
||||
"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,
|
||||
# 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
|
||||
"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
|
||||
src = Utils.get_options()["mygame_options"]["rom_file"]
|
||||
# or point to worlds/mygame/data/mod_template
|
||||
src = os.path.join(os.path.dirname(__file__), "data", "mod_template")
|
||||
# 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")
|
||||
# generate the file
|
||||
generate_mod(src, out_file, data)
|
||||
|
|
|
@ -11,18 +11,18 @@ from worlds.alttp.Items import ItemFactory
|
|||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
world: MultiWorld
|
||||
multiworld: MultiWorld
|
||||
_state_cache = {}
|
||||
|
||||
def get_state(self, items):
|
||||
if (self.world, tuple(items)) in self._state_cache:
|
||||
return self._state_cache[self.world, tuple(items)]
|
||||
state = CollectionState(self.world)
|
||||
if (self.multiworld, tuple(items)) in self._state_cache:
|
||||
return self._state_cache[self.multiworld, tuple(items)]
|
||||
state = CollectionState(self.multiworld)
|
||||
for item in items:
|
||||
item.classification = ItemClassification.progression
|
||||
state.collect(item)
|
||||
state.sweep_for_events()
|
||||
self._state_cache[self.world, tuple(items)] = state
|
||||
self._state_cache[self.multiworld, tuple(items)] = state
|
||||
return state
|
||||
|
||||
def get_path(self, state, region):
|
||||
|
@ -44,11 +44,11 @@ class TestBase(unittest.TestCase):
|
|||
items = item_pool[0]
|
||||
all_except = item_pool[1] if len(item_pool) > 1 else None
|
||||
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,
|
||||
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
|
||||
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,
|
||||
items=item_pool[0], missing_item=missing_item, entry=i):
|
||||
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):
|
||||
for i, (entrance, access, *item_pool) in enumerate(access_pool):
|
||||
items = item_pool[0]
|
||||
all_except = item_pool[1] if len(item_pool) > 1 else None
|
||||
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,
|
||||
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
|
||||
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,
|
||||
items=item_pool[0], missing_item=missing_item, entry=i):
|
||||
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):
|
||||
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.extend(ItemFactory(item_pool[0], 1))
|
||||
|
|
|
@ -14,46 +14,46 @@ from worlds import AutoWorld
|
|||
|
||||
class TestDungeon(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.multiworld = MultiWorld(1)
|
||||
args = Namespace()
|
||||
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)})
|
||||
self.world.set_options(args)
|
||||
self.world.set_default_common_options()
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
self.starting_regions = [] # Where to start exploring
|
||||
self.remove_exits = [] # Block dungeon exits
|
||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||
create_regions(self.world, 1)
|
||||
create_dungeons(self.world, 1)
|
||||
create_shops(self.world, 1)
|
||||
self.multiworld.difficulty_requirements[1] = difficulties['normal']
|
||||
create_regions(self.multiworld, 1)
|
||||
create_dungeons(self.multiworld, 1)
|
||||
create_shops(self.multiworld, 1)
|
||||
for exitname, regionname in mandatory_connections:
|
||||
connect_simple(self.world, exitname, regionname, 1)
|
||||
connect_simple(self.world, 'Big Bomb Shop', 'Big Bomb Shop', 1)
|
||||
self.world.get_region('Menu', 1).exits = []
|
||||
self.world.swamp_patch_required[1] = True
|
||||
self.world.worlds[1].set_rules()
|
||||
self.world.worlds[1].create_items()
|
||||
self.world.itempool.extend(get_dungeon_item_pool(self.world))
|
||||
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))
|
||||
connect_simple(self.multiworld, exitname, regionname, 1)
|
||||
connect_simple(self.multiworld, 'Big Bomb Shop', 'Big Bomb Shop', 1)
|
||||
self.multiworld.get_region('Menu', 1).exits = []
|
||||
self.multiworld.swamp_patch_required[1] = True
|
||||
self.multiworld.worlds[1].set_rules()
|
||||
self.multiworld.worlds[1].create_items()
|
||||
self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
|
||||
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):
|
||||
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:
|
||||
items = item_pool[0]
|
||||
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):
|
||||
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.extend(ItemFactory(item_pool[0], 1))
|
||||
else:
|
||||
items = ItemFactory(items, 1)
|
||||
state = CollectionState(self.world)
|
||||
state.reachable_regions[1].add(self.world.get_region('Menu', 1))
|
||||
state = CollectionState(self.multiworld)
|
||||
state.reachable_regions[1].add(self.multiworld.get_region('Menu', 1))
|
||||
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)
|
||||
for exit in region.exits:
|
||||
if exit.connected_region is not None:
|
||||
|
@ -63,4 +63,4 @@ class TestDungeon(unittest.TestCase):
|
|||
item.classification = ItemClassification.progression
|
||||
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)
|
|
@ -27,7 +27,7 @@ def generate_multi_world(players: int = 1) -> MultiWorld:
|
|||
|
||||
|
||||
class PlayerDefinition(object):
|
||||
world: MultiWorld
|
||||
multiworld: MultiWorld
|
||||
id: int
|
||||
menu: Region
|
||||
locations: List[Location]
|
||||
|
@ -36,7 +36,7 @@ class PlayerDefinition(object):
|
|||
regions: List[Region]
|
||||
|
||||
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.menu = menu
|
||||
self.locations = locations
|
||||
|
@ -48,7 +48,7 @@ class PlayerDefinition(object):
|
|||
region_tag = "_region" + str(len(self.regions))
|
||||
region_name = "player" + str(self.id) + region_tag
|
||||
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)
|
||||
|
||||
entrance = Entrance(self.id, region_name + "_entrance", parent)
|
||||
|
@ -57,7 +57,7 @@ class PlayerDefinition(object):
|
|||
entrance.access_rule = access_rule
|
||||
|
||||
self.regions.append(region)
|
||||
self.world.regions.append(region)
|
||||
self.multiworld.regions.append(region)
|
||||
|
||||
return region
|
||||
|
||||
|
|
|
@ -7,15 +7,15 @@ gen_steps = ["generate_early", "create_regions", "create_items", "set_rules", "g
|
|||
|
||||
|
||||
def setup_default_world(world_type) -> MultiWorld:
|
||||
world = MultiWorld(1)
|
||||
world.game[1] = world_type.game
|
||||
world.player_name = {1: "Tester"}
|
||||
world.set_seed()
|
||||
multiworld = MultiWorld(1)
|
||||
multiworld.game[1] = world_type.game
|
||||
multiworld.player_name = {1: "Tester"}
|
||||
multiworld.set_seed()
|
||||
args = Namespace()
|
||||
for name, option in world_type.option_definitions.items():
|
||||
setattr(args, name, {1: option.from_any(option.default)})
|
||||
world.set_options(args)
|
||||
world.set_default_common_options()
|
||||
multiworld.set_options(args)
|
||||
multiworld.set_default_common_options()
|
||||
for step in gen_steps:
|
||||
call_all(world, step)
|
||||
return world
|
||||
call_all(multiworld, step)
|
||||
return multiworld
|
||||
|
|
|
@ -14,23 +14,23 @@ from worlds import AutoWorld
|
|||
|
||||
class TestInverted(TestBase):
|
||||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.multiworld = MultiWorld(1)
|
||||
args = Namespace()
|
||||
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)})
|
||||
self.world.set_options(args)
|
||||
self.world.set_default_common_options()
|
||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||
self.world.mode[1] = "inverted"
|
||||
create_inverted_regions(self.world, 1)
|
||||
create_dungeons(self.world, 1)
|
||||
create_shops(self.world, 1)
|
||||
link_inverted_entrances(self.world, 1)
|
||||
self.world.worlds[1].create_items()
|
||||
self.world.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.world.itempool.extend(get_dungeon_item_pool(self.world))
|
||||
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.world.get_location('Agahnim 1', 1).item = None
|
||||
self.world.get_location('Agahnim 2', 1).item = None
|
||||
mark_light_world_regions(self.world, 1)
|
||||
self.world.worlds[1].set_rules()
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
self.multiworld.difficulty_requirements[1] = difficulties['normal']
|
||||
self.multiworld.mode[1] = "inverted"
|
||||
create_inverted_regions(self.multiworld, 1)
|
||||
create_dungeons(self.multiworld, 1)
|
||||
create_shops(self.multiworld, 1)
|
||||
link_inverted_entrances(self.multiworld, 1)
|
||||
self.multiworld.worlds[1].create_items()
|
||||
self.multiworld.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
|
||||
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.multiworld.get_location('Agahnim 1', 1).item = None
|
||||
self.multiworld.get_location('Agahnim 2', 1).item = None
|
||||
mark_light_world_regions(self.multiworld, 1)
|
||||
self.multiworld.worlds[1].set_rules()
|
||||
|
|
|
@ -14,16 +14,16 @@ from worlds import AutoWorld
|
|||
class TestInvertedBombRules(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.world.mode[1] = "inverted"
|
||||
self.multiworld = MultiWorld(1)
|
||||
self.multiworld.mode[1] = "inverted"
|
||||
args = Namespace
|
||||
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)})
|
||||
self.world.set_options(args)
|
||||
self.world.set_default_common_options()
|
||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||
create_inverted_regions(self.world, 1)
|
||||
create_dungeons(self.world, 1)
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
self.multiworld.difficulty_requirements[1] = difficulties['normal']
|
||||
create_inverted_regions(self.multiworld, 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.
|
||||
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)
|
||||
for entrance_name in (entrances + must_exits):
|
||||
if entrance_name not in ['Desert Palace Entrance (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)']:
|
||||
entrance = self.world.get_entrance(entrance_name, 1)
|
||||
connect_entrance(self.world, entrance_name, 'Inverted Big Bomb Shop', 1)
|
||||
set_inverted_big_bomb_rules(self.world, 1)
|
||||
entrance = self.multiworld.get_entrance(entrance_name, 1)
|
||||
connect_entrance(self.multiworld, entrance_name, 'Inverted Big Bomb Shop', 1)
|
||||
set_inverted_big_bomb_rules(self.multiworld, 1)
|
||||
entrance.connected_region.entrances.remove(entrance)
|
||||
entrance.connected_region = None
|
||||
|
||||
|
@ -45,9 +45,9 @@ class TestInvertedBombRules(unittest.TestCase):
|
|||
|
||||
def testInvalidEntrances(self):
|
||||
for entrance_name in ['Desert Palace Entrance (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)']:
|
||||
entrance = self.world.get_entrance(entrance_name, 1)
|
||||
connect_entrance(self.world, entrance_name, 'Inverted Big Bomb Shop', 1)
|
||||
entrance = self.multiworld.get_entrance(entrance_name, 1)
|
||||
connect_entrance(self.multiworld, entrance_name, 'Inverted Big Bomb Shop', 1)
|
||||
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 = None
|
||||
|
|
|
@ -15,24 +15,24 @@ from worlds import AutoWorld
|
|||
|
||||
class TestInvertedMinor(TestBase):
|
||||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.multiworld = MultiWorld(1)
|
||||
args = Namespace()
|
||||
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)})
|
||||
self.world.set_options(args)
|
||||
self.world.set_default_common_options()
|
||||
self.world.mode[1] = "inverted"
|
||||
self.world.logic[1] = "minorglitches"
|
||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||
create_inverted_regions(self.world, 1)
|
||||
create_dungeons(self.world, 1)
|
||||
create_shops(self.world, 1)
|
||||
link_inverted_entrances(self.world, 1)
|
||||
self.world.worlds[1].create_items()
|
||||
self.world.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.world.itempool.extend(get_dungeon_item_pool(self.world))
|
||||
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.world.get_location('Agahnim 1', 1).item = None
|
||||
self.world.get_location('Agahnim 2', 1).item = None
|
||||
mark_light_world_regions(self.world, 1)
|
||||
self.world.worlds[1].set_rules()
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
self.multiworld.mode[1] = "inverted"
|
||||
self.multiworld.logic[1] = "minorglitches"
|
||||
self.multiworld.difficulty_requirements[1] = difficulties['normal']
|
||||
create_inverted_regions(self.multiworld, 1)
|
||||
create_dungeons(self.multiworld, 1)
|
||||
create_shops(self.multiworld, 1)
|
||||
link_inverted_entrances(self.multiworld, 1)
|
||||
self.multiworld.worlds[1].create_items()
|
||||
self.multiworld.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
|
||||
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.multiworld.get_location('Agahnim 1', 1).item = None
|
||||
self.multiworld.get_location('Agahnim 2', 1).item = None
|
||||
mark_light_world_regions(self.multiworld, 1)
|
||||
self.multiworld.worlds[1].set_rules()
|
||||
|
|
|
@ -16,26 +16,26 @@ from worlds import AutoWorld
|
|||
|
||||
class TestInvertedOWG(TestBase):
|
||||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.multiworld = MultiWorld(1)
|
||||
args = Namespace()
|
||||
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)})
|
||||
self.world.set_options(args)
|
||||
self.world.set_default_common_options()
|
||||
self.world.logic[1] = "owglitches"
|
||||
self.world.mode[1] = "inverted"
|
||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||
create_inverted_regions(self.world, 1)
|
||||
create_dungeons(self.world, 1)
|
||||
create_shops(self.world, 1)
|
||||
link_inverted_entrances(self.world, 1)
|
||||
self.world.worlds[1].create_items()
|
||||
self.world.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.world.itempool.extend(get_dungeon_item_pool(self.world))
|
||||
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.world.get_location('Agahnim 1', 1).item = None
|
||||
self.world.get_location('Agahnim 2', 1).item = None
|
||||
self.world.precollected_items[1].clear()
|
||||
self.world.itempool.append(ItemFactory('Pegasus Boots', 1))
|
||||
mark_light_world_regions(self.world, 1)
|
||||
self.world.worlds[1].set_rules()
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
self.multiworld.logic[1] = "owglitches"
|
||||
self.multiworld.mode[1] = "inverted"
|
||||
self.multiworld.difficulty_requirements[1] = difficulties['normal']
|
||||
create_inverted_regions(self.multiworld, 1)
|
||||
create_dungeons(self.multiworld, 1)
|
||||
create_shops(self.multiworld, 1)
|
||||
link_inverted_entrances(self.multiworld, 1)
|
||||
self.multiworld.worlds[1].create_items()
|
||||
self.multiworld.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
|
||||
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.multiworld.get_location('Agahnim 1', 1).item = None
|
||||
self.multiworld.get_location('Agahnim 2', 1).item = None
|
||||
self.multiworld.precollected_items[1].clear()
|
||||
self.multiworld.itempool.append(ItemFactory('Pegasus Boots', 1))
|
||||
mark_light_world_regions(self.multiworld, 1)
|
||||
self.multiworld.worlds[1].set_rules()
|
||||
|
|
|
@ -30,28 +30,28 @@ def MCItemFactory(items, player: int):
|
|||
class TestMinecraft(TestBase):
|
||||
|
||||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.world.game[1] = "Minecraft"
|
||||
self.world.worlds[1] = MinecraftWorld(self.world, 1)
|
||||
self.multiworld = MultiWorld(1)
|
||||
self.multiworld.game[1] = "Minecraft"
|
||||
self.multiworld.worlds[1] = MinecraftWorld(self.multiworld, 1)
|
||||
exclusion_pools = ['hard', 'unreasonable', 'postgame']
|
||||
for pool in exclusion_pools:
|
||||
setattr(self.world, f"include_{pool}_advancements", {1: False})
|
||||
setattr(self.world, "advancement_goal", {1: AdvancementGoal(30)})
|
||||
setattr(self.world, "egg_shards_required", {1: EggShardsRequired(0)})
|
||||
setattr(self.world, "egg_shards_available", {1: EggShardsAvailable(0)})
|
||||
setattr(self.world, "required_bosses", {1: BossGoal(1)}) # ender dragon
|
||||
setattr(self.world, "shuffle_structures", {1: ShuffleStructures(False)})
|
||||
setattr(self.world, "bee_traps", {1: BeeTraps(0)})
|
||||
setattr(self.world, "combat_difficulty", {1: CombatDifficulty(1)}) # normal
|
||||
setattr(self.world, "structure_compasses", {1: Toggle(False)})
|
||||
setattr(self.world, "death_link", {1: Toggle(False)})
|
||||
AutoWorld.call_single(self.world, "create_regions", 1)
|
||||
AutoWorld.call_single(self.world, "generate_basic", 1)
|
||||
AutoWorld.call_single(self.world, "set_rules", 1)
|
||||
setattr(self.multiworld, f"include_{pool}_advancements", {1: False})
|
||||
setattr(self.multiworld, "advancement_goal", {1: AdvancementGoal(30)})
|
||||
setattr(self.multiworld, "egg_shards_required", {1: EggShardsRequired(0)})
|
||||
setattr(self.multiworld, "egg_shards_available", {1: EggShardsAvailable(0)})
|
||||
setattr(self.multiworld, "required_bosses", {1: BossGoal(1)}) # ender dragon
|
||||
setattr(self.multiworld, "shuffle_structures", {1: ShuffleStructures(False)})
|
||||
setattr(self.multiworld, "bee_traps", {1: BeeTraps(0)})
|
||||
setattr(self.multiworld, "combat_difficulty", {1: CombatDifficulty(1)}) # normal
|
||||
setattr(self.multiworld, "structure_compasses", {1: Toggle(False)})
|
||||
setattr(self.multiworld, "death_link", {1: Toggle(False)})
|
||||
AutoWorld.call_single(self.multiworld, "create_regions", 1)
|
||||
AutoWorld.call_single(self.multiworld, "generate_basic", 1)
|
||||
AutoWorld.call_single(self.multiworld, "set_rules", 1)
|
||||
|
||||
def _get_items(self, item_pool, all_except):
|
||||
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.extend(MCItemFactory(item_pool[0], 1))
|
||||
|
|
|
@ -15,23 +15,23 @@ from worlds import AutoWorld
|
|||
|
||||
class TestMinor(TestBase):
|
||||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.multiworld = MultiWorld(1)
|
||||
args = Namespace()
|
||||
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)})
|
||||
self.world.set_options(args)
|
||||
self.world.set_default_common_options()
|
||||
self.world.logic[1] = "minorglitches"
|
||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||
self.world.worlds[1].er_seed = 0
|
||||
self.world.worlds[1].create_regions()
|
||||
self.world.worlds[1].create_items()
|
||||
self.world.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.world.itempool.extend(get_dungeon_item_pool(self.world))
|
||||
self.world.itempool.extend(ItemFactory(
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
self.multiworld.logic[1] = "minorglitches"
|
||||
self.multiworld.difficulty_requirements[1] = difficulties['normal']
|
||||
self.multiworld.worlds[1].er_seed = 0
|
||||
self.multiworld.worlds[1].create_regions()
|
||||
self.multiworld.worlds[1].create_items()
|
||||
self.multiworld.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
|
||||
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.world.get_location('Agahnim 2', 1).item = None
|
||||
mark_dark_world_regions(self.world, 1)
|
||||
self.world.worlds[1].set_rules()
|
||||
self.multiworld.get_location('Agahnim 1', 1).item = None
|
||||
self.multiworld.get_location('Agahnim 2', 1).item = None
|
||||
mark_dark_world_regions(self.multiworld, 1)
|
||||
self.multiworld.worlds[1].set_rules()
|
||||
|
|
|
@ -16,23 +16,23 @@ from worlds import AutoWorld
|
|||
|
||||
class TestVanillaOWG(TestBase):
|
||||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.multiworld = MultiWorld(1)
|
||||
args = Namespace()
|
||||
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)})
|
||||
self.world.set_options(args)
|
||||
self.world.set_default_common_options()
|
||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||
self.world.logic[1] = "owglitches"
|
||||
self.world.worlds[1].er_seed = 0
|
||||
self.world.worlds[1].create_regions()
|
||||
self.world.worlds[1].create_items()
|
||||
self.world.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.world.itempool.extend(get_dungeon_item_pool(self.world))
|
||||
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.world.get_location('Agahnim 1', 1).item = None
|
||||
self.world.get_location('Agahnim 2', 1).item = None
|
||||
self.world.precollected_items[1].clear()
|
||||
self.world.itempool.append(ItemFactory('Pegasus Boots', 1))
|
||||
mark_dark_world_regions(self.world, 1)
|
||||
self.world.worlds[1].set_rules()
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
self.multiworld.difficulty_requirements[1] = difficulties['normal']
|
||||
self.multiworld.logic[1] = "owglitches"
|
||||
self.multiworld.worlds[1].er_seed = 0
|
||||
self.multiworld.worlds[1].create_regions()
|
||||
self.multiworld.worlds[1].create_items()
|
||||
self.multiworld.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
|
||||
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.multiworld.get_location('Agahnim 1', 1).item = None
|
||||
self.multiworld.get_location('Agahnim 2', 1).item = None
|
||||
self.multiworld.precollected_items[1].clear()
|
||||
self.multiworld.itempool.append(ItemFactory('Pegasus Boots', 1))
|
||||
mark_dark_world_regions(self.multiworld, 1)
|
||||
self.multiworld.worlds[1].set_rules()
|
|
@ -14,21 +14,21 @@ from worlds import AutoWorld
|
|||
|
||||
class TestVanilla(TestBase):
|
||||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.multiworld = MultiWorld(1)
|
||||
args = Namespace()
|
||||
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)})
|
||||
self.world.set_options(args)
|
||||
self.world.set_default_common_options()
|
||||
self.world.logic[1] = "noglitches"
|
||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||
self.world.worlds[1].er_seed = 0
|
||||
self.world.worlds[1].create_regions()
|
||||
self.world.worlds[1].create_items()
|
||||
self.world.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.world.itempool.extend(get_dungeon_item_pool(self.world))
|
||||
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.world.get_location('Agahnim 1', 1).item = None
|
||||
self.world.get_location('Agahnim 2', 1).item = None
|
||||
mark_dark_world_regions(self.world, 1)
|
||||
self.world.worlds[1].set_rules()
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
self.multiworld.logic[1] = "noglitches"
|
||||
self.multiworld.difficulty_requirements[1] = difficulties['normal']
|
||||
self.multiworld.worlds[1].er_seed = 0
|
||||
self.multiworld.worlds[1].create_regions()
|
||||
self.multiworld.worlds[1].create_items()
|
||||
self.multiworld.required_medallions[1] = ['Ether', 'Quake']
|
||||
self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld))
|
||||
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.multiworld.get_location('Agahnim 1', 1).item = None
|
||||
self.multiworld.get_location('Agahnim 2', 1).item = None
|
||||
mark_dark_world_regions(self.multiworld, 1)
|
||||
self.multiworld.worlds[1].set_rules()
|
|
@ -22,29 +22,29 @@ class WorldTestBase(unittest.TestCase):
|
|||
def world_setup(self, seed: typing.Optional[int] = None) -> None:
|
||||
if not hasattr(self, "game"):
|
||||
raise NotImplementedError("didn't define game name")
|
||||
self.world = MultiWorld(1)
|
||||
self.world.game[1] = self.game
|
||||
self.world.player_name = {1: "Tester"}
|
||||
self.world.set_seed(seed)
|
||||
self.multiworld = MultiWorld(1)
|
||||
self.multiworld.game[1] = self.game
|
||||
self.multiworld.player_name = {1: "Tester"}
|
||||
self.multiworld.set_seed(seed)
|
||||
args = Namespace()
|
||||
for name, option in AutoWorld.AutoWorldRegister.world_types[self.game].option_definitions.items():
|
||||
setattr(args, name, {
|
||||
1: option.from_any(self.options.get(name, getattr(option, "default")))
|
||||
})
|
||||
self.world.set_options(args)
|
||||
self.world.set_default_common_options()
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
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:
|
||||
if isinstance(item_names, str):
|
||||
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:
|
||||
self.world.state.collect(item)
|
||||
self.multiworld.state.collect(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:
|
||||
return 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]:
|
||||
if isinstance(item_names, str):
|
||||
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]:
|
||||
""" 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):
|
||||
items = (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:
|
||||
if isinstance(items, Item):
|
||||
items = (items,)
|
||||
for item in items:
|
||||
if item.location and item.location.event and item.location in self.world.state.events:
|
||||
self.world.state.events.remove(item.location)
|
||||
self.world.state.remove(item)
|
||||
if item.location and item.location.event and item.location in self.multiworld.state.events:
|
||||
self.multiworld.state.events.remove(item.location)
|
||||
self.multiworld.state.remove(item)
|
||||
|
||||
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:
|
||||
return self.world.state.count(item_name, 1)
|
||||
return self.multiworld.state.count(item_name, 1)
|
||||
|
||||
def assertAccessDependency(self,
|
||||
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]
|
||||
|
||||
self.collect_all_but(all_items)
|
||||
for location in self.world.get_locations():
|
||||
self.assertEqual(self.world.state.can_reach(location), location.name not in locations)
|
||||
for location in self.multiworld.get_locations():
|
||||
self.assertEqual(self.multiworld.state.can_reach(location), location.name not in locations)
|
||||
for item_names in possible_items:
|
||||
items = self.collect_by_name(item_names)
|
||||
for location in locations:
|
||||
|
@ -95,4 +95,4 @@ class WorldTestBase(unittest.TestCase):
|
|||
self.remove(items)
|
||||
|
||||
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)
|
||||
|
|
|
@ -9,7 +9,7 @@ class OptionsTest(ZillionTestBase):
|
|||
|
||||
def test_validate_default(self) -> None:
|
||||
self.world_setup()
|
||||
validate(self.world, 1)
|
||||
validate(self.multiworld, 1)
|
||||
|
||||
def test_vblr_ap_to_zz(self) -> None:
|
||||
""" 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():
|
||||
self.options = {option_name: value}
|
||||
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
|
||||
|
||||
# TODO: test validate with invalid combinations of options
|
||||
|
|
|
@ -13,7 +13,7 @@ class ZillionTestBase(WorldTestBase):
|
|||
This makes sure that gun 3 is required by making all the canisters
|
||||
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
|
||||
for zz_loc_name, zz_loc in zz_world.zz_system.randomizer.locations.items():
|
||||
if zz_loc_name.startswith("r15c6"):
|
||||
|
|
|
@ -182,7 +182,7 @@ class World(metaclass=AutoWorldRegister):
|
|||
web: WebWorld = WebWorld()
|
||||
|
||||
# autoset on creation:
|
||||
world: "MultiWorld"
|
||||
multiworld: "MultiWorld"
|
||||
player: int
|
||||
|
||||
# automatically generated
|
||||
|
@ -196,7 +196,7 @@ class World(metaclass=AutoWorldRegister):
|
|||
__file__: str # path it was loaded from
|
||||
|
||||
def __init__(self, world: "MultiWorld", player: int):
|
||||
self.world = world
|
||||
self.multiworld = world
|
||||
self.player = player
|
||||
|
||||
# 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:
|
||||
"""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.")
|
||||
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
|
||||
def collect_item(self, state: "CollectionState", item: "Item", remove: bool = False) -> Optional[str]:
|
||||
|
|
|
@ -81,7 +81,7 @@ def KholdstareDefeatRule(state, player: int) -> bool:
|
|||
state.has('Fire Rod', player) or
|
||||
(
|
||||
state.has('Bombos', player) and
|
||||
(state.has_sword(player) or state.world.swordless[player])
|
||||
(state.has_sword(player) or state.multiworld.swordless[player])
|
||||
)
|
||||
) and
|
||||
(
|
||||
|
@ -90,7 +90,7 @@ def KholdstareDefeatRule(state, player: int) -> bool:
|
|||
(
|
||||
state.has('Fire Rod', player) and
|
||||
state.has('Bombos', player) and
|
||||
state.world.swordless[player] and
|
||||
state.multiworld.swordless[player] and
|
||||
state.can_extend_magic(player, 16)
|
||||
)
|
||||
)
|
||||
|
@ -114,7 +114,7 @@ def AgahnimDefeatRule(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 \
|
||||
state.has_fire_source(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)
|
||||
common = can_hurt and state.has_fire_source(player)
|
||||
# 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
|
||||
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
|
||||
|
|
|
@ -18,7 +18,7 @@ def create_dungeons(world, player):
|
|||
dungeon.boss = BossFactory(default_boss, player) if default_boss else None
|
||||
for region in dungeon.regions:
|
||||
world.get_region(region, player).dungeon = dungeon
|
||||
dungeon.world = world
|
||||
dungeon.multiworld = world
|
||||
return dungeon
|
||||
|
||||
ES = make_dungeon('Hyrule Castle', None, ['Hyrule Castle', 'Sewers', 'Sewer Drop', 'Sewers (Dark)', 'Sanctuary'],
|
||||
|
|
|
@ -224,7 +224,7 @@ for diff in {'easy', 'normal', 'hard', 'expert'}:
|
|||
|
||||
def generate_itempool(world):
|
||||
player = world.player
|
||||
world = world.world
|
||||
world = world.multiworld
|
||||
|
||||
if world.difficulty[player] not in difficulties:
|
||||
raise NotImplementedError(f"Diffulty {world.difficulty[player]}")
|
||||
|
@ -286,7 +286,7 @@ def generate_itempool(world):
|
|||
region = world.get_region('Light World', player)
|
||||
|
||||
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)
|
||||
world.clear_location_cache()
|
||||
|
|
|
@ -13,7 +13,7 @@ from worlds.alttp.Options import smallkey_shuffle
|
|||
|
||||
def set_rules(world):
|
||||
player = world.player
|
||||
world = world.world
|
||||
world = world.multiworld
|
||||
if world.logic[player] == 'nologic':
|
||||
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
|
||||
|
@ -81,7 +81,7 @@ def set_rules(world):
|
|||
set_big_bomb_rules(world, player)
|
||||
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)
|
||||
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:
|
||||
set_inverted_big_bomb_rules(world, player)
|
||||
|
||||
|
@ -97,9 +97,9 @@ def set_rules(world):
|
|||
|
||||
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']:
|
||||
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')
|
||||
|
||||
|
@ -204,7 +204,7 @@ def global_rules(world, player):
|
|||
((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or
|
||||
(state.has('Cane of Byrna', player) and
|
||||
(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))
|
||||
|
@ -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 - 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_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 - 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 - 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.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
|
||||
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 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_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 (
|
||||
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('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 - 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))
|
||||
# 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
|
||||
|
@ -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 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),
|
||||
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),
|
||||
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]:
|
||||
set_rule(world.get_entrance('Ganons Tower Big Key Door', 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),
|
||||
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),
|
||||
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),
|
||||
lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 3))
|
||||
set_rule(world.get_entrance('Ganons Tower Moldorm Door', player),
|
||||
lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 4))
|
||||
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))
|
||||
ganon = world.get_location('Ganon', player)
|
||||
set_rule(ganon, lambda state: GanonDefeatRule(state, player))
|
||||
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':
|
||||
add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player))
|
||||
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_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':
|
||||
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):
|
||||
|
|
|
@ -163,7 +163,7 @@ class ALTTPWorld(World):
|
|||
check_enemizer(self.enemizer_path)
|
||||
|
||||
player = self.player
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
|
||||
# system for sharing ER layouts
|
||||
self.er_seed = str(world.random.randint(0, 2 ** 64))
|
||||
|
@ -195,7 +195,7 @@ class ALTTPWorld(World):
|
|||
|
||||
def create_regions(self):
|
||||
player = self.player
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
|
||||
world.triforce_pieces_available[player] = max(world.triforce_pieces_available[player],
|
||||
world.triforce_pieces_required[player])
|
||||
|
@ -219,21 +219,21 @@ class ALTTPWorld(World):
|
|||
link_entrances(world, player)
|
||||
mark_light_world_regions(world, player)
|
||||
for region_name, entrance_name in indirect_connections_not_inverted.items():
|
||||
world.register_indirect_condition(self.world.get_region(region_name, player),
|
||||
self.world.get_entrance(entrance_name, player))
|
||||
world.register_indirect_condition(world.get_region(region_name, player),
|
||||
world.get_entrance(entrance_name, player))
|
||||
else:
|
||||
link_inverted_entrances(world, player)
|
||||
mark_dark_world_regions(world, player)
|
||||
for region_name, entrance_name in indirect_connections_inverted.items():
|
||||
world.register_indirect_condition(self.world.get_region(region_name, player),
|
||||
self.world.get_entrance(entrance_name, player))
|
||||
world.register_indirect_condition(world.get_region(region_name, player),
|
||||
world.get_entrance(entrance_name, player))
|
||||
|
||||
world.random = old_random
|
||||
plando_connect(world, player)
|
||||
|
||||
for region_name, entrance_name in indirect_connections.items():
|
||||
world.register_indirect_condition(self.world.get_region(region_name, player),
|
||||
self.world.get_entrance(entrance_name, player))
|
||||
world.register_indirect_condition(world.get_region(region_name, player),
|
||||
world.get_entrance(entrance_name, player))
|
||||
|
||||
|
||||
def collect_item(self, state: CollectionState, item: Item, remove=False):
|
||||
|
@ -278,15 +278,15 @@ class ALTTPWorld(World):
|
|||
if 'Sword' in item_name:
|
||||
if state.has('Golden Sword', item.player):
|
||||
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:
|
||||
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:
|
||||
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'
|
||||
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'
|
||||
elif 'Glove' in item_name:
|
||||
if state.has('Titans Mitts', item.player):
|
||||
|
@ -298,20 +298,20 @@ class ALTTPWorld(World):
|
|||
elif 'Shield' in item_name:
|
||||
if state.has('Mirror Shield', item.player):
|
||||
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'
|
||||
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'
|
||||
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'
|
||||
elif 'Bow' in item_name:
|
||||
if state.has('Silver Bow', item.player):
|
||||
return
|
||||
elif state.has('Bow', item.player) and (self.world.difficulty_requirements[item.player].progressive_bow_limit >= 2
|
||||
or self.world.logic[item.player] == 'noglitches'
|
||||
or self.world.swordless[item.player]): # modes where silver bow is always required for ganon
|
||||
elif state.has('Bow', item.player) and (self.multiworld.difficulty_requirements[item.player].progressive_bow_limit >= 2
|
||||
or self.multiworld.logic[item.player] == 'noglitches'
|
||||
or self.multiworld.swordless[item.player]): # modes where silver bow is always required for ganon
|
||||
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'
|
||||
elif item.advancement:
|
||||
return item_name
|
||||
|
@ -319,7 +319,7 @@ class ALTTPWorld(World):
|
|||
def pre_fill(self):
|
||||
from Fill import fill_restrictive, FillError
|
||||
attempts = 5
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
player = self.player
|
||||
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']]
|
||||
|
@ -362,7 +362,7 @@ class ALTTPWorld(World):
|
|||
ShopSlotFill(world)
|
||||
|
||||
def use_enemizer(self):
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
player = self.player
|
||||
return (world.boss_shuffle[player] or world.enemy_shuffle[player]
|
||||
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
|
||||
|
@ -370,7 +370,7 @@ class ALTTPWorld(World):
|
|||
or world.killable_thieves[player])
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
player = self.player
|
||||
try:
|
||||
use_enemizer = self.use_enemizer()
|
||||
|
@ -409,7 +409,7 @@ class ALTTPWorld(World):
|
|||
deathlink=world.death_link[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)
|
||||
patch = LttPDeltaPatch(os.path.splitext(rompath)[0]+LttPDeltaPatch.patch_file_ending, player=player,
|
||||
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
|
||||
if rom_name:
|
||||
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:
|
||||
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}.")
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
if self.world.goal[self.player] == "icerodhunt":
|
||||
if self.multiworld.goal[self.player] == "icerodhunt":
|
||||
item = "Nothing"
|
||||
else:
|
||||
item = self.world.random.choice(extras_list)
|
||||
return GetBeemizerItem(self.world, self.player, item)
|
||||
item = self.multiworld.random.choice(extras_list)
|
||||
return GetBeemizerItem(self.multiworld, self.player, item)
|
||||
|
||||
def get_pre_fill_items(self):
|
||||
res = []
|
||||
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:
|
||||
for item in dungeon.all_items:
|
||||
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):
|
||||
def _lttp_has_key(self, item, player, count: int = 1):
|
||||
if self.world.logic[player] == 'nologic':
|
||||
if self.multiworld.logic[player] == 'nologic':
|
||||
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.prog_items[item, player] >= count
|
||||
|
|
|
@ -43,7 +43,7 @@ class ArchipIDLEWorld(World):
|
|||
|
||||
def generate_basic(self):
|
||||
item_table_copy = list(item_table)
|
||||
self.world.random.shuffle(item_table_copy)
|
||||
self.multiworld.random.shuffle(item_table_copy)
|
||||
|
||||
item_pool = []
|
||||
for i in range(100):
|
||||
|
@ -55,31 +55,31 @@ class ArchipIDLEWorld(World):
|
|||
)
|
||||
item_pool.append(item)
|
||||
|
||||
self.world.itempool += item_pool
|
||||
self.multiworld.itempool += item_pool
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return Item(name, ItemClassification.progression, self.item_name_to_id[name], self.player)
|
||||
|
||||
def create_regions(self):
|
||||
self.world.regions += [
|
||||
create_region(self.world, self.player, 'Menu', None, ['Entrance to IDLE Zone']),
|
||||
create_region(self.world, self.player, 'IDLE Zone', self.location_name_to_id)
|
||||
self.multiworld.regions += [
|
||||
create_region(self.multiworld, self.player, 'Menu', None, ['Entrance to IDLE Zone']),
|
||||
create_region(self.multiworld, self.player, 'IDLE Zone', self.location_name_to_id)
|
||||
]
|
||||
|
||||
# link up our region with the entrance we just made
|
||||
self.world.get_entrance('Entrance to IDLE Zone', self.player)\
|
||||
.connect(self.world.get_region('IDLE Zone', self.player))
|
||||
self.multiworld.get_entrance('Entrance to IDLE Zone', self.player)\
|
||||
.connect(self.multiworld.get_region('IDLE Zone', self.player))
|
||||
|
||||
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):
|
||||
region = Region(name, RegionType.Generic, name, player)
|
||||
region.world = world
|
||||
region.multiworld = world
|
||||
if locations:
|
||||
for location_name in locations.keys():
|
||||
location = ArchipIDLELocation(player, location_name, locations[location_name], region)
|
||||
|
|
|
@ -38,12 +38,12 @@ class ChecksFinderWorld(World):
|
|||
|
||||
def _get_checksfinder_data(self):
|
||||
return {
|
||||
'world_seed': self.world.slot_seeds[self.player].getrandbits(32),
|
||||
'seed_name': self.world.seed_name,
|
||||
'player_name': self.world.get_player_name(self.player),
|
||||
'world_seed': self.multiworld.slot_seeds[self.player].getrandbits(32),
|
||||
'seed_name': self.multiworld.seed_name,
|
||||
'player_name': self.multiworld.get_player_name(self.player),
|
||||
'player_id': self.player,
|
||||
'client_version': client_version,
|
||||
'race': self.world.is_race,
|
||||
'race': self.multiworld.is_race,
|
||||
}
|
||||
|
||||
def generate_basic(self):
|
||||
|
@ -61,15 +61,15 @@ class ChecksFinderWorld(World):
|
|||
# Convert itempool into real items
|
||||
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):
|
||||
set_rules(self.world, self.player)
|
||||
set_completion_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
set_completion_rules(self.multiworld, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
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)
|
||||
for loc_name, loc_data in advancement_table.items()
|
||||
if loc_data.region == region_name]
|
||||
|
@ -77,13 +77,13 @@ class ChecksFinderWorld(World):
|
|||
ret.exits.append(Entrance(self.player, exit, ret))
|
||||
return ret
|
||||
|
||||
self.world.regions += [ChecksFinderRegion(*r) for r in checksfinder_regions]
|
||||
link_checksfinder_structures(self.world, self.player)
|
||||
self.multiworld.regions += [ChecksFinderRegion(*r) for r in checksfinder_regions]
|
||||
link_checksfinder_structures(self.multiworld, self.player)
|
||||
|
||||
def fill_slot_data(self):
|
||||
slot_data = self._get_checksfinder_data()
|
||||
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}:
|
||||
slot_data[option_name] = int(option.value)
|
||||
return slot_data
|
||||
|
|
|
@ -79,7 +79,7 @@ class DarkSouls3World(World):
|
|||
|
||||
def create_regions(self):
|
||||
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
|
||||
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
|
||||
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))
|
||||
firelink_shrine_region.exits.append(Entrance(self.player, "Goto Kiln Of The First Flame",
|
||||
firelink_shrine_region))
|
||||
firelink_shrine_region.exits.append(Entrance(self.player, "Goto Bell Tower",
|
||||
firelink_shrine_region))
|
||||
self.world.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.world.get_entrance("Goto Bell Tower", self.player).connect(firelink_shrine_bell_tower_region)
|
||||
self.multiworld.get_entrance("Goto High Wall of Lothric", self.player).connect(high_wall_of_lothric_region)
|
||||
self.multiworld.get_entrance("Goto Kiln Of The First Flame", self.player).connect(kiln_of_the_first_flame_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))
|
||||
high_wall_of_lothric_region.exits.append(Entrance(self.player, "Goto Lothric Castle",
|
||||
high_wall_of_lothric_region))
|
||||
self.world.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 Undead Settlement", self.player).connect(undead_settlement_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))
|
||||
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 Farron keep", road_of_sacrifices_region))
|
||||
self.world.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 Cathedral", self.player).connect(cathedral_of_the_deep_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))
|
||||
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))
|
||||
catacombs_of_carthus_region.exits.append(Entrance(self.player, "Goto Smouldering Lake",
|
||||
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)
|
||||
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))
|
||||
irithyll_of_the_boreal_valley_region.exits.append(Entrance(self.player, "Goto Anor Londo",
|
||||
irithyll_of_the_boreal_valley_region))
|
||||
self.world.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 Irithyll dungeon", self.player).connect(irithyll_dungeon_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 Profaned capital", irithyll_dungeon_region))
|
||||
self.world.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 Archdragon peak", self.player).connect(archdragon_peak_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 Grand Archives", lothric_castle_region))
|
||||
self.world.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 Consumed King Garden", self.player).connect(consumed_king_garden_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))
|
||||
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
|
||||
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:
|
||||
for name, address in location_table.items():
|
||||
location = DarkSouls3Location(self.player, name, self.location_name_to_id[name], new_region)
|
||||
new_region.locations.append(location)
|
||||
self.world.regions.append(new_region)
|
||||
self.multiworld.regions.append(new_region)
|
||||
return new_region
|
||||
|
||||
def create_items(self):
|
||||
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
|
||||
if name != "Basin of Vows":
|
||||
self.world.itempool += [self.create_item(name)]
|
||||
self.multiworld.itempool += [self.create_item(name)]
|
||||
|
||||
def generate_early(self):
|
||||
pass
|
||||
|
@ -178,46 +178,46 @@ class DarkSouls3World(World):
|
|||
def set_rules(self) -> None:
|
||||
|
||||
# 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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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
|
||||
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 - Lothric Prince", self.player))
|
||||
|
||||
# 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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
black_hand_gotthard_corpse_rule = lambda state: \
|
||||
(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))
|
||||
set_rule(self.world.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: Grand Archives Key", 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 - Yhorm the Giant", self.player) and \
|
||||
state.has("Cinders of a Lord - Aldrich", self.player) and \
|
||||
|
@ -226,30 +226,30 @@ class DarkSouls3World(World):
|
|||
def generate_basic(self):
|
||||
# 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")
|
||||
if self.world.late_basin_of_vows[self.player]:
|
||||
self.world.get_location("IBV: Soul of Pontiff Sulyvahn", self.player).place_locked_item(item)
|
||||
if self.multiworld.late_basin_of_vows[self.player]:
|
||||
self.multiworld.get_location("IBV: Soul of Pontiff Sulyvahn", self.player).place_locked_item(item)
|
||||
else:
|
||||
self.world.itempool += [item]
|
||||
self.multiworld.itempool += [item]
|
||||
|
||||
# Fill item pool with additional items
|
||||
item_pool_len = self.item_name_to_id.__len__()
|
||||
total_required_locations = self.location_name_to_id.__len__()
|
||||
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):
|
||||
# Depending on the specified option, modify items hexadecimal value to add an upgrade level
|
||||
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
|
||||
for name in weapons_upgrade_5_table.keys():
|
||||
if self.world.random.randint(0, 100) < 33:
|
||||
value = self.world.random.randint(1, 5)
|
||||
if self.multiworld.random.randint(0, 100) < 33:
|
||||
value = self.multiworld.random.randint(1, 5)
|
||||
item_dictionary_copy[name] += value
|
||||
|
||||
for name in weapons_upgrade_10_table.keys():
|
||||
if self.world.random.randint(0, 100) < 33:
|
||||
value = self.world.random.randint(1, 10)
|
||||
if self.multiworld.random.randint(0, 100) < 33:
|
||||
value = self.multiworld.random.randint(1, 10)
|
||||
item_dictionary_copy[name] += value
|
||||
|
||||
# Create the mandatory lists to generate the player's output file
|
||||
|
@ -258,7 +258,7 @@ class DarkSouls3World(World):
|
|||
locations_id = []
|
||||
locations_address = []
|
||||
locations_target = []
|
||||
for location in self.world.get_filled_locations():
|
||||
for location in self.multiworld.get_filled_locations():
|
||||
if location.item.player == self.player:
|
||||
items_id.append(location.item.code)
|
||||
items_address.append(item_dictionary[location.item.name])
|
||||
|
@ -273,12 +273,12 @@ class DarkSouls3World(World):
|
|||
|
||||
data = {
|
||||
"options": {
|
||||
"auto_equip": self.world.auto_equip[self.player].value,
|
||||
"lock_equip": self.world.lock_equip[self.player].value,
|
||||
"no_weapon_requirements": self.world.no_weapon_requirements[self.player].value,
|
||||
"auto_equip": self.multiworld.auto_equip[self.player].value,
|
||||
"lock_equip": self.multiworld.lock_equip[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
|
||||
"slot": self.world.player_name[self.player], # to connect to server
|
||||
"seed": self.multiworld.seed_name, # to verify the server's multiworld
|
||||
"slot": self.multiworld.player_name[self.player], # to connect to server
|
||||
"base_id": self.base_id, # to merge location and items lists
|
||||
"locationsId": locations_id,
|
||||
"locationsAddress": locations_address,
|
||||
|
@ -288,6 +288,6 @@ class DarkSouls3World(World):
|
|||
}
|
||||
|
||||
# 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:
|
||||
json.dump(data, outfile)
|
||||
|
|
|
@ -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):
|
||||
# Shamelessly stolen from the ROR2 definition
|
||||
ret = Region(name, None, name, player)
|
||||
ret.world = world
|
||||
ret.multiworld = world
|
||||
if locations:
|
||||
for locationName, locationData in locations.items():
|
||||
loc_id = active_locations.get(locationName, 0)
|
||||
|
|
|
@ -69,13 +69,13 @@ class DKC3World(World):
|
|||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_slot_data()
|
||||
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
|
||||
|
||||
return slot_data
|
||||
|
||||
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] = []
|
||||
|
||||
# Levels
|
||||
|
@ -85,14 +85,14 @@ class DKC3World(World):
|
|||
# Rocket Rush Cog
|
||||
total_required_locations -= 1
|
||||
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
|
||||
if self.world.goal[self.player] == "knautilus":
|
||||
self.world.get_location(LocationName.kastle_kaos, self.player).place_locked_item(self.create_item(ItemName.victory))
|
||||
if self.multiworld.goal[self.player] == "knautilus":
|
||||
self.multiworld.get_location(LocationName.kastle_kaos, self.player).place_locked_item(self.create_item(ItemName.victory))
|
||||
number_of_bosses = 7
|
||||
else:
|
||||
self.world.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]
|
||||
self.multiworld.get_location(LocationName.banana_bird_mother, self.player).place_locked_item(self.create_item(ItemName.victory))
|
||||
number_of_banana_birds = self.multiworld.number_of_banana_birds[self.player]
|
||||
|
||||
# Bosses
|
||||
total_required_locations += number_of_bosses
|
||||
|
@ -100,15 +100,15 @@ class DKC3World(World):
|
|||
# Secret Caves
|
||||
total_required_locations += 13
|
||||
|
||||
if self.world.kongsanity[self.player]:
|
||||
if self.multiworld.kongsanity[self.player]:
|
||||
total_required_locations += 39
|
||||
|
||||
## Brothers Bear
|
||||
if False:#self.world.include_trade_sequence[self.player]:
|
||||
total_required_locations += 10
|
||||
|
||||
number_of_bonus_coins = (self.world.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 = (self.multiworld.krematoa_bonus_coin_cost[self.player] * 5)
|
||||
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.dk_coin) for _ in range(41)]
|
||||
|
@ -119,27 +119,27 @@ class DKC3World(World):
|
|||
total_junk_count = total_required_locations - len(itempool)
|
||||
|
||||
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))
|
||||
|
||||
itempool += junk_pool
|
||||
|
||||
self.active_level_list = level_list.copy()
|
||||
|
||||
if self.world.level_shuffle[self.player]:
|
||||
self.world.random.shuffle(self.active_level_list)
|
||||
if self.multiworld.level_shuffle[self.player]:
|
||||
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):
|
||||
try:
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
player = self.player
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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
|
||||
if rom_name:
|
||||
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:
|
||||
world_names = [
|
||||
|
@ -181,14 +181,14 @@ class DKC3World(World):
|
|||
er_hint_data = {}
|
||||
for world_index in range(len(world_names)):
|
||||
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:
|
||||
er_hint_data[location.address] = world_names[world_index]
|
||||
multidata['er_hint_data'][self.player] = er_hint_data
|
||||
|
||||
def create_regions(self):
|
||||
location_table = setup_locations(self.world, self.player)
|
||||
create_regions(self.world, self.player, location_table)
|
||||
location_table = setup_locations(self.multiworld, self.player)
|
||||
create_regions(self.multiworld, self.player, location_table)
|
||||
|
||||
def create_item(self, name: str, force_non_progression=False) -> Item:
|
||||
data = item_table[name]
|
||||
|
@ -205,4 +205,4 @@ class DKC3World(World):
|
|||
return created_item
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
|
|
@ -77,7 +77,7 @@ class FactorioModFile(worlds.Files.APContainer):
|
|||
|
||||
def generate_mod(world: "Factorio", output_directory: str):
|
||||
player = world.player
|
||||
multiworld = world.world
|
||||
multiworld = world.multiworld
|
||||
global data_final_template, locale_template, control_template, data_template, settings_template
|
||||
with template_load_lock:
|
||||
if not data_final_template:
|
||||
|
|
|
@ -20,7 +20,7 @@ def _sorter(location: "FactorioScienceLocation"):
|
|||
|
||||
|
||||
def get_shapes(factorio_world: "Factorio") -> Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]]:
|
||||
world = factorio_world.world
|
||||
world = factorio_world.multiworld
|
||||
player = factorio_world.player
|
||||
prerequisites: Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]] = {}
|
||||
layout = world.tech_tree_layout[player].value
|
||||
|
|
|
@ -73,32 +73,32 @@ class Factorio(World):
|
|||
generate_output = generate_mod
|
||||
|
||||
def generate_early(self) -> None:
|
||||
self.world.max_tech_cost[self.player] = max(self.world.max_tech_cost[self.player],
|
||||
self.world.min_tech_cost[self.player])
|
||||
self.tech_mix = self.world.tech_cost_mix[self.player]
|
||||
self.skip_silo = self.world.silo[self.player].value == Silo.option_spawn
|
||||
self.multiworld.max_tech_cost[self.player] = max(self.multiworld.max_tech_cost[self.player],
|
||||
self.multiworld.min_tech_cost[self.player])
|
||||
self.tech_mix = self.multiworld.tech_cost_mix[self.player]
|
||||
self.skip_silo = self.multiworld.silo[self.player].value == Silo.option_spawn
|
||||
|
||||
def create_regions(self):
|
||||
player = self.player
|
||||
random = self.world.random
|
||||
menu = Region("Menu", RegionType.Generic, "Menu", player, self.world)
|
||||
random = self.multiworld.random
|
||||
menu = Region("Menu", RegionType.Generic, "Menu", player, self.multiworld)
|
||||
crash = Entrance(player, "Crash Land", menu)
|
||||
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 + \
|
||||
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 = []
|
||||
|
||||
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_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)
|
||||
for loc_name in location_names]
|
||||
rand_values = sorted(random.randint(self.world.min_tech_cost[self.player],
|
||||
self.world.max_tech_cost[self.player]) for _ in self.locations)
|
||||
rand_values = sorted(random.randint(self.multiworld.min_tech_cost[self.player],
|
||||
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)):
|
||||
location.count = rand_values[i]
|
||||
del rand_values
|
||||
|
@ -108,27 +108,27 @@ class Factorio(World):
|
|||
event = FactorioItem("Victory", ItemClassification.progression, None, player)
|
||||
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)
|
||||
nauvis.locations.append(location)
|
||||
event = FactorioItem(f"Automated {ingredient}", ItemClassification.progression, None, player)
|
||||
location.place_locked_item(event)
|
||||
|
||||
crash.connect(nauvis)
|
||||
self.world.regions += [menu, nauvis]
|
||||
self.multiworld.regions += [menu, nauvis]
|
||||
|
||||
def set_rules(self):
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
player = self.player
|
||||
self.custom_technologies = self.set_custom_technologies()
|
||||
self.set_custom_recipes()
|
||||
shapes = get_shapes(self)
|
||||
if world.logic[player] != 'nologic':
|
||||
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)
|
||||
|
||||
if self.world.recipe_ingredients[self.player]:
|
||||
if self.multiworld.recipe_ingredients[self.player]:
|
||||
custom_recipe = self.custom_recipes[ingredient]
|
||||
|
||||
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))
|
||||
|
||||
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 \
|
||||
else next(iter(all_product_sources.get("rocket-silo")))
|
||||
part_recipe = self.custom_recipes["rocket-part"]
|
||||
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 \
|
||||
else next(iter(all_product_sources.get("satellite")))
|
||||
victory_tech_names = get_rocket_requirements(silo_recipe, part_recipe, satellite_recipe)
|
||||
|
@ -165,12 +165,12 @@ class Factorio(World):
|
|||
|
||||
def generate_basic(self):
|
||||
player = self.player
|
||||
want_progressives = collections.defaultdict(lambda: self.world.progressive[player].
|
||||
want_progressives(self.world.random))
|
||||
self.world.itempool.extend(self.create_item("Evolution Trap") for _ in
|
||||
range(self.world.evolution_traps[player].value))
|
||||
self.world.itempool.extend(self.create_item("Attack Trap") for _ in
|
||||
range(self.world.attack_traps[player].value))
|
||||
want_progressives = collections.defaultdict(lambda: self.multiworld.progressive[player].
|
||||
want_progressives(self.multiworld.random))
|
||||
self.multiworld.itempool.extend(self.create_item("Evolution Trap") for _ in
|
||||
range(self.multiworld.evolution_traps[player].value))
|
||||
self.multiworld.itempool.extend(self.create_item("Attack Trap") for _ in
|
||||
range(self.multiworld.attack_traps[player].value))
|
||||
|
||||
cost_sorted_locations = sorted(self.locations, key=lambda location: location.name)
|
||||
special_index = {"automation": 0,
|
||||
|
@ -189,19 +189,19 @@ class Factorio(World):
|
|||
tech_item = self.create_item(item_name)
|
||||
index = special_index.get(tech_name, None)
|
||||
if index is None:
|
||||
self.world.itempool.append(tech_item)
|
||||
self.multiworld.itempool.append(tech_item)
|
||||
else:
|
||||
loc = cost_sorted_locations[index]
|
||||
loc.place_locked_item(tech_item)
|
||||
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
|
||||
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
|
||||
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:
|
||||
loc.revealed = True
|
||||
|
||||
|
@ -259,7 +259,7 @@ class Factorio(World):
|
|||
# have to first sort for determinism, while filtering out non-stacking items
|
||||
pool: typing.List[str] = sorted(pool & valid_ingredients)
|
||||
# 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_energy = original.total_energy * factor
|
||||
target_num_ingredients = len(original.ingredients)
|
||||
|
@ -303,7 +303,7 @@ class Factorio(World):
|
|||
if min_num > max_num:
|
||||
fallback_pool.append(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
|
||||
remaining_raw -= num * ingredient_raw
|
||||
remaining_energy -= num * ingredient_energy
|
||||
|
@ -347,58 +347,58 @@ class Factorio(World):
|
|||
|
||||
def set_custom_technologies(self):
|
||||
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():
|
||||
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
|
||||
|
||||
def set_custom_recipes(self):
|
||||
original_rocket_part = recipes["rocket-part"]
|
||||
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)
|
||||
self.world.random.shuffle(valid_pool)
|
||||
valid_pool = sorted(science_pack_pools[self.multiworld.max_science_pack[self.player].get_max_pack()] & valid_ingredients)
|
||||
self.multiworld.random.shuffle(valid_pool)
|
||||
self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category,
|
||||
{valid_pool[x]: 10 for x in range(3)},
|
||||
original_rocket_part.products,
|
||||
original_rocket_part.energy)}
|
||||
|
||||
if self.world.recipe_ingredients[self.player]:
|
||||
if self.multiworld.recipe_ingredients[self.player]:
|
||||
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])
|
||||
self.world.random.shuffle(valid_pool)
|
||||
self.multiworld.random.shuffle(valid_pool)
|
||||
if pack in recipes: # skips over space science pack
|
||||
new_recipe = self.make_quick_recipe(recipes[pack], valid_pool)
|
||||
self.custom_recipes[pack] = new_recipe
|
||||
|
||||
if self.world.silo[self.player].value == Silo.option_randomize_recipe \
|
||||
or self.world.satellite[self.player].value == Satellite.option_randomize_recipe:
|
||||
if self.multiworld.silo[self.player].value == Silo.option_randomize_recipe \
|
||||
or self.multiworld.satellite[self.player].value == Satellite.option_randomize_recipe:
|
||||
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]
|
||||
|
||||
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,
|
||||
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
|
||||
|
||||
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,
|
||||
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
|
||||
bridge = "ap-energy-bridge"
|
||||
new_recipe = self.make_quick_recipe(
|
||||
Recipe(bridge, "crafting", {"replace_1": 1, "replace_2": 1, "replace_3": 1},
|
||||
{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:
|
||||
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
|
||||
|
||||
needed_recipes = self.world.max_science_pack[self.player].get_allowed_packs() | {"rocket-part"}
|
||||
if self.world.silo[self.player] != Silo.option_spawn:
|
||||
needed_recipes = self.multiworld.max_science_pack[self.player].get_allowed_packs() | {"rocket-part"}
|
||||
if self.multiworld.silo[self.player] != Silo.option_spawn:
|
||||
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"}
|
||||
|
||||
for recipe in needed_recipes:
|
||||
|
@ -448,10 +448,10 @@ class FactorioScienceLocation(FactorioLocation):
|
|||
|
||||
self.ingredients = {Factorio.ordered_science_packs[self.complexity]: 1}
|
||||
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.count = parent.world.random.randint(parent.world.min_tech_cost[self.player],
|
||||
parent.world.max_tech_cost[self.player])
|
||||
self.count = parent.multiworld.random.randint(parent.multiworld.min_tech_cost[self.player],
|
||||
parent.multiworld.max_tech_cost[self.player])
|
||||
|
||||
@property
|
||||
def factorio_ingredients(self) -> typing.List[typing.Tuple[str, int]]:
|
||||
|
|
|
@ -51,15 +51,15 @@ class FF1World(World):
|
|||
return
|
||||
|
||||
def create_regions(self):
|
||||
locations = get_options(self.world, 'locations', self.player)
|
||||
rules = get_options(self.world, 'rules', self.player)
|
||||
locations = get_options(self.multiworld, 'locations', self.player)
|
||||
rules = get_options(self.multiworld, 'rules', self.player)
|
||||
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_item = Item(CHAOS_TERMINATED_EVENT, ItemClassification.progression, EventId, self.player)
|
||||
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"]],
|
||||
self.player)
|
||||
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 "
|
||||
"4-4-0.finalfantasyrandomizer.com")
|
||||
menu_region.locations.append(terminated_event)
|
||||
self.world.regions += [menu_region]
|
||||
self.multiworld.regions += [menu_region]
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return self.ff1_items.generate_item(name, self.player)
|
||||
|
||||
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):
|
||||
items = get_options(self.world, 'items', self.player)
|
||||
items = get_options(self.multiworld, 'items', self.player)
|
||||
if FF1_BRIDGE in items.keys():
|
||||
self._place_locked_item_in_sphere0(FF1_BRIDGE)
|
||||
if items:
|
||||
possible_early_items = [name for name in FF1_STARTER_ITEMS if name in items.keys()]
|
||||
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)
|
||||
else:
|
||||
# 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
|
||||
self.locked_items]
|
||||
|
||||
self.world.itempool += items
|
||||
self.multiworld.itempool += items
|
||||
|
||||
def _place_locked_item_in_sphere0(self, progression_item: str):
|
||||
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()
|
||||
if rules and len(rules[0]) == 0 and name not in self.locked_locations]
|
||||
if sphere_0_locations:
|
||||
initial_location = self.world.random.choice(sphere_0_locations)
|
||||
locked_location = self.world.get_location(initial_location, self.player)
|
||||
initial_location = self.multiworld.random.choice(sphere_0_locations)
|
||||
locked_location = self.multiworld.get_location(initial_location, self.player)
|
||||
locked_location.place_locked_item(self.create_item(progression_item))
|
||||
self.locked_items.append(progression_item)
|
||||
self.locked_locations.append(locked_location.name)
|
||||
|
@ -116,7 +116,7 @@ class FF1World(World):
|
|||
return slot_data
|
||||
|
||||
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):
|
||||
|
|
|
@ -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) -> \
|
||||
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:
|
||||
return None
|
||||
return location.item.name, location.item.player
|
||||
|
|
|
@ -44,7 +44,7 @@ class GenericWorld(World):
|
|||
web = GenericWeb()
|
||||
|
||||
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:
|
||||
if name == "Nothing":
|
||||
|
|
|
@ -29,7 +29,7 @@ def hk_set_rule(hk_world: World, location: str, rule):
|
|||
locations = hk_world.created_multi_locations.get(location)
|
||||
if locations is None:
|
||||
try:
|
||||
locations = [hk_world.world.get_location(location, player)]
|
||||
locations = [hk_world.multiworld.get_location(location, player)]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
|
@ -39,7 +39,7 @@ def hk_set_rule(hk_world: World, location: str, rule):
|
|||
|
||||
def set_rules(hk_world: World):
|
||||
player = hk_world.player
|
||||
world = hk_world.world
|
||||
world = hk_world.multiworld
|
||||
set_generated_rules(hk_world, hk_set_rule)
|
||||
|
||||
# Shop costs
|
||||
|
|
|
@ -166,7 +166,7 @@ class HKWorld(World):
|
|||
self.vanilla_shop_costs = deepcopy(vanilla_shop_costs)
|
||||
|
||||
def generate_early(self):
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
charm_costs = world.RandomCharmCosts[self.player].get_costs(world.random)
|
||||
self.charm_costs = world.PlandoCharmCosts[self.player].get_costs(charm_costs)
|
||||
# world.exclude_locations[self.player].value.update(white_palace_locations)
|
||||
|
@ -182,22 +182,22 @@ class HKWorld(World):
|
|||
|
||||
def white_palace_exclusions(self):
|
||||
exclusions = set()
|
||||
wp = self.world.WhitePalace[self.player]
|
||||
wp = self.multiworld.WhitePalace[self.player]
|
||||
if wp <= WhitePalace.option_nopathofpain:
|
||||
exclusions.update(path_of_pain_locations)
|
||||
if wp <= WhitePalace.option_kingfragment:
|
||||
exclusions.update(white_palace_checks)
|
||||
if wp == WhitePalace.option_exclude:
|
||||
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
|
||||
exclusions.update(white_palace_transitions)
|
||||
exclusions.update(white_palace_events)
|
||||
return exclusions
|
||||
|
||||
def create_regions(self):
|
||||
menu_region: Region = create_region(self.world, self.player, 'Menu')
|
||||
self.world.regions.append(menu_region)
|
||||
menu_region: Region = create_region(self.multiworld, self.player, 'Menu')
|
||||
self.multiworld.regions.append(menu_region)
|
||||
# wp_exclusions = self.white_palace_exclusions()
|
||||
|
||||
# Link regions
|
||||
|
@ -226,12 +226,12 @@ class HKWorld(World):
|
|||
pool: typing.List[HKItem] = []
|
||||
wp_exclusions = self.white_palace_exclusions()
|
||||
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"))
|
||||
|
||||
randomized_starting_items = set()
|
||||
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)
|
||||
|
||||
# noinspection PyShadowingNames
|
||||
|
@ -262,7 +262,7 @@ class HKWorld(World):
|
|||
unfilled_locations += 1
|
||||
pool.append(item)
|
||||
else:
|
||||
self.world.push_precollected(item)
|
||||
self.multiworld.push_precollected(item)
|
||||
return
|
||||
|
||||
if vanilla:
|
||||
|
@ -277,49 +277,49 @@ class HKWorld(World):
|
|||
location.progress_type = LocationProgressType.EXCLUDED
|
||||
|
||||
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):
|
||||
if item_name in junk_replace:
|
||||
item_name = self.get_filler_item_name()
|
||||
|
||||
if (item_name == "Crystal_Heart" and self.world.SplitCrystalHeart[self.player]) or \
|
||||
(item_name == "Mothwing_Cloak" and self.world.SplitMothwingCloak[self.player]):
|
||||
if (item_name == "Crystal_Heart" and self.multiworld.SplitCrystalHeart[self.player]) or \
|
||||
(item_name == "Mothwing_Cloak" and self.multiworld.SplitMothwingCloak[self.player]):
|
||||
_add("Left_" + item_name, location_name)
|
||||
_add("Right_" + item_name, "Split_" + location_name)
|
||||
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("Right_" + item_name, "Right_" + location_name)
|
||||
continue
|
||||
if item_name == "Shade_Cloak" and self.world.SplitMothwingCloak[self.player]:
|
||||
if self.world.random.randint(0, 1):
|
||||
if item_name == "Shade_Cloak" and self.multiworld.SplitMothwingCloak[self.player]:
|
||||
if self.multiworld.random.randint(0, 1):
|
||||
item_name = "Left_Mothwing_Cloak"
|
||||
else:
|
||||
item_name = "Right_Mothwing_Cloak"
|
||||
|
||||
_add(item_name, location_name)
|
||||
|
||||
if self.world.RandomizeElevatorPass[self.player]:
|
||||
if self.multiworld.RandomizeElevatorPass[self.player]:
|
||||
randomized = True
|
||||
_add("Elevator_Pass", "Elevator_Pass")
|
||||
|
||||
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)
|
||||
unfilled_locations += 1
|
||||
|
||||
# Balance the 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.
|
||||
if additional_shop_items > 0:
|
||||
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')
|
||||
|
||||
for _ in range(additional_shop_items):
|
||||
shop = self.world.random.choice(shops)
|
||||
shop = self.multiworld.random.choice(shops)
|
||||
loc = self.create_location(shop)
|
||||
unfilled_locations += 1
|
||||
if len(self.created_multi_locations[shop]) >= 16:
|
||||
|
@ -330,7 +330,7 @@ class HKWorld(World):
|
|||
# Create filler items, if needed
|
||||
if item_count < unfilled_locations:
|
||||
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.sort_shops_by_cost()
|
||||
|
||||
|
@ -345,24 +345,24 @@ class HKWorld(World):
|
|||
loc.costs = costs
|
||||
|
||||
def apply_costsanity(self):
|
||||
setting = self.world.CostSanity[self.player].value
|
||||
setting = self.multiworld.CostSanity[self.player].value
|
||||
if not setting:
|
||||
return # noop
|
||||
|
||||
def _compute_weights(weights: dict, desc: str) -> typing.Dict[str, int]:
|
||||
if all(x == 0 for x in weights.values()):
|
||||
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."
|
||||
)
|
||||
weights = {k: 1 for k in weights}
|
||||
|
||||
return {k: v for k, v in weights.items() if v}
|
||||
|
||||
random = self.world.random
|
||||
hybrid_chance = getattr(self.world, f"CostSanityHybridChance")[self.player].value
|
||||
random = self.multiworld.random
|
||||
hybrid_chance = getattr(self.multiworld, f"CostSanityHybridChance")[self.player].value
|
||||
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()
|
||||
}
|
||||
weights_geoless = dict(weights)
|
||||
|
@ -374,16 +374,16 @@ class HKWorld(World):
|
|||
if hybrid_chance > 0:
|
||||
if len(weights) == 1:
|
||||
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."
|
||||
)
|
||||
if len(weights_geoless) == 1:
|
||||
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."
|
||||
)
|
||||
|
||||
for region in self.world.get_regions(self.player):
|
||||
for region in self.multiworld.get_regions(self.player):
|
||||
for location in region.locations:
|
||||
if location.vanilla:
|
||||
continue
|
||||
|
@ -417,7 +417,7 @@ class HKWorld(World):
|
|||
location.sort_costs()
|
||||
|
||||
def set_rules(self):
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
player = self.player
|
||||
if world.logic[player] != 'nologic':
|
||||
goal = world.Goal[player]
|
||||
|
@ -436,7 +436,7 @@ class HKWorld(World):
|
|||
|
||||
options = slot_data["options"] = {}
|
||||
for option_name in self.option_definitions:
|
||||
option = getattr(self.world, option_name)[self.player]
|
||||
option = getattr(self.multiworld, option_name)[self.player]
|
||||
try:
|
||||
optionvalue = int(option.value)
|
||||
except TypeError:
|
||||
|
@ -445,10 +445,10 @@ class HKWorld(World):
|
|||
options[option_name] = optionvalue
|
||||
|
||||
# 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)
|
||||
if not self.world.CostSanity[self.player]:
|
||||
if not self.multiworld.CostSanity[self.player]:
|
||||
for shop, terms in shop_cost_types.items():
|
||||
unit = cost_terms[next(iter(terms))].option
|
||||
if unit == "Geo":
|
||||
|
@ -460,7 +460,7 @@ class HKWorld(World):
|
|||
|
||||
# HKAP 0.1.0 and later cost data.
|
||||
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:
|
||||
if location.costs:
|
||||
location_costs[location.name] = location.costs
|
||||
|
@ -479,7 +479,7 @@ class HKWorld(World):
|
|||
basename = name
|
||||
if name in shop_cost_types:
|
||||
costs = {
|
||||
term: self.world.random.randint(*self.ranges[term])
|
||||
term: self.multiworld.random.randint(*self.ranges[term])
|
||||
for term in shop_cost_types[name]
|
||||
}
|
||||
elif name in vanilla_location_costs:
|
||||
|
@ -491,7 +491,7 @@ class HKWorld(World):
|
|||
i = len(multi) + 1
|
||||
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,
|
||||
self.location_name_to_id[name], region, costs=costs, vanilla=vanilla,
|
||||
basename=basename)
|
||||
|
@ -577,16 +577,16 @@ class HKWorld(World):
|
|||
'RandomizeGeoRocks', 'RandomizeSoulTotems', 'RandomizeLoreTablets', 'RandomizeJunkPitChests',
|
||||
'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
|
||||
exclusions)
|
||||
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:
|
||||
ret = Region(name, RegionType.Generic, name, player)
|
||||
ret.world = world
|
||||
ret.multiworld = world
|
||||
if location_names:
|
||||
for location in location_names:
|
||||
loc_id = HKWorld.location_name_to_id.get(location, None)
|
||||
|
@ -654,16 +654,16 @@ class HKItem(Item):
|
|||
|
||||
|
||||
class HKLogicMixin(LogicMixin):
|
||||
world: MultiWorld
|
||||
multiworld: MultiWorld
|
||||
|
||||
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:
|
||||
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:
|
||||
return self.world.StartLocation[player] == start_location
|
||||
return self.multiworld.StartLocation[player] == start_location
|
||||
|
||||
def _hk_nail_combat(self, player: int) -> bool:
|
||||
return self.has_any({'LFFTSLASH', 'RIGHTSLASH', 'UPSLASH'}, player)
|
||||
|
|
|
@ -89,7 +89,7 @@ class Hylics2Logic(LogicMixin):
|
|||
|
||||
|
||||
def set_rules(hylics2world):
|
||||
world = hylics2world.world
|
||||
world = hylics2world.multiworld
|
||||
player = hylics2world.player
|
||||
|
||||
# Afterlife
|
||||
|
|
|
@ -64,8 +64,8 @@ class Hylics2World(World):
|
|||
|
||||
# set random starting location if option is enabled
|
||||
def generate_early(self):
|
||||
if self.world.random_start[self.player]:
|
||||
i = self.world.random.randint(0, 3)
|
||||
if self.multiworld.random_start[self.player]:
|
||||
i = self.multiworld.random.randint(0, 3)
|
||||
if i == 0:
|
||||
self.start_location = "Waynehouse"
|
||||
elif i == 1:
|
||||
|
@ -77,12 +77,12 @@ class Hylics2World(World):
|
|||
|
||||
def generate_basic(self):
|
||||
# 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"))
|
||||
set_rule(loc, lambda state: state._hylics2_has_upper_chamber_key(self.player)
|
||||
and state._hylics2_has_vessel_room_key(self.player))
|
||||
self.world.get_region("Hylemxylem", self.player).locations.append(loc)
|
||||
self.world.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
|
||||
self.multiworld.get_region("Hylemxylem", self.player).locations.append(loc)
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
|
||||
|
||||
# create item pool
|
||||
pool = []
|
||||
|
@ -94,53 +94,53 @@ class Hylics2World(World):
|
|||
pool.append(self.add_item(data["name"], data["classification"], i))
|
||||
|
||||
# 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():
|
||||
pool.append(self.add_item(data["name"], data["classification"], i))
|
||||
|
||||
# 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
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
|
||||
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())
|
||||
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
|
||||
# placed at Sage Airship: TV
|
||||
if self.world.extra_items_in_logic[self.player]:
|
||||
tv = self.world.random.choice(tvs)
|
||||
if self.multiworld.extra_items_in_logic[self.player]:
|
||||
tv = self.multiworld.random.choice(tvs)
|
||||
gest = gestures.index((200681, Items.gesture_item_table[200681]))
|
||||
while tv[1]["name"] == "Sage Airship: TV":
|
||||
tv = self.world.random.choice(tvs)
|
||||
self.world.get_location(tv[1]["name"], self.player)\
|
||||
tv = self.multiworld.random.choice(tvs)
|
||||
self.multiworld.get_location(tv[1]["name"], self.player)\
|
||||
.place_locked_item(self.add_item(gestures[gest][1]["name"], gestures[gest][1]["classification"],
|
||||
gestures[gest]))
|
||||
gestures.remove(gestures[gest])
|
||||
tvs.remove(tv)
|
||||
|
||||
for i in range(len(gestures)):
|
||||
gest = self.world.random.choice(gestures)
|
||||
tv = self.world.random.choice(tvs)
|
||||
self.world.get_location(tv[1]["name"], self.player)\
|
||||
gest = self.multiworld.random.choice(gestures)
|
||||
tv = self.multiworld.random.choice(tvs)
|
||||
self.multiworld.get_location(tv[1]["name"], self.player)\
|
||||
.place_locked_item(self.add_item(gest[1]["name"], gest[1]["classification"], gest[1]))
|
||||
gestures.remove(gest)
|
||||
tvs.remove(tv)
|
||||
|
@ -150,22 +150,22 @@ class Hylics2World(World):
|
|||
pool.append(self.add_item(data["name"], data["classification"], i))
|
||||
|
||||
# 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 j in range(data["count"]):
|
||||
pool.append(self.add_item(data["name"], data["classification"], i))
|
||||
|
||||
# add to world's pool
|
||||
self.world.itempool += pool
|
||||
self.multiworld.itempool += pool
|
||||
|
||||
|
||||
def fill_slot_data(self) -> Dict[str, Any]:
|
||||
slot_data: Dict[str, Any] = {
|
||||
"party_shuffle": self.world.party_shuffle[self.player].value,
|
||||
"medallion_shuffle": self.world.medallion_shuffle[self.player].value,
|
||||
"random_start" : self.world.random_start[self.player].value,
|
||||
"party_shuffle": self.multiworld.party_shuffle[self.player].value,
|
||||
"medallion_shuffle": self.multiworld.medallion_shuffle[self.player].value,
|
||||
"random_start" : self.multiworld.random_start[self.player].value,
|
||||
"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
|
||||
|
||||
|
@ -173,29 +173,29 @@ class Hylics2World(World):
|
|||
def create_regions(self) -> None:
|
||||
|
||||
region_table: Dict[int, Region] = {
|
||||
0: Region("Menu", RegionType.Generic, "Menu", self.player, self.world),
|
||||
1: Region("Afterlife", RegionType.Generic, "Afterlife", self.player, self.world),
|
||||
2: Region("Waynehouse", RegionType.Generic, "Waynehouse", self.player, self.world),
|
||||
3: Region("World", RegionType.Generic, "World", self.player, self.world),
|
||||
4: Region("New Muldul", RegionType.Generic, "New Muldul", self.player, self.world),
|
||||
5: Region("New Muldul Vault", RegionType.Generic, "New Muldul Vault", self.player, self.world),
|
||||
6: Region("Viewax", RegionType.Generic, "Viewax's Edifice", self.player, self.world),
|
||||
7: Region("Airship", RegionType.Generic, "Airship", self.player, self.world),
|
||||
8: Region("Arcade Island", RegionType.Generic, "Arcade Island", self.player, self.world),
|
||||
9: Region("TV Island", RegionType.Generic, "TV Island", self.player, self.world),
|
||||
10: Region("Juice Ranch", RegionType.Generic, "Juice Ranch", self.player, self.world),
|
||||
11: Region("Shield Facility", RegionType.Generic, "Shield Facility", self.player, self.world),
|
||||
12: Region("Worm Pod", RegionType.Generic, "Worm Pod", self.player, self.world),
|
||||
13: Region("Foglast", RegionType.Generic, "Foglast", self.player, self.world),
|
||||
14: Region("Drill Castle", RegionType.Generic, "Drill Castle", self.player, self.world),
|
||||
15: Region("Sage Labyrinth", RegionType.Generic, "Sage Labyrinth", self.player, self.world),
|
||||
16: Region("Sage Airship", RegionType.Generic, "Sage Airship", self.player, self.world),
|
||||
17: Region("Hylemxylem", RegionType.Generic, "Hylemxylem", self.player, self.world)
|
||||
0: Region("Menu", RegionType.Generic, "Menu", self.player, self.multiworld),
|
||||
1: Region("Afterlife", RegionType.Generic, "Afterlife", self.player, self.multiworld),
|
||||
2: Region("Waynehouse", RegionType.Generic, "Waynehouse", self.player, self.multiworld),
|
||||
3: Region("World", RegionType.Generic, "World", self.player, self.multiworld),
|
||||
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.multiworld),
|
||||
6: Region("Viewax", RegionType.Generic, "Viewax's Edifice", self.player, self.multiworld),
|
||||
7: Region("Airship", RegionType.Generic, "Airship", self.player, self.multiworld),
|
||||
8: Region("Arcade Island", RegionType.Generic, "Arcade Island", self.player, self.multiworld),
|
||||
9: Region("TV Island", RegionType.Generic, "TV Island", self.player, self.multiworld),
|
||||
10: Region("Juice Ranch", RegionType.Generic, "Juice Ranch", self.player, self.multiworld),
|
||||
11: Region("Shield Facility", RegionType.Generic, "Shield Facility", self.player, self.multiworld),
|
||||
12: Region("Worm Pod", RegionType.Generic, "Worm Pod", self.player, self.multiworld),
|
||||
13: Region("Foglast", RegionType.Generic, "Foglast", self.player, self.multiworld),
|
||||
14: Region("Drill Castle", RegionType.Generic, "Drill Castle", self.player, self.multiworld),
|
||||
15: Region("Sage Labyrinth", RegionType.Generic, "Sage Labyrinth", self.player, self.multiworld),
|
||||
16: Region("Sage Airship", RegionType.Generic, "Sage Airship", self.player, self.multiworld),
|
||||
17: Region("Hylemxylem", RegionType.Generic, "Hylemxylem", self.player, self.multiworld)
|
||||
}
|
||||
|
||||
# create regions from table
|
||||
for i, reg in region_table.items():
|
||||
self.world.regions.append(reg)
|
||||
self.multiworld.regions.append(reg)
|
||||
# get all exits per region
|
||||
for j, exits in Exits.region_exit_table.items():
|
||||
if j == i:
|
||||
|
@ -203,7 +203,7 @@ class Hylics2World(World):
|
|||
# create entrance and connect it to parent and destination regions
|
||||
ent = Entrance(self.player, k, reg)
|
||||
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":
|
||||
ent.connect(region_table[2])
|
||||
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"]]))
|
||||
|
||||
# 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():
|
||||
region_table[data["region"]].locations\
|
||||
.append(Hylics2Location(self.player, data["name"], i, region_table[data["region"]]))
|
||||
|
||||
# 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():
|
||||
region_table[data["region"]].locations\
|
||||
.append(Hylics2Location(self.player, data["name"], i, region_table[data["region"]]))
|
||||
|
|
|
@ -88,7 +88,7 @@ class MeritousWorld(World):
|
|||
return crystal_pool
|
||||
|
||||
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:
|
||||
return "Crystals x500"
|
||||
elif rand_crystals < 28:
|
||||
|
@ -97,14 +97,14 @@ class MeritousWorld(World):
|
|||
return "Crystals x2000"
|
||||
|
||||
def generate_early(self):
|
||||
self.goal = self.world.goal[self.player].value
|
||||
self.include_evolution_traps = self.world.include_evolution_traps[self.player].value
|
||||
self.include_psi_keys = self.world.include_psi_keys[self.player].value
|
||||
self.item_cache_cost = self.world.item_cache_cost[self.player].value
|
||||
self.death_link = self.world.death_link[self.player].value
|
||||
self.goal = self.multiworld.goal[self.player].value
|
||||
self.include_evolution_traps = self.multiworld.include_evolution_traps[self.player].value
|
||||
self.include_psi_keys = self.multiworld.include_psi_keys[self.player].value
|
||||
self.item_cache_cost = self.multiworld.item_cache_cost[self.player].value
|
||||
self.death_link = self.multiworld.death_link[self.player].value
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player)
|
||||
create_regions(self.multiworld, self.player)
|
||||
|
||||
def create_items(self):
|
||||
frequencies = [0, # Nothing [0]
|
||||
|
@ -133,22 +133,22 @@ class MeritousWorld(World):
|
|||
if len(item_pool) < location_count:
|
||||
item_pool += self._make_crystals(location_count - len(item_pool))
|
||||
|
||||
self.world.itempool += item_pool
|
||||
self.multiworld.itempool += item_pool
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
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.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.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.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"))
|
||||
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"))
|
||||
|
||||
if not self.include_psi_keys:
|
||||
|
@ -156,22 +156,22 @@ class MeritousWorld(World):
|
|||
psi_key_storage = []
|
||||
for i in range(0, 3):
|
||||
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)]
|
||||
|
||||
fill_restrictive(self.world, self.world.get_all_state(
|
||||
fill_restrictive(self.multiworld, self.multiworld.get_all_state(
|
||||
False), psi_key_storage, psi_keys)
|
||||
|
||||
if not self.include_evolution_traps:
|
||||
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"))
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
|
|
|
@ -45,7 +45,7 @@ class MinecraftLogic(LogicMixin):
|
|||
self.can_reach('Bastion Remnant', 'Region', player))
|
||||
|
||||
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
|
||||
return (self.can_reach('Zombie Doctor', 'Location', player) or
|
||||
(self._mc_has_diamond_pickaxe(player) and self.can_reach('Village', 'Region', player)))
|
||||
|
@ -58,10 +58,10 @@ class MinecraftLogic(LogicMixin):
|
|||
|
||||
# Difficulty-dependent functions
|
||||
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):
|
||||
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':
|
||||
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':
|
||||
|
@ -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)
|
||||
|
||||
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 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
|
||||
def set_advancement_rules(world: MultiWorld, player: int):
|
||||
|
|
|
@ -70,21 +70,21 @@ class MinecraftWorld(World):
|
|||
def _get_mc_data(self):
|
||||
exits = [connection[0] for connection in default_connections]
|
||||
return {
|
||||
'world_seed': self.world.slot_seeds[self.player].getrandbits(32),
|
||||
'seed_name': self.world.seed_name,
|
||||
'player_name': self.world.get_player_name(self.player),
|
||||
'world_seed': self.multiworld.slot_seeds[self.player].getrandbits(32),
|
||||
'seed_name': self.multiworld.seed_name,
|
||||
'player_name': self.multiworld.get_player_name(self.player),
|
||||
'player_id': self.player,
|
||||
'client_version': client_version,
|
||||
'structures': {exit: self.world.get_entrance(exit, self.player).connected_region.name for exit in exits},
|
||||
'advancement_goal': self.world.advancement_goal[self.player].value,
|
||||
'egg_shards_required': min(self.world.egg_shards_required[self.player].value,
|
||||
self.world.egg_shards_available[self.player].value),
|
||||
'egg_shards_available': self.world.egg_shards_available[self.player].value,
|
||||
'required_bosses': self.world.required_bosses[self.player].current_key,
|
||||
'MC35': bool(self.world.send_defeated_mobs[self.player].value),
|
||||
'death_link': bool(self.world.death_link[self.player].value),
|
||||
'starting_items': str(self.world.starting_items[self.player].value),
|
||||
'race': self.world.is_race,
|
||||
'structures': {exit: self.multiworld.get_entrance(exit, self.player).connected_region.name for exit in exits},
|
||||
'advancement_goal': self.multiworld.advancement_goal[self.player].value,
|
||||
'egg_shards_required': min(self.multiworld.egg_shards_required[self.player].value,
|
||||
self.multiworld.egg_shards_available[self.player].value),
|
||||
'egg_shards_available': self.multiworld.egg_shards_available[self.player].value,
|
||||
'required_bosses': self.multiworld.required_bosses[self.player].current_key,
|
||||
'MC35': bool(self.multiworld.send_defeated_mobs[self.player].value),
|
||||
'death_link': bool(self.multiworld.death_link[self.player].value),
|
||||
'starting_items': str(self.multiworld.starting_items[self.player].value),
|
||||
'race': self.multiworld.is_race,
|
||||
}
|
||||
|
||||
def generate_basic(self):
|
||||
|
@ -96,18 +96,18 @@ class MinecraftWorld(World):
|
|||
for (name, num) in required_items.items():
|
||||
itempool += [name] * num
|
||||
# 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]
|
||||
for struct_name in structures:
|
||||
itempool.append(f"Structure Compass ({struct_name})")
|
||||
# Add dragon egg shards
|
||||
if self.world.egg_shards_required[self.player] > 0:
|
||||
itempool += ["Dragon Egg Shard"] * self.world.egg_shards_available[self.player]
|
||||
if self.multiworld.egg_shards_required[self.player] > 0:
|
||||
itempool += ["Dragon Egg Shard"] * self.multiworld.egg_shards_available[self.player]
|
||||
# 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
|
||||
# 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
|
||||
itempool = [item for item in map(lambda name: self.create_item(name), itempool)]
|
||||
|
||||
|
@ -115,29 +115,29 @@ class MinecraftWorld(World):
|
|||
exclusion_pool = set()
|
||||
exclusion_types = ['hard', 'unreasonable']
|
||||
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])
|
||||
# For postgame advancements, check with the boss goal
|
||||
exclusion_pool.update(get_postgame_advancements(self.world.required_bosses[self.player].current_key))
|
||||
exclusion_rules(self.world, self.player, exclusion_pool)
|
||||
exclusion_pool.update(get_postgame_advancements(self.multiworld.required_bosses[self.player].current_key))
|
||||
exclusion_rules(self.multiworld, self.player, exclusion_pool)
|
||||
|
||||
# Prefill event locations with their events
|
||||
self.world.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.world.get_location("Wither", self.player).place_locked_item(self.create_item("Defeat Wither"))
|
||||
self.multiworld.get_location("Blaze Spawner", self.player).place_locked_item(self.create_item("Blaze Rods"))
|
||||
self.multiworld.get_location("Ender Dragon", self.player).place_locked_item(self.create_item("Defeat Ender Dragon"))
|
||||
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:
|
||||
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):
|
||||
set_advancement_rules(self.world, self.player)
|
||||
set_completion_rules(self.world, self.player)
|
||||
set_advancement_rules(self.multiworld, self.player)
|
||||
set_completion_rules(self.multiworld, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
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)
|
||||
for loc_name, loc_data in advancement_table.items()
|
||||
if loc_data.region == region_name]
|
||||
|
@ -145,19 +145,19 @@ class MinecraftWorld(World):
|
|||
ret.exits.append(Entrance(self.player, exit, ret))
|
||||
return ret
|
||||
|
||||
self.world.regions += [MCRegion(*r) for r in mc_regions]
|
||||
link_minecraft_structures(self.world, self.player)
|
||||
self.multiworld.regions += [MCRegion(*r) for r in mc_regions]
|
||||
link_minecraft_structures(self.multiworld, self.player)
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
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:
|
||||
f.write(b64encode(bytes(json.dumps(data), 'utf-8')))
|
||||
|
||||
def fill_slot_data(self):
|
||||
slot_data = self._get_mc_data()
|
||||
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}:
|
||||
slot_data[option_name] = int(option.value)
|
||||
return slot_data
|
||||
|
|
|
@ -768,7 +768,7 @@ patch_sets = {
|
|||
|
||||
def patch_cosmetics(ootworld, rom):
|
||||
# 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
|
||||
versioned_patch_set = None
|
||||
|
|
|
@ -9,7 +9,7 @@ class Dungeon(object):
|
|||
else:
|
||||
return [obj]
|
||||
|
||||
self.world = world
|
||||
self.multiworld = world
|
||||
self.name = name
|
||||
self.hint_text = hint
|
||||
self.font_color = font_color
|
||||
|
@ -18,7 +18,7 @@ class Dungeon(object):
|
|||
self.small_keys = to_array(small_keys)
|
||||
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:
|
||||
region.dungeon = self
|
||||
self.regions.append(region)
|
||||
|
|
|
@ -7,7 +7,7 @@ class OOTEntrance(Entrance):
|
|||
|
||||
def __init__(self, player, world, name='', parent=None):
|
||||
super(OOTEntrance, self).__init__(player, name, parent)
|
||||
self.world = world
|
||||
self.multiworld = world
|
||||
self.access_rules = []
|
||||
self.reverse = None
|
||||
self.replaces = None
|
||||
|
@ -30,8 +30,8 @@ class OOTEntrance(Entrance):
|
|||
return previously_connected
|
||||
|
||||
def get_new_target(self):
|
||||
root = self.world.get_region('Root Exits', self.player)
|
||||
target_entrance = OOTEntrance(self.player, self.world, 'Root -> ' + self.connected_region.name, root)
|
||||
root = self.multiworld.get_region('Root Exits', self.player)
|
||||
target_entrance = OOTEntrance(self.player, self.multiworld, 'Root -> ' + self.connected_region.name, root)
|
||||
target_entrance.connect(self.connected_region)
|
||||
target_entrance.replaces = self
|
||||
root.exits.append(target_entrance)
|
||||
|
|
|
@ -369,7 +369,7 @@ class EntranceShuffleError(Exception):
|
|||
|
||||
|
||||
def shuffle_random_entrances(ootworld):
|
||||
world = ootworld.world
|
||||
world = ootworld.multiworld
|
||||
player = ootworld.player
|
||||
|
||||
# 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):
|
||||
|
||||
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:
|
||||
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}')
|
||||
|
||||
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:
|
||||
if entrance.connected_region != None:
|
||||
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.
|
||||
# success rate over randomization
|
||||
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):
|
||||
world = ootworld.world
|
||||
world = ootworld.multiworld
|
||||
player = ootworld.player
|
||||
|
||||
# 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
|
||||
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
|
||||
|
||||
all_state = all_state_orig.copy()
|
||||
|
@ -827,7 +827,7 @@ def same_hint_area(first, second):
|
|||
return False
|
||||
|
||||
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:
|
||||
return original_entrance
|
||||
|
||||
|
@ -842,14 +842,14 @@ def get_entrance_replacing(region, entrance_name, player):
|
|||
def change_connections(entrance, target):
|
||||
entrance.connect(target.disconnect())
|
||||
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.replaces = entrance.reverse
|
||||
|
||||
def restore_connections(entrance, target):
|
||||
target.connect(entrance.disconnect())
|
||||
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())
|
||||
target.replaces.reverse.replaces = None
|
||||
|
||||
|
@ -866,7 +866,7 @@ def check_entrances_compatibility(entrance, target, rollbacks):
|
|||
def confirm_replacement(entrance, target):
|
||||
delete_target_entrance(target)
|
||||
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
|
||||
delete_target_entrance(entrance.reverse.assumed)
|
||||
logging.getLogger('').debug(f'Connected {replaced_reverse} to {replaced_reverse.connected_region}')
|
||||
|
|
|
@ -131,13 +131,13 @@ def getItemGenericName(item):
|
|||
def isRestrictedDungeonItem(dungeon, item):
|
||||
if not isinstance(item, OOTItem):
|
||||
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
|
||||
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
|
||||
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
|
||||
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 False
|
||||
|
||||
|
@ -148,7 +148,7 @@ def isRestrictedDungeonItem(dungeon, item):
|
|||
def attach_name(text, hinted_object, world):
|
||||
if hinted_object.player == world.player:
|
||||
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):
|
||||
|
@ -439,7 +439,7 @@ def get_specific_item_hint(world, checked):
|
|||
itemname = world.named_item_pool.pop(0)
|
||||
if itemname == "Bottle" and world.hint_dist == "bingo":
|
||||
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)
|
||||
and location.name not in world.hint_exclusions
|
||||
and location.item.name in bingoBottlesForHints
|
||||
|
@ -448,7 +448,7 @@ def get_specific_item_hint(world, checked):
|
|||
]
|
||||
else:
|
||||
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)
|
||||
and location.name not in world.hint_exclusions
|
||||
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_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:
|
||||
return None
|
||||
|
||||
|
@ -639,13 +639,13 @@ def buildWorldGossipHints(world, checkedLocations=None):
|
|||
world.woth_dungeon = 0
|
||||
|
||||
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
|
||||
# Can only be forced with vanilla bridge or trials
|
||||
if world.bridge != 'vanilla' and world.trials == 0 and world.misc_hints:
|
||||
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)
|
||||
except StopIteration: # start with them
|
||||
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.
|
||||
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:
|
||||
item_icon = chr(location.item.special['item_id'])
|
||||
location_text = getHint(location.name, world.clearer_hints).text
|
||||
|
@ -956,18 +956,18 @@ def buildGanonText(world, messages):
|
|||
text += "\x05\x42your pocket\x05\x40"
|
||||
else:
|
||||
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)
|
||||
location = find_light_arrows
|
||||
location_hint = get_hint_area(location)
|
||||
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:
|
||||
location_hint = location_hint.replace('Ganon\'s Castle', 'my castle')
|
||||
text += get_raw_text(location_hint)
|
||||
except StopIteration:
|
||||
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':
|
||||
text += get_raw_text(getHint(getItemGenericName(location.item), world.clearer_hints).text)
|
||||
break
|
||||
|
|
|
@ -748,7 +748,7 @@ def replace_max_item(items, item, max):
|
|||
|
||||
|
||||
def generate_itempool(ootworld):
|
||||
world = ootworld.world
|
||||
world = ootworld.multiworld
|
||||
player = ootworld.player
|
||||
global random
|
||||
random = world.random
|
||||
|
@ -1280,32 +1280,32 @@ def get_pool_core(world):
|
|||
|
||||
if world.free_scarecrow:
|
||||
item = world.create_item('Scarecrow Song')
|
||||
world.world.push_precollected(item)
|
||||
world.multiworld.push_precollected(item)
|
||||
world.remove_from_start_inventory.append(item.name)
|
||||
|
||||
if world.no_epona_race:
|
||||
item = world.create_item('Epona')
|
||||
world.world.push_precollected(item)
|
||||
world.multiworld.push_precollected(item)
|
||||
world.remove_from_start_inventory.append(item.name)
|
||||
|
||||
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]:
|
||||
world.world.push_precollected(item)
|
||||
world.multiworld.push_precollected(item)
|
||||
world.remove_from_start_inventory.append(item.name)
|
||||
pool.extend(get_junk_item())
|
||||
if world.shuffle_smallkeys == 'remove':
|
||||
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)
|
||||
pool.extend(get_junk_item())
|
||||
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]:
|
||||
world.world.push_precollected(item)
|
||||
world.multiworld.push_precollected(item)
|
||||
world.remove_from_start_inventory.append(item.name)
|
||||
pool.extend(get_junk_item())
|
||||
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]:
|
||||
world.world.push_precollected(item)
|
||||
world.multiworld.push_precollected(item)
|
||||
world.remove_from_start_inventory.append(item.name)
|
||||
pool.extend(get_junk_item())
|
||||
|
||||
|
@ -1331,7 +1331,7 @@ def get_pool_core(world):
|
|||
# Yes somehow you need 3 keys. This dungeon is bonkers
|
||||
items = [world.create_item('Small Key (Spirit Temple)') for i in range(3)]
|
||||
for item in items:
|
||||
world.world.push_precollected(item)
|
||||
world.multiworld.push_precollected(item)
|
||||
world.remove_from_start_inventory.append(item.name)
|
||||
#if not world.dungeon_mq['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']:
|
||||
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)
|
||||
|
||||
if world.triforce_hunt:
|
||||
|
|
|
@ -882,7 +882,7 @@ def make_player_message(text):
|
|||
def update_item_messages(messages, world):
|
||||
new_item_messages = {**ITEM_MESSAGES, **KEYSANITY_MESSAGES}
|
||||
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)
|
||||
else:
|
||||
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():
|
||||
destination = ootworld.world.get_entrance(entr, ootworld.player).connected_region
|
||||
destination = ootworld.multiworld.get_entrance(entr, ootworld.player).connected_region
|
||||
|
||||
if destination.pretty_name:
|
||||
destination_name = destination.pretty_name
|
||||
|
|
|
@ -1326,7 +1326,7 @@ def patch_rom(world, rom):
|
|||
override_table = get_override_table(world)
|
||||
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_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:
|
||||
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)
|
||||
|
||||
# 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
|
||||
special = item.special if item.game == 'Ocarina of Time' else {} # this shouldn't matter hopefully
|
||||
locationaddress = location.address1
|
||||
|
@ -1686,7 +1686,7 @@ def patch_rom(world, rom):
|
|||
pass
|
||||
elif dungeon in ['Bottom of the Well', 'Ice Cavern']:
|
||||
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)
|
||||
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")
|
||||
|
@ -1696,13 +1696,13 @@ def patch_rom(world, rom):
|
|||
else:
|
||||
dungeon_name, boss_name, compass_id, map_id = dungeon_list[dungeon]
|
||||
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)
|
||||
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)
|
||||
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 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)
|
||||
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")
|
||||
|
@ -1730,7 +1730,7 @@ def patch_rom(world, rom):
|
|||
rom.write_int16(0xB6D57E, 0x0003)
|
||||
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."
|
||||
if len(world.world.worlds) > 1:
|
||||
if len(world.multiworld.worlds) > 1:
|
||||
tycoon_message = make_player_message(tycoon_message)
|
||||
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):
|
||||
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
|
||||
|
@ -2154,8 +2154,8 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
|
|||
if location.item.name == 'Ice Trap':
|
||||
split_item_name[0] = create_fake_name(split_item_name[0])
|
||||
|
||||
if len(world.world.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))
|
||||
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.multiworld.get_player_name(location.item.player))
|
||||
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])
|
||||
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:
|
||||
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))
|
||||
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:
|
||||
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)
|
||||
|
|
|
@ -53,7 +53,7 @@ def isliteral(expr):
|
|||
class Rule_AST_Transformer(ast.NodeTransformer):
|
||||
|
||||
def __init__(self, world, player):
|
||||
self.world = world
|
||||
self.multiworld = world
|
||||
self.player = player
|
||||
self.events = set()
|
||||
# map Region -> rule ast string -> item name
|
||||
|
@ -86,9 +86,9 @@ class Rule_AST_Transformer(ast.NodeTransformer):
|
|||
ctx=ast.Load()),
|
||||
args=[ast.Str(escaped_items[node.id]), ast.Constant(self.player)],
|
||||
keywords=[])
|
||||
elif node.id in self.world.__dict__:
|
||||
elif node.id in self.multiworld.__dict__:
|
||||
# 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__:
|
||||
return self.make_call(node, node.id, [], [])
|
||||
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):
|
||||
# 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:
|
||||
iname = escaped_items[iname]
|
||||
|
@ -182,7 +182,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
|
|||
new_args = []
|
||||
for child in node.args:
|
||||
if isinstance(child, ast.Name):
|
||||
if child.id in self.world.__dict__:
|
||||
if child.id in self.multiworld.__dict__:
|
||||
# child = ast.Attribute(
|
||||
# value=ast.Attribute(
|
||||
# value=ast.Name(id='state', ctx=ast.Load()),
|
||||
|
@ -190,7 +190,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
|
|||
# ctx=ast.Load()),
|
||||
# attr=child.id,
|
||||
# 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:
|
||||
child = self.visit(child)
|
||||
elif child.id in escaped_items:
|
||||
|
@ -217,7 +217,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
|
|||
value=ast.Attribute(
|
||||
value=ast.Attribute(
|
||||
value=ast.Name(id='state', ctx=ast.Load()),
|
||||
attr='world',
|
||||
attr='multiworld',
|
||||
ctx=ast.Load()),
|
||||
attr='worlds',
|
||||
ctx=ast.Load()),
|
||||
|
@ -242,7 +242,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
|
|||
# Fast check for json can_use
|
||||
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 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)
|
||||
|
||||
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.
|
||||
def create_delayed_rules(self):
|
||||
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.show_in_spoiler = False
|
||||
|
||||
|
@ -395,7 +395,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
|
|||
set_rule(event, access_rule)
|
||||
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
|
||||
self.delayed_rules.clear()
|
||||
|
||||
|
@ -448,7 +448,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
|
|||
## Handlers for compile-time optimizations (former State functions)
|
||||
|
||||
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))
|
||||
# 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
|
||||
|
@ -456,7 +456,7 @@ class Rule_AST_Transformer(ast.NodeTransformer):
|
|||
return ast.NameConstant(True)
|
||||
|
||||
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))
|
||||
# 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
|
||||
|
@ -464,10 +464,10 @@ class Rule_AST_Transformer(ast.NodeTransformer):
|
|||
return ast.NameConstant(True)
|
||||
|
||||
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
|
||||
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))
|
||||
# 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
|
||||
|
@ -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
|
||||
|
||||
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):
|
||||
return ast.parse(f"state._oot_has_bottle({self.player})", mode='eval').body
|
||||
|
|
|
@ -26,7 +26,7 @@ class OOTLogic(LogicMixin):
|
|||
|
||||
# Used for fall damage and other situations where damage is unavoidable
|
||||
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:
|
||||
return mult != 'ohko' and mult != 'quadruple'
|
||||
else:
|
||||
|
@ -39,7 +39,7 @@ class OOTLogic(LogicMixin):
|
|||
def _oot_reach_as_age(self, regionname, age, player):
|
||||
if self.age[player] is None:
|
||||
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
|
||||
return can_reach
|
||||
return self.age[player] == age
|
||||
|
@ -52,7 +52,7 @@ class OOTLogic(LogicMixin):
|
|||
}
|
||||
if regionname in name_map[tod]:
|
||||
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':
|
||||
self.day_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]
|
||||
bc = 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
|
||||
if not start in rrp:
|
||||
|
@ -110,7 +110,7 @@ class OOTLogic(LogicMixin):
|
|||
def set_rules(ootworld):
|
||||
logger = logging.getLogger('')
|
||||
|
||||
world = ootworld.world
|
||||
world = ootworld.multiworld
|
||||
player = ootworld.player
|
||||
|
||||
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
|
||||
def set_entrances_based_rules(ootworld):
|
||||
|
||||
if ootworld.world.accessibility == 'beatable':
|
||||
if ootworld.multiworld.accessibility == 'beatable':
|
||||
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()):
|
||||
# If a shop is not reachable as adult, it can't have Goron Tunic or Zora Tunic as child can't buy these
|
||||
|
|
|
@ -118,14 +118,14 @@ class OOTWorld(World):
|
|||
|
||||
def generate_early(self):
|
||||
# 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(
|
||||
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)
|
||||
|
||||
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):
|
||||
option_value = int(result)
|
||||
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.starting_items = Counter()
|
||||
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 = {
|
||||
"medallions": {"Light Medallion", "Forest Medallion", "Fire Medallion", "Water Medallion",
|
||||
|
@ -185,13 +185,13 @@ class OOTWorld(World):
|
|||
# Determine skipped trials in GT
|
||||
# This needs to be done before the logic rules in GT are parsed
|
||||
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}
|
||||
|
||||
# Determine which dungeons are MQ
|
||||
# Possible future plan: allow user to pick which dungeons are MQ
|
||||
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:
|
||||
self.mq_dungeons = 0
|
||||
mq_dungeons = []
|
||||
|
@ -208,8 +208,8 @@ class OOTWorld(World):
|
|||
|
||||
# No Logic forces all tricks on, prog balancing off and beatable-only
|
||||
elif self.logic_rules == 'no_logic':
|
||||
self.world.progression_balancing[self.player].value = False
|
||||
self.world.accessibility[self.player] = self.world.accessibility[self.player].from_text("minimal")
|
||||
self.multiworld.progression_balancing[self.player].value = False
|
||||
self.multiworld.accessibility[self.player] = self.multiworld.accessibility[self.player].from_text("minimal")
|
||||
for trick in normalized_name_tricks.values():
|
||||
setattr(self, trick['name'], True)
|
||||
|
||||
|
@ -310,7 +310,7 @@ class OOTWorld(World):
|
|||
|
||||
for region in region_json:
|
||||
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:
|
||||
new_region.pretty_name = region['pretty_name']
|
||||
if 'font_color' in region:
|
||||
|
@ -355,19 +355,19 @@ class OOTWorld(World):
|
|||
new_location.show_in_spoiler = False
|
||||
if 'exits' in region:
|
||||
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.rule_string = rule
|
||||
if self.world.logic_rules != 'none':
|
||||
if self.multiworld.logic_rules != 'none':
|
||||
self.parser.parse_spot_rule(new_exit)
|
||||
if new_exit.never:
|
||||
logger.debug('Dropping unreachable exit: %s', new_exit.name)
|
||||
else:
|
||||
new_region.exits.append(new_exit)
|
||||
|
||||
self.world.regions.append(new_region)
|
||||
self.multiworld.regions.append(new_region)
|
||||
self.regions.append(new_region)
|
||||
self.world._recache()
|
||||
self.multiworld._recache()
|
||||
|
||||
def set_scrub_prices(self):
|
||||
# Get Deku Scrub Locations
|
||||
|
@ -387,7 +387,7 @@ class OOTWorld(World):
|
|||
elif self.shuffle_scrubs == 'random':
|
||||
# this is a random value between 0-99
|
||||
# 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.
|
||||
self.scrub_prices[scrub_item] = price
|
||||
|
@ -402,14 +402,14 @@ class OOTWorld(World):
|
|||
self.shop_prices = {}
|
||||
for region in self.regions:
|
||||
if self.shopsanity == 'random':
|
||||
shop_item_count = self.world.random.randint(0, 4)
|
||||
shop_item_count = self.multiworld.random.randint(0, 4)
|
||||
else:
|
||||
shop_item_count = int(self.shopsanity)
|
||||
|
||||
for location in region.locations:
|
||||
if location.type == 'Shop':
|
||||
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):
|
||||
boss_location_names = (
|
||||
|
@ -424,7 +424,7 @@ class OOTWorld(World):
|
|||
'Links Pocket'
|
||||
)
|
||||
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]
|
||||
prizepool = [item for item in boss_rewards if item.name not in placed_prizes]
|
||||
|
@ -432,12 +432,12 @@ class OOTWorld(World):
|
|||
|
||||
while bossCount:
|
||||
bossCount -= 1
|
||||
self.world.random.shuffle(prizepool)
|
||||
self.world.random.shuffle(prize_locs)
|
||||
self.multiworld.random.shuffle(prizepool)
|
||||
self.multiworld.random.shuffle(prize_locs)
|
||||
item = prizepool.pop()
|
||||
loc = prize_locs.pop()
|
||||
loc.place_locked_item(item)
|
||||
self.world.itempool.remove(item)
|
||||
self.multiworld.itempool.remove(item)
|
||||
|
||||
def create_item(self, name: str):
|
||||
if name in item_table:
|
||||
|
@ -449,7 +449,7 @@ class OOTWorld(World):
|
|||
def make_event_item(self, name, location, item=None):
|
||||
if item is None:
|
||||
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.event = True
|
||||
if name not in item_table:
|
||||
|
@ -463,11 +463,11 @@ class OOTWorld(World):
|
|||
world_type = 'Glitched World'
|
||||
overworld_data_path = data_path(world_type, 'Overworld.json')
|
||||
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)
|
||||
self.world.regions.append(menu)
|
||||
self.multiworld.regions.append(menu)
|
||||
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)
|
||||
self.parser.create_delayed_rules()
|
||||
|
||||
|
@ -478,7 +478,7 @@ class OOTWorld(World):
|
|||
# Bind entrances to vanilla
|
||||
for region in self.regions:
|
||||
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):
|
||||
# Generate itempool
|
||||
|
@ -491,7 +491,7 @@ class OOTWorld(World):
|
|||
junk_pool = get_junk_pool(self)
|
||||
removed_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:
|
||||
self.remove_from_start_inventory.remove(item.name)
|
||||
removed_items.append(item.name)
|
||||
|
@ -512,7 +512,7 @@ class OOTWorld(World):
|
|||
if self.start_with_rupees:
|
||||
self.starting_items['Rupees'] = 999
|
||||
|
||||
self.world.itempool += self.itempool
|
||||
self.multiworld.itempool += self.itempool
|
||||
self.remove_from_start_inventory.extend(removed_items)
|
||||
|
||||
def set_rules(self):
|
||||
|
@ -533,7 +533,7 @@ class OOTWorld(World):
|
|||
for entrance in self.get_shuffled_entrances():
|
||||
if entrance.connected_region is not None:
|
||||
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:
|
||||
assumed_entrance = entrance.assumed
|
||||
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
|
||||
# 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()
|
||||
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
|
||||
(loc.internal or loc.type == 'Drop') and loc.event and loc.locked and loc not in reachable]
|
||||
for loc in unreachable:
|
||||
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.
|
||||
# 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:
|
||||
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 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)
|
||||
# If open zora's domain then we need to kill Deliver Rutos Letter
|
||||
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)
|
||||
|
||||
def pre_fill(self):
|
||||
|
@ -641,11 +641,11 @@ class OOTWorld(World):
|
|||
if loc.item is None and (
|
||||
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
|
||||
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:
|
||||
self.world.itempool.remove(item)
|
||||
self.world.random.shuffle(dungeon_locations)
|
||||
fill_restrictive(self.world, self.world.get_all_state(False), dungeon_locations,
|
||||
self.multiworld.itempool.remove(item)
|
||||
self.multiworld.random.shuffle(dungeon_locations)
|
||||
fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), dungeon_locations,
|
||||
dungeon_itempool, True, True)
|
||||
any_dungeon_locations.extend(dungeon_locations) # adds only the unfilled locations
|
||||
|
||||
|
@ -653,22 +653,22 @@ class OOTWorld(World):
|
|||
if self.shuffle_fortresskeys == 'any_dungeon':
|
||||
itempools['any_dungeon'].add('Small Key (Thieves Hideout)')
|
||||
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:
|
||||
self.world.itempool.remove(item)
|
||||
self.multiworld.itempool.remove(item)
|
||||
any_dungeon_itempool.sort(key=lambda item:
|
||||
{'GanonBossKey': 4, 'BossKey': 3, 'SmallKey': 2, 'HideoutSmallKey': 1}.get(item.type, 0))
|
||||
self.world.random.shuffle(any_dungeon_locations)
|
||||
fill_restrictive(self.world, self.world.get_all_state(False), any_dungeon_locations,
|
||||
self.multiworld.random.shuffle(any_dungeon_locations)
|
||||
fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), any_dungeon_locations,
|
||||
any_dungeon_itempool, True, True)
|
||||
|
||||
# If anything is overworld-only, fill into local non-dungeon locations
|
||||
if self.shuffle_fortresskeys == 'overworld':
|
||||
itempools['overworld'].add('Small Key (Thieves Hideout)')
|
||||
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:
|
||||
self.world.itempool.remove(item)
|
||||
self.multiworld.itempool.remove(item)
|
||||
overworld_itempool.sort(key=lambda item:
|
||||
{'GanonBossKey': 4, 'BossKey': 3, 'SmallKey': 2, 'HideoutSmallKey': 1}.get(item.type, 0))
|
||||
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 != 'Song' or self.shuffle_song_items != 'song') and
|
||||
(loc.name not in dungeon_song_locations or self.shuffle_song_items != 'dungeon')]
|
||||
self.world.random.shuffle(non_dungeon_locations)
|
||||
fill_restrictive(self.world, self.world.get_all_state(False), non_dungeon_locations,
|
||||
self.multiworld.random.shuffle(non_dungeon_locations)
|
||||
fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), non_dungeon_locations,
|
||||
overworld_itempool, True, True)
|
||||
|
||||
# Place songs
|
||||
|
@ -686,16 +686,16 @@ class OOTWorld(World):
|
|||
tries = 5
|
||||
if self.shuffle_song_items == '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':
|
||||
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:
|
||||
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:
|
||||
self.world.itempool.remove(song)
|
||||
self.multiworld.itempool.remove(song)
|
||||
|
||||
important_warps = (self.shuffle_special_interior_entrances or self.shuffle_overworld_entrances or
|
||||
self.warp_songs or self.spawn_positions)
|
||||
|
@ -717,8 +717,8 @@ class OOTWorld(World):
|
|||
|
||||
while tries:
|
||||
try:
|
||||
self.world.random.shuffle(song_locations)
|
||||
fill_restrictive(self.world, self.world.get_all_state(False), song_locations[:], songs[:],
|
||||
self.multiworld.random.shuffle(song_locations)
|
||||
fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), song_locations[:], songs[:],
|
||||
True, True)
|
||||
logger.debug(f"Successfully placed songs for player {self.player} after {6 - tries} attempt(s)")
|
||||
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
|
||||
if self.shopsanity != 'off':
|
||||
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(
|
||||
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: {
|
||||
'Buy Deku Shield': 3 * int(self.open_forest == 'closed'),
|
||||
'Buy Goron Tunic': 2,
|
||||
'Buy Zora Tunic': 2
|
||||
}.get(item.name,
|
||||
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:
|
||||
self.world.itempool.remove(item)
|
||||
fill_restrictive(self.world, self.world.get_all_state(False), shop_locations, shop_items, True, True)
|
||||
self.multiworld.itempool.remove(item)
|
||||
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
|
||||
|
||||
# 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 impa.item is None:
|
||||
item_to_place = self.world.random.choice(list(item for item in self.world.itempool if
|
||||
item.player == self.player and item.name in SaveContext.giveable_items))
|
||||
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))
|
||||
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
|
||||
self.world.push_precollected(impa.item)
|
||||
self.world.push_precollected(self.create_item("Zeldas Letter"))
|
||||
self.multiworld.push_precollected(impa.item)
|
||||
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
|
||||
# Check for dungeon ER later
|
||||
|
@ -789,8 +789,8 @@ class OOTWorld(World):
|
|||
|
||||
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]
|
||||
junk_fill_locations = self.world.random.sample(locations, round(len(locations) * ganon_junk_fill))
|
||||
exclusion_rules(self.world, self.player, junk_fill_locations)
|
||||
junk_fill_locations = self.multiworld.random.sample(locations, round(len(locations) * ganon_junk_fill))
|
||||
exclusion_rules(self.multiworld, self.player, junk_fill_locations)
|
||||
|
||||
# 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.
|
||||
|
@ -813,12 +813,12 @@ class OOTWorld(World):
|
|||
trap_location_ids = [loc.address for loc in self.get_locations() if loc.item.trap]
|
||||
self.trap_appearances = {}
|
||||
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
|
||||
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'])
|
||||
if self.hints != 'none':
|
||||
buildWorldGossipHints(self)
|
||||
|
@ -841,17 +841,17 @@ class OOTWorld(World):
|
|||
else:
|
||||
entrance = loadzone.reverse
|
||||
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:
|
||||
self.world.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player)
|
||||
self.multiworld.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player)
|
||||
else:
|
||||
reverse = loadzone.replaces.reverse
|
||||
if reverse in all_entrances:
|
||||
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:
|
||||
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.
|
||||
@classmethod
|
||||
|
@ -979,9 +979,9 @@ class OOTWorld(World):
|
|||
|
||||
# Helper functions
|
||||
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
|
||||
(type == None or entrance.type == type) and
|
||||
(not only_primary or entrance.primary))]
|
||||
return [entrance for entrance in self.multiworld.get_entrances() if (entrance.player == self.player and
|
||||
(type == None or entrance.type == type) and
|
||||
(not only_primary or entrance.primary))]
|
||||
|
||||
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
|
||||
|
@ -993,13 +993,13 @@ class OOTWorld(World):
|
|||
yield loc
|
||||
|
||||
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):
|
||||
return self.world.get_region(region, self.player)
|
||||
return self.multiworld.get_region(region, self.player)
|
||||
|
||||
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):
|
||||
if item.type == 'Token':
|
||||
|
@ -1030,7 +1030,7 @@ class OOTWorld(World):
|
|||
# Specifically ensures that only real items are gotten, not any events.
|
||||
# In particular, ensures that Time Travel needs to be found.
|
||||
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
|
||||
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')):
|
||||
|
|
|
@ -6,9 +6,9 @@ from BaseClasses import Location
|
|||
# TODO: implement Mapstone counting, Open, OpenWorld, connection rules
|
||||
|
||||
def set_rules(world):
|
||||
temp_base_rule(world.world, world.player)
|
||||
temp_base_rule(world.multiworld, world.player)
|
||||
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):
|
||||
|
|
|
@ -24,17 +24,17 @@ class OriBlindForest(World):
|
|||
def generate_early(self):
|
||||
logic_sets = {"casual-core"}
|
||||
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)
|
||||
self.logic_sets = logic_sets
|
||||
|
||||
set_rules = set_rules
|
||||
|
||||
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):
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
menu = self.create_region("Menu")
|
||||
world.regions.append(menu)
|
||||
start = Entrance(self.player, "Start Game", menu)
|
||||
|
@ -62,7 +62,7 @@ class OriBlindForest(World):
|
|||
|
||||
def generate_basic(self):
|
||||
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:
|
||||
return Item(name,
|
||||
|
|
|
@ -159,7 +159,7 @@ class Overcooked2Level:
|
|||
sublevel: int
|
||||
|
||||
def __init__(self):
|
||||
self.world = Overcooked2GameWorld.ONE
|
||||
self.multiworld = Overcooked2GameWorld.ONE
|
||||
self.sublevel = 0
|
||||
|
||||
def __iter__(self):
|
||||
|
@ -167,21 +167,21 @@ class Overcooked2Level:
|
|||
|
||||
def __next__(self):
|
||||
self.sublevel += 1
|
||||
if self.sublevel > self.world.sublevel_count:
|
||||
if self.world == Overcooked2GameWorld.KEVIN:
|
||||
if self.sublevel > self.multiworld.sublevel_count:
|
||||
if self.multiworld == Overcooked2GameWorld.KEVIN:
|
||||
raise StopIteration
|
||||
self.world = Overcooked2GameWorld(self.world.value + 1)
|
||||
self.multiworld = Overcooked2GameWorld(self.multiworld.value + 1)
|
||||
self.sublevel = 1
|
||||
|
||||
return self
|
||||
|
||||
@property
|
||||
def level_id(self) -> int:
|
||||
return self.world.base_id + (self.sublevel - 1)
|
||||
return self.multiworld.base_id + (self.sublevel - 1)
|
||||
|
||||
@property
|
||||
def level_name(self) -> str:
|
||||
return self.world.as_str + "-" + str(self.sublevel)
|
||||
return self.multiworld.as_str + "-" + str(self.sublevel)
|
||||
|
||||
@property
|
||||
def location_name_item(self) -> str:
|
||||
|
|
|
@ -79,7 +79,7 @@ class Overcooked2World(World):
|
|||
|
||||
def place_event(self, location_name: str, item_name: str,
|
||||
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))
|
||||
|
||||
def add_region(self, region_name: str):
|
||||
|
@ -88,13 +88,13 @@ class Overcooked2World(World):
|
|||
RegionType.Generic,
|
||||
region_name,
|
||||
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):
|
||||
sourceRegion = self.world.get_region(source, self.player)
|
||||
targetRegion = self.world.get_region(target, self.player)
|
||||
sourceRegion = self.multiworld.get_region(source, self.player)
|
||||
targetRegion = self.multiworld.get_region(target, self.player)
|
||||
|
||||
connection = Entrance(self.player, '', sourceRegion)
|
||||
if rule:
|
||||
|
@ -117,7 +117,7 @@ class Overcooked2World(World):
|
|||
else:
|
||||
location_id = level_id
|
||||
|
||||
region = self.world.get_region(region_name, self.player)
|
||||
region = self.multiworld.get_region(region_name, self.player)
|
||||
location = Overcooked2Location(
|
||||
self.player,
|
||||
location_name,
|
||||
|
@ -145,8 +145,8 @@ class Overcooked2World(World):
|
|||
)
|
||||
|
||||
def get_options(self) -> Dict[str, Any]:
|
||||
return OC2Options({option.__name__: getattr(self.world, name)[self.player].result
|
||||
if issubclass(option, OC2OnToggle) else getattr(self.world, name)[self.player].value
|
||||
return OC2Options({option.__name__: getattr(self.multiworld, name)[self.player].result
|
||||
if issubclass(option, OC2OnToggle) else getattr(self.multiworld, name)[self.player].value
|
||||
for name, option in overcooked_options.items()})
|
||||
|
||||
# Helper Data
|
||||
|
@ -170,7 +170,7 @@ class Overcooked2World(World):
|
|||
if self.options["ShuffleLevelOrder"]:
|
||||
self.level_mapping = \
|
||||
level_shuffle_factory(
|
||||
self.world.random,
|
||||
self.multiworld.random,
|
||||
self.options["PrepLevels"] != PrepLevelMode.excluded.value,
|
||||
self.options["IncludeHordeLevels"],
|
||||
)
|
||||
|
@ -246,7 +246,7 @@ class Overcooked2World(World):
|
|||
|
||||
completion_condition: Callable[[CollectionState], bool] = lambda state: \
|
||||
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):
|
||||
self.itempool = []
|
||||
|
@ -298,7 +298,7 @@ class Overcooked2World(World):
|
|||
while len(self.itempool) < pool_count:
|
||||
self.itempool.append(self.create_item("Bonus Star", ItemClassification.useful))
|
||||
|
||||
self.world.itempool += self.itempool
|
||||
self.multiworld.itempool += self.itempool
|
||||
|
||||
def set_rules(self):
|
||||
pass
|
||||
|
@ -324,7 +324,7 @@ class Overcooked2World(World):
|
|||
# Items get distributed to locations
|
||||
|
||||
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
|
||||
story_level_order = dict()
|
||||
|
@ -363,7 +363,7 @@ class Overcooked2World(World):
|
|||
# Set Kevin Unlock Requirements
|
||||
if self.options["KevinLevels"]:
|
||||
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:
|
||||
return None # This kevin level will be unlocked by the server at runtime
|
||||
level_id = oc2_location_name_to_id[location.name]
|
||||
|
@ -376,7 +376,7 @@ class Overcooked2World(World):
|
|||
|
||||
# Place Items at Level Completion Screens (local only)
|
||||
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 location in region.locations:
|
||||
if location.item is None:
|
||||
|
|
|
@ -73,22 +73,22 @@ class PokemonRedBlueWorld(World):
|
|||
def encode_name(name, t):
|
||||
try:
|
||||
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)
|
||||
except KeyError as e:
|
||||
raise KeyError(f"Invalid character(s) in {t} name for player {self.world.player_name[self.player]}") from e
|
||||
self.trainer_name = encode_name(self.world.trainer_name[self.player].value, "Player")
|
||||
self.rival_name = encode_name(self.world.rival_name[self.player].value, "Rival")
|
||||
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.multiworld.trainer_name[self.player].value, "Player")
|
||||
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"]
|
||||
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",
|
||||
"Soul Badge", "Volcano Badge", "Earth Badge"]
|
||||
self.world.random.shuffle(badges)
|
||||
self.multiworld.random.shuffle(badges)
|
||||
badges_to_add += [badges.pop(), badges.pop()]
|
||||
hm_moves = ["Cut", "Fly", "Surf", "Strength", "Flash"]
|
||||
self.world.random.shuffle(hm_moves)
|
||||
self.multiworld.random.shuffle(hm_moves)
|
||||
self.extra_badges = {}
|
||||
for badge in badges_to_add:
|
||||
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"]
|
||||
item_pool = []
|
||||
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
|
||||
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
|
||||
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
|
||||
item = self.create_item(location.original_item)
|
||||
if location.event:
|
||||
self.world.get_location(location.name, self.player).place_locked_item(item)
|
||||
elif ("Badge" not in item.name or self.world.badgesanity[self.player].value) and \
|
||||
(item.name != "Oak's Parcel" or self.world.old_man[self.player].value != 1):
|
||||
self.multiworld.get_location(location.name, self.player).place_locked_item(item)
|
||||
elif ("Badge" not in item.name or self.multiworld.badgesanity[self.player].value) and \
|
||||
(item.name != "Oak's Parcel" or self.multiworld.old_man[self.player].value != 1):
|
||||
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):
|
||||
|
@ -121,17 +121,17 @@ class PokemonRedBlueWorld(World):
|
|||
process_wild_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")
|
||||
locations = []
|
||||
for location in self.world.get_locations():
|
||||
if location.player == self.player and location.item is None and location.can_reach(self.world.state) \
|
||||
for location in self.multiworld.get_locations():
|
||||
if location.player == self.player and location.item is None and location.can_reach(self.multiworld.state) \
|
||||
and location.item_rule(item):
|
||||
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:
|
||||
self.world.non_local_items[self.player].value -= self.item_name_groups["Badges"]
|
||||
if not self.multiworld.badgesanity[self.player].value:
|
||||
self.multiworld.non_local_items[self.player].value -= self.item_name_groups["Badges"]
|
||||
for i in range(5):
|
||||
try:
|
||||
badges = []
|
||||
|
@ -142,11 +142,11 @@ class PokemonRedBlueWorld(World):
|
|||
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",
|
||||
"Cinnabar Gym - Blaine 1", "Viridian Gym - Giovanni 1"]:
|
||||
badgelocs.append(self.world.get_location(loc, self.player))
|
||||
state = self.world.get_all_state(False)
|
||||
self.world.random.shuffle(badges)
|
||||
self.world.random.shuffle(badgelocs)
|
||||
fill_restrictive(self.world, state, badgelocs.copy(), badges, True, True)
|
||||
badgelocs.append(self.multiworld.get_location(loc, self.player))
|
||||
state = self.multiworld.get_all_state(False)
|
||||
self.multiworld.random.shuffle(badges)
|
||||
self.multiworld.random.shuffle(badgelocs)
|
||||
fill_restrictive(self.multiworld, state, badgelocs.copy(), badges, True, True)
|
||||
except FillError:
|
||||
for location in badgelocs:
|
||||
location.item = None
|
||||
|
@ -155,36 +155,36 @@ class PokemonRedBlueWorld(World):
|
|||
else:
|
||||
raise FillError(f"Failed to place badges for player {self.player}")
|
||||
|
||||
locs = [self.world.get_location("Fossil - Choice A", self.player),
|
||||
self.world.get_location("Fossil - Choice B", self.player)]
|
||||
locs = [self.multiworld.get_location("Fossil - Choice A", self.player),
|
||||
self.multiworld.get_location("Fossil - Choice B", self.player)]
|
||||
for loc in locs:
|
||||
add_item_rule(loc, lambda i: i.advancement or i.name in self.item_name_groups["Unique"]
|
||||
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:
|
||||
locs.append(loc)
|
||||
|
||||
for loc in locs:
|
||||
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)
|
||||
for item in self.world.itempool:
|
||||
for item in self.multiworld.itempool:
|
||||
if item.player == self.player and loc.item_rule(item):
|
||||
self.world.itempool.remove(item)
|
||||
state = sweep_from_pool(self.world.state, self.world.itempool + unplaced_items)
|
||||
self.multiworld.itempool.remove(item)
|
||||
state = sweep_from_pool(self.multiworld.state, self.multiworld.itempool + unplaced_items)
|
||||
if state.can_reach(loc, "Location", self.player):
|
||||
loc.place_locked_item(item)
|
||||
break
|
||||
else:
|
||||
unplaced_items.append(item)
|
||||
self.world.itempool += unplaced_items
|
||||
self.multiworld.itempool += unplaced_items
|
||||
|
||||
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):
|
||||
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):
|
||||
intervene = True
|
||||
if intervene:
|
||||
|
@ -192,12 +192,12 @@ class PokemonRedBlueWorld(World):
|
|||
# let you choose the exact weights for HM compatibility
|
||||
logging.warning(
|
||||
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")
|
||||
|
||||
def create_regions(self):
|
||||
if self.world.free_fly_location[self.player].value:
|
||||
fly_map_code = self.world.random.randint(5, 9)
|
||||
if self.multiworld.free_fly_location[self.player].value:
|
||||
fly_map_code = self.multiworld.random.randint(5, 9)
|
||||
if fly_map_code == 9:
|
||||
fly_map_code = 10
|
||||
if fly_map_code == 5:
|
||||
|
@ -208,11 +208,11 @@ class PokemonRedBlueWorld(World):
|
|||
"Vermilion City", "Celadon City", "Fuchsia City", "Cinnabar Island", "Indigo Plateau",
|
||||
"Saffron City"][fly_map_code]
|
||||
self.fly_map_code = fly_map_code
|
||||
create_regions(self.world, self.player)
|
||||
self.world.completion_condition[self.player] = lambda state, player=self.player: state.has("Become Champion", player=player)
|
||||
create_regions(self.multiworld, self.player)
|
||||
self.multiworld.completion_condition[self.player] = lambda state, player=self.player: state.has("Become Champion", player=player)
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return PokemonRBItem(name, self.player)
|
||||
|
@ -221,21 +221,21 @@ class PokemonRedBlueWorld(World):
|
|||
generate_output(self, output_directory)
|
||||
|
||||
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)
|
||||
if self.extra_badges:
|
||||
for hm_move, badge in self.extra_badges.items():
|
||||
spoiler_handle.write(hm_move + " enabled by: " + (" " * 20)[:20 - len(hm_move)] + badge + "\n")
|
||||
|
||||
def write_spoiler(self, spoiler_handle):
|
||||
if self.world.randomize_type_matchup_types[self.player].value or \
|
||||
self.world.randomize_type_matchup_type_effectiveness[self.player].value:
|
||||
spoiler_handle.write(f"\n\nType matchups ({self.world.player_name[self.player]}):\n\n")
|
||||
if self.multiworld.randomize_type_matchup_types[self.player].value or \
|
||||
self.multiworld.randomize_type_matchup_type_effectiveness[self.player].value:
|
||||
spoiler_handle.write(f"\n\nType matchups ({self.multiworld.player_name[self.player]}):\n\n")
|
||||
for matchup in self.type_chart:
|
||||
spoiler_handle.write(f"{matchup[0]} deals {matchup[2] * 10}% damage to {matchup[1]}\n")
|
||||
|
||||
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]])
|
||||
|
||||
|
||||
|
|
|
@ -6,39 +6,39 @@ class PokemonLogic(LogicMixin):
|
|||
def pokemon_rb_can_surf(self, 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
|
||||
self.has(self.world.worlds[player].extra_badges.get("Surf"), player)
|
||||
or self.world.badges_needed_for_hm_moves[player].value == 0))
|
||||
self.has(self.multiworld.worlds[player].extra_badges.get("Surf"), player)
|
||||
or self.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
|
||||
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))
|
||||
and (self.has("Cascade Badge", player) or
|
||||
self.has(self.world.worlds[player].extra_badges.get("Cut"), player) or
|
||||
self.world.badges_needed_for_hm_moves[player].value == 0))
|
||||
self.has(self.multiworld.worlds[player].extra_badges.get("Cut"), player) or
|
||||
self.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
|
||||
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
|
||||
(self.has("Thunder Badge", player) or self.has(self.world.worlds[player].extra_badges.get("Fly"), player)
|
||||
or self.world.badges_needed_for_hm_moves[player].value == 0))
|
||||
(self.has("Thunder Badge", player) or self.has(self.multiworld.worlds[player].extra_badges.get("Fly"), player)
|
||||
or self.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
|
||||
def pokemon_rb_can_strength(self, player):
|
||||
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(self.world.worlds[player].extra_badges.get("Strength"), player)
|
||||
or self.world.badges_needed_for_hm_moves[player].value == 0)
|
||||
self.has(self.multiworld.worlds[player].extra_badges.get("Strength"), player)
|
||||
or self.multiworld.badges_needed_for_hm_moves[player].value == 0)
|
||||
|
||||
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))
|
||||
and (self.has("Boulder Badge", player) or self.has(self.world.worlds[player].extra_badges.get("Flash"),
|
||||
player) or self.world.badges_needed_for_hm_moves[player].value == 0))
|
||||
and (self.has("Boulder Badge", player) or self.has(self.multiworld.worlds[player].extra_badges.get("Flash"),
|
||||
player) or self.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
|
||||
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):
|
||||
return True
|
||||
return False
|
||||
|
||||
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):
|
||||
return len([item for item in
|
||||
|
@ -49,7 +49,7 @@ class PokemonLogic(LogicMixin):
|
|||
"HM04 Strength", "HM05 Flash"] if self.has(item, player)]) >= count
|
||||
|
||||
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)
|
||||
else:
|
||||
# this could just be "True", but you never know what weird options I might introduce later ;)
|
||||
|
|
|
@ -150,15 +150,15 @@ def create_regions(world: MultiWorld, player: int):
|
|||
connect(world, player, "Menu", "Anywhere", one_way=True)
|
||||
connect(world, player, "Menu", "Pallet Town", one_way=True)
|
||||
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, "Route 1", "Viridian City")
|
||||
connect(world, player, "Viridian City", "Route 22")
|
||||
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, "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 East", "Diglett's Cave", lambda state: state.pokemon_rb_can_cut(player))
|
||||
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 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
|
||||
(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", "Rock Tunnel B1F")
|
||||
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, "Vermilion City", "Route 11")
|
||||
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 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))
|
||||
|
@ -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", "Route 18")
|
||||
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 West", 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, "Route 15", "Route 14")
|
||||
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, "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", "Seafoam Islands 1F")
|
||||
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, "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 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", "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 B1F", "Seafoam Islands B2F", 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 9F", "Silph Co 10F", 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 B2F", "Rocket Hideout B3F", 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, "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", "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:
|
||||
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)
|
||||
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)
|
||||
|
|
|
@ -43,7 +43,7 @@ def get_encounter_slots(self):
|
|||
|
||||
for location in encounter_slots:
|
||||
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
|
||||
|
||||
|
||||
|
@ -64,19 +64,19 @@ def randomize_pokemon(self, mon, mons_list, randomize_type):
|
|||
if randomize_type == 3:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
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:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
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:
|
||||
mon = self.world.random.choice(mons_list)
|
||||
mon = self.multiworld.random.choice(mons_list)
|
||||
return mon
|
||||
|
||||
|
||||
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
|
||||
or self.world.trainer_legendaries[self.player].value]
|
||||
or self.multiworld.trainer_legendaries[self.player].value]
|
||||
address = rom_addresses["Trainer_Data"]
|
||||
while address < rom_addresses["Trainer_Data_End"]:
|
||||
if data[address] == 255:
|
||||
|
@ -93,14 +93,14 @@ def process_trainer_data(self, data):
|
|||
for i in range(1, 4):
|
||||
for l in ["A", "B", "C", "D", "E", "F", "G", "H"]:
|
||||
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:
|
||||
mon = poke_data.evolves_to[mon]
|
||||
if l in ["F", "G", "H"] and mon in poke_data.evolves_to:
|
||||
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 = 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:
|
||||
data[address] = poke_data.pokemon_data[mon]["id"]
|
||||
|
||||
|
@ -114,22 +114,22 @@ def process_static_pokemon(self):
|
|||
|
||||
tower_6F_mons = set()
|
||||
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
|
||||
or self.world.randomize_legendary_pokemon[self.player].value == 3]
|
||||
if self.world.randomize_legendary_pokemon[self.player].value == 0:
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
|
||||
if self.multiworld.randomize_legendary_pokemon[self.player].value == 0:
|
||||
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))
|
||||
elif self.world.randomize_legendary_pokemon[self.player].value == 1:
|
||||
self.world.random.shuffle(legendary_mons)
|
||||
elif self.multiworld.randomize_legendary_pokemon[self.player].value == 1:
|
||||
self.multiworld.random.shuffle(legendary_mons)
|
||||
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()))
|
||||
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
|
||||
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)
|
||||
while legendary_slots:
|
||||
swap_slot = legendary_slots.pop()
|
||||
|
@ -137,15 +137,15 @@ def process_static_pokemon(self):
|
|||
slot_type = slot.type.split()[0]
|
||||
if slot_type == "Legendary":
|
||||
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))
|
||||
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
|
||||
|
||||
for slot in static_slots:
|
||||
location = self.world.get_location(slot.name, self.player)
|
||||
randomize_type = self.world.randomize_static_pokemon[self.player].value
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
randomize_type = self.multiworld.randomize_static_pokemon[self.player].value
|
||||
slot_type = slot.type.split()[0]
|
||||
if slot_type == "Legendary":
|
||||
slot_type = "Missable"
|
||||
|
@ -160,8 +160,8 @@ def process_static_pokemon(self):
|
|||
location.place_locked_item(mon)
|
||||
|
||||
for slot in starter_slots:
|
||||
location = self.world.get_location(slot.name, self.player)
|
||||
randomize_type = self.world.randomize_starter_pokemon[self.player].value
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
randomize_type = self.multiworld.randomize_starter_pokemon[self.player].value
|
||||
slot_type = "Missable"
|
||||
if not randomize_type:
|
||||
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)
|
||||
|
||||
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
|
||||
or self.world.randomize_legendary_pokemon[self.player].value == 3]
|
||||
self.world.random.shuffle(encounter_slots)
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
|
||||
self.multiworld.random.shuffle(encounter_slots)
|
||||
locations = []
|
||||
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 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):
|
||||
# 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)
|
||||
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.event = True
|
||||
location.locked = True
|
||||
|
@ -198,33 +198,33 @@ def process_wild_pokemon(self):
|
|||
|
||||
mons_to_add = []
|
||||
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)]
|
||||
if self.world.catch_em_all[self.player].value == 1:
|
||||
(pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
|
||||
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
|
||||
(pokemon not in poke_data.legendary_pokemon or self.world.randomize_legendary_pokemon[self.player].value == 3)]
|
||||
elif self.world.catch_em_all[self.player].value == 2:
|
||||
(pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
|
||||
elif self.multiworld.catch_em_all[self.player].value == 2:
|
||||
mons_to_add = remaining_pokemon.copy()
|
||||
logic_needed_mons = max(self.world.oaks_aide_rt_2[self.player].value,
|
||||
self.world.oaks_aide_rt_11[self.player].value,
|
||||
self.world.oaks_aide_rt_15[self.player].value)
|
||||
if self.world.accessibility[self.player] == "minimal":
|
||||
logic_needed_mons = max(self.multiworld.oaks_aide_rt_2[self.player].value,
|
||||
self.multiworld.oaks_aide_rt_11[self.player].value,
|
||||
self.multiworld.oaks_aide_rt_15[self.player].value)
|
||||
if self.multiworld.accessibility[self.player] == "minimal":
|
||||
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])
|
||||
+ len(mons_to_add) < logic_needed_mons):
|
||||
mons_to_add.append(remaining_pokemon.pop())
|
||||
for mon in mons_to_add:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
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][
|
||||
"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"],
|
||||
self.local_poke_data[mon]["type2"]]])]
|
||||
if not candidate_locations:
|
||||
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))
|
||||
for location in candidate_locations:
|
||||
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:
|
||||
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.event = True
|
||||
location.locked = True
|
||||
|
@ -250,19 +250,19 @@ def process_pokemon_data(self):
|
|||
learnsets = deepcopy(poke_data.learnsets)
|
||||
|
||||
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"]]
|
||||
self.world.random.shuffle(stats)
|
||||
self.multiworld.random.shuffle(stats)
|
||||
mon_data["hp"] = stats[0]
|
||||
mon_data["atk"] = stats[1]
|
||||
mon_data["def"] = stats[2]
|
||||
mon_data["spd"] = stats[3]
|
||||
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
|
||||
stats = [1, 1, 1, 1, 1]
|
||||
while old_stats > 0:
|
||||
stat = self.world.random.randint(0, 4)
|
||||
stat = self.multiworld.random.randint(0, 4)
|
||||
if stats[stat] < 255:
|
||||
old_stats -= 1
|
||||
stats[stat] += 1
|
||||
|
@ -271,30 +271,30 @@ def process_pokemon_data(self):
|
|||
mon_data["def"] = stats[2]
|
||||
mon_data["spd"] = stats[3]
|
||||
mon_data["spc"] = stats[4]
|
||||
if self.world.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:
|
||||
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"]
|
||||
type2 = local_poke_data[poke_data.evolves_from[mon]]["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"]:
|
||||
while type2 == type1:
|
||||
type2 = self.world.random.choice(list(poke_data.type_names.values()))
|
||||
elif self.world.random.randint(1, 100) <= self.world.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()))
|
||||
elif self.multiworld.random.randint(1, 100) <= self.multiworld.secondary_type_chance[self.player].value:
|
||||
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
|
||||
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
|
||||
if ((self.world.secondary_type_chance[self.player].value == -1 and mon_data["type1"]
|
||||
!= mon_data["type2"]) or self.world.random.randint(1, 100)
|
||||
<= self.world.secondary_type_chance[self.player].value):
|
||||
if ((self.multiworld.secondary_type_chance[self.player].value == -1 and mon_data["type1"]
|
||||
!= mon_data["type2"]) or self.multiworld.random.randint(1, 100)
|
||||
<= self.multiworld.secondary_type_chance[self.player].value):
|
||||
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["type2"] = type2
|
||||
if self.world.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:
|
||||
if self.multiworld.randomize_pokemon_movesets[self.player].value == 1:
|
||||
if mon_data["type1"] == "Normal" and mon_data["type2"] == "Normal":
|
||||
chances = [[75, "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())
|
||||
for move in ["No Move"] + poke_data.hm_moves:
|
||||
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):
|
||||
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:
|
||||
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:
|
||||
for move_num in range(0, len(learnsets[mon])):
|
||||
learnsets[mon][move_num] = get_move(moves, chances, self.world.random)
|
||||
if self.world.randomize_pokemon_catch_rates[self.player].value:
|
||||
mon_data["catch rate"] = self.world.random.randint(self.world.minimum_catch_rate[self.player], 255)
|
||||
learnsets[mon][move_num] = get_move(moves, chances, self.multiworld.random)
|
||||
if self.multiworld.randomize_pokemon_catch_rates[self.player].value:
|
||||
mon_data["catch rate"] = self.multiworld.random.randint(self.multiworld.minimum_catch_rate[self.player], 255)
|
||||
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"]:
|
||||
mon_data["tms"] = local_poke_data[poke_data.evolves_from[mon]]["tms"]
|
||||
elif mon != "Mew":
|
||||
tms_hms = poke_data.tm_moves + poke_data.hm_moves
|
||||
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"]]
|
||||
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])
|
||||
elif (flag < 50 and self.world.tm_compatibility[self.player].value == 2) or (flag >= 50 and self.world.hm_compatibility[self.player].value == 2):
|
||||
bit = [0, 1][self.world.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):
|
||||
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.multiworld.tm_compatibility[self.player].value == 2) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 2):
|
||||
bit = [0, 1][self.multiworld.random.randint(0, 1)]
|
||||
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
|
||||
else:
|
||||
continue
|
||||
|
@ -350,14 +350,14 @@ def process_pokemon_data(self):
|
|||
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
random = self.world.slot_seeds[self.player]
|
||||
game_version = self.world.game_version[self.player].current_key
|
||||
random = self.multiworld.slot_seeds[self.player]
|
||||
game_version = self.multiworld.game_version[self.player].current_key
|
||||
data = bytearray(get_base_rom_bytes(game_version))
|
||||
|
||||
basemd5 = hashlib.md5()
|
||||
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:
|
||||
continue
|
||||
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[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["Guard_Drink_List"]] = 0x54
|
||||
data[rom_addresses["Guard_Drink_List"] + 1] = 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["Option_Blind_Trainers"]] = round(self.world.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_Encounter_Minimum_Steps']] = self.world.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_Pokemon_League_Badges']] = self.world.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_EXP_Modifier']] = self.world.exp_modifier[self.player].value
|
||||
if not self.world.require_item_finder[self.player].value:
|
||||
data[rom_addresses["Option_Blind_Trainers"]] = round(self.multiworld.blind_trainers[self.player].value * 2.55)
|
||||
data[rom_addresses['Option_Cerulean_Cave_Condition']] = self.multiworld.cerulean_cave_condition[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.multiworld.victory_road_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.multiworld.viridian_gym_condition[self.player].value
|
||||
data[rom_addresses['Option_EXP_Modifier']] = self.multiworld.exp_modifier[self.player].value
|
||||
if not self.multiworld.require_item_finder[self.player].value:
|
||||
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):
|
||||
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):
|
||||
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_Lying']] = 0x15
|
||||
money = str(self.world.starting_money[self.player].value)
|
||||
money = str(self.multiworld.starting_money[self.player].value)
|
||||
while len(money) < 6:
|
||||
money = "0" + money
|
||||
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_Low"]] = int(money[4:], 16)
|
||||
data[rom_addresses["Text_Badges_Needed"]] = encode_text(
|
||||
str(max(self.world.victory_road_condition[self.player].value,
|
||||
self.world.elite_four_condition[self.player].value)))[0]
|
||||
if self.world.badges_needed_for_hm_moves[self.player].value == 0:
|
||||
str(max(self.multiworld.victory_road_condition[self.player].value,
|
||||
self.multiworld.elite_four_condition[self.player].value)))[0]
|
||||
if self.multiworld.badges_needed_for_hm_moves[self.player].value == 0:
|
||||
for hm_move in poke_data.hm_moves:
|
||||
write_bytes(data, bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
|
||||
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(" ", "_")])
|
||||
|
||||
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 = []
|
||||
defending_types = []
|
||||
for matchup in chart:
|
||||
|
@ -458,7 +458,7 @@ def generate_output(self, output_directory: str):
|
|||
for matchup, chart_row in zip(matchups, chart):
|
||||
chart_row[0] = matchup[0]
|
||||
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 = []
|
||||
for matchup in chart:
|
||||
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[1] = random.choice(list(poke_data.type_names.values()))
|
||||
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 = []
|
||||
for matchup in chart:
|
||||
effectiveness_list.append(matchup[2])
|
||||
random.shuffle(effectiveness_list)
|
||||
for (matchup, effectiveness) in zip(chart, effectiveness_list):
|
||||
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:
|
||||
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:
|
||||
matchup[2] = random.choice([i for i in range(0, 21) if i != 10])
|
||||
type_loc = rom_addresses["Type_Chart"]
|
||||
|
@ -492,7 +492,7 @@ def generate_output(self, output_directory: str):
|
|||
# to the way effectiveness messages are generated.
|
||||
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]
|
||||
for i, chance in enumerate(chances):
|
||||
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]):
|
||||
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_Rt11"]] = self.world.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_Rt2"]] = self.multiworld.oaks_aide_rt_2[self.player]
|
||||
data[rom_addresses["Option_Aide_Rt11"]] = self.multiworld.oaks_aide_rt_11[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
|
||||
|
||||
if self.world.reusable_tms[self.player].value:
|
||||
if self.multiworld.reusable_tms[self.player].value:
|
||||
data[rom_addresses["Option_Reusable_TMs"]] = 0xC9
|
||||
|
||||
process_trainer_data(self, data)
|
||||
|
@ -537,17 +537,17 @@ def generate_output(self, output_directory: str):
|
|||
data[rom_addresses['Title_Mon_First']] = mons.pop()
|
||||
for mon in range(0, 16):
|
||||
data[rom_addresses['Title_Mons'] + mon] = mons.pop()
|
||||
if self.world.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
|
||||
else 1 if mon == self.world.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)
|
||||
if self.multiworld.game_version[self.player].value:
|
||||
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.multiworld.get_location("Pallet Town - Starter 2", self.player).item.name else
|
||||
2 if mon == self.multiworld.get_location("Pallet Town - Starter 3", self.player).item.name else 3)
|
||||
else:
|
||||
mons.sort(key=lambda mon: 0 if mon == self.world.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
|
||||
2 if mon == self.world.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'])
|
||||
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.multiworld.get_location("Pallet Town - Starter 1", self.player).item.name else
|
||||
2 if mon == self.multiworld.get_location("Pallet Town - Starter 3", self.player).item.name else 3)
|
||||
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(">", " ")
|
||||
|
@ -557,23 +557,23 @@ def generate_output(self, output_directory: str):
|
|||
write_bytes(data, self.rival_name, rom_addresses['Rival_Name'])
|
||||
|
||||
write_bytes(data, basemd5.digest(), 0xFFCC)
|
||||
write_bytes(data, self.world.seed_name.encode(), 0xFFDC)
|
||||
write_bytes(data, self.world.player_name[self.player].encode(), 0xFFF0)
|
||||
write_bytes(data, self.multiworld.seed_name.encode(), 0xFFDC)
|
||||
write_bytes(data, self.multiworld.player_name[self.player].encode(), 0xFFF0)
|
||||
|
||||
|
||||
|
||||
outfilepname = f'_P{self.player}'
|
||||
outfilepname += f"_{self.world.get_file_safe_player_name(self.player).replace(' ', '_')}" \
|
||||
if self.world.player_name[self.player] != 'Player%d' % self.player else ''
|
||||
rompath = os.path.join(output_directory, f'AP_{self.world.seed_name}{outfilepname}.gb')
|
||||
outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}" \
|
||||
if self.multiworld.player_name[self.player] != 'Player%d' % self.player else ''
|
||||
rompath = os.path.join(output_directory, f'AP_{self.multiworld.seed_name}{outfilepname}.gb')
|
||||
with open(rompath, 'wb') as outfile:
|
||||
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,
|
||||
player_name=self.world.player_name[self.player], patched_path=rompath)
|
||||
player_name=self.multiworld.player_name[self.player], patched_path=rompath)
|
||||
else:
|
||||
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()
|
||||
os.unlink(rompath)
|
||||
|
|
|
@ -10,13 +10,13 @@ def set_rules(world, 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),
|
||||
"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),
|
||||
"Cerulean City - Bicycle Shop": lambda state: state.has("Bike Voucher", 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 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),
|
||||
"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),
|
||||
|
|
|
@ -5,10 +5,10 @@ from ..AutoWorld import LogicMixin
|
|||
|
||||
class RaftLogic(LogicMixin):
|
||||
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):
|
||||
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):
|
||||
return self.has("Smelter", player)
|
||||
|
|
|
@ -43,8 +43,8 @@ class RaftWorld(World):
|
|||
required_client_version = (0, 3, 4)
|
||||
|
||||
def generate_basic(self):
|
||||
minRPSpecified = self.world.minimum_resource_pack_amount[self.player].value
|
||||
maxRPSpecified = self.world.maximum_resource_pack_amount[self.player].value
|
||||
minRPSpecified = self.multiworld.minimum_resource_pack_amount[self.player].value
|
||||
maxRPSpecified = self.multiworld.maximum_resource_pack_amount[self.player].value
|
||||
minimumResourcePackAmount = min(minRPSpecified, maxRPSpecified)
|
||||
maximumResourcePackAmount = max(minRPSpecified, maxRPSpecified)
|
||||
# Generate item pool
|
||||
|
@ -56,21 +56,21 @@ class RaftWorld(World):
|
|||
extraItemNamePool = []
|
||||
extras = len(location_table) - len(item_table) - 1 # Victory takes up 1 unaccounted-for slot
|
||||
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 i in range(minimumResourcePackAmount, maximumResourcePackAmount + 1):
|
||||
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()
|
||||
# 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"])
|
||||
|
||||
# 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)
|
||||
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 = list(dupeItemPool)
|
||||
|
@ -84,23 +84,23 @@ class RaftWorld(World):
|
|||
raft_item = self.create_item_replaceAsNecessary(randomItem)
|
||||
pool.append(raft_item)
|
||||
|
||||
self.world.itempool += pool
|
||||
self.multiworld.itempool += pool
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player)
|
||||
create_regions(self.multiworld, self.player)
|
||||
|
||||
def get_pre_fill_items(self):
|
||||
if self.world.island_frequency_locations[self.player] in [0, 1, 2, 3]:
|
||||
return [loc.item for loc in self.world.get_filled_locations()]
|
||||
if self.multiworld.island_frequency_locations[self.player] in [0, 1, 2, 3]:
|
||||
return [loc.item for loc in self.multiworld.get_filled_locations()]
|
||||
return []
|
||||
|
||||
def create_item_replaceAsNecessary(self, name: str) -> Item:
|
||||
isFrequency = "Frequency" in name
|
||||
shouldUseProgressive = ((isFrequency and self.world.island_frequency_locations[self.player].value == 4)
|
||||
or (not isFrequency and self.world.progressive_items[self.player].value))
|
||||
shouldUseProgressive = ((isFrequency and self.multiworld.island_frequency_locations[self.player].value == 4)
|
||||
or (not isFrequency and self.multiworld.progressive_items[self.player].value))
|
||||
if shouldUseProgressive and name in progressive_table:
|
||||
name = progressive_table[name]
|
||||
return self.create_item(name)
|
||||
|
@ -128,7 +128,7 @@ class RaftWorld(World):
|
|||
return super(RaftWorld, self).collect_item(state, item, remove)
|
||||
|
||||
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("Vasagatan Frequency to Balboa", "Balboa 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("Varuna Point Frequency to Temperance", "Temperance 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("Vasagatan", "Balboa Island Frequency")
|
||||
self.setLocationItemFromRegion("BalboaIsland", "Caravan Island Frequency")
|
||||
|
@ -144,7 +144,7 @@ class RaftWorld(World):
|
|||
self.setLocationItemFromRegion("Tangaroa", "Varuna Point Frequency")
|
||||
self.setLocationItemFromRegion("Varuna Point", "Temperance 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 = {
|
||||
"Vasagatan": "Vasagatan Frequency",
|
||||
"BalboaIsland": "Balboa Island Frequency",
|
||||
|
@ -172,37 +172,37 @@ class RaftWorld(World):
|
|||
else:
|
||||
currentLocation = availableLocationList[0] # Utopia (only one left in list)
|
||||
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])
|
||||
elif self.world.island_frequency_locations[self.player] == 3:
|
||||
elif self.multiworld.island_frequency_locations[self.player] == 3:
|
||||
self.setLocationItemFromRegion(previousLocation, locationToFrequencyItemMap[currentLocation])
|
||||
previousLocation = currentLocation
|
||||
|
||||
# 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))
|
||||
|
||||
def setLocationItem(self, location: str, itemName: str):
|
||||
itemToUse = next(filter(lambda itm: itm.name == itemName, self.world.itempool))
|
||||
self.world.itempool.remove(itemToUse)
|
||||
self.world.get_location(location, self.player).place_locked_item(itemToUse)
|
||||
itemToUse = next(filter(lambda itm: itm.name == itemName, self.multiworld.itempool))
|
||||
self.multiworld.itempool.remove(itemToUse)
|
||||
self.multiworld.get_location(location, self.player).place_locked_item(itemToUse)
|
||||
|
||||
def setLocationItemFromRegion(self, region: str, itemName: str):
|
||||
itemToUse = next(filter(lambda itm: itm.name == itemName, self.world.itempool))
|
||||
self.world.itempool.remove(itemToUse)
|
||||
itemToUse = next(filter(lambda itm: itm.name == itemName, self.multiworld.itempool))
|
||||
self.multiworld.itempool.remove(itemToUse)
|
||||
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):
|
||||
return {
|
||||
"IslandGenerationDistance": self.world.island_generation_distance[self.player].value,
|
||||
"ExpensiveResearch": bool(self.world.expensive_research[self.player].value),
|
||||
"DeathLink": bool(self.world.death_link[self.player].value)
|
||||
"IslandGenerationDistance": self.multiworld.island_generation_distance[self.player].value,
|
||||
"ExpensiveResearch": bool(self.multiworld.expensive_research[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):
|
||||
ret = Region(name, RegionType.Generic, name, player)
|
||||
ret.world = world
|
||||
ret.multiworld = world
|
||||
if locations:
|
||||
for location in locations:
|
||||
loc_id = locations_lookup_name_to_id.get(location, 0)
|
||||
|
|
|
@ -102,7 +102,7 @@ def create_regions(world: MultiWorld, player: int):
|
|||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
ret = Region(name, RegionType.Generic, name, player)
|
||||
ret.world = world
|
||||
ret.multiworld = world
|
||||
if locations:
|
||||
for loc_name in locations:
|
||||
loc_data = location_table.get(loc_name)
|
||||
|
|
|
@ -15,10 +15,10 @@ class LegacyLogic(LogicMixin):
|
|||
return self.stat_upgrade_count(player) >= amount
|
||||
|
||||
def total_stat_upgrades_count(self, player: int) -> int:
|
||||
return int(self.world.health_pool[player]) + \
|
||||
int(self.world.mana_pool[player]) + \
|
||||
int(self.world.attack_pool[player]) + \
|
||||
int(self.world.magic_damage_pool[player])
|
||||
return int(self.multiworld.health_pool[player]) + \
|
||||
int(self.multiworld.mana_pool[player]) + \
|
||||
int(self.multiworld.attack_pool[player]) + \
|
||||
int(self.multiworld.magic_damage_pool[player])
|
||||
|
||||
def stat_upgrade_count(self: CollectionState, player: int) -> int:
|
||||
return self.item_count("Health Up", player) + self.item_count("Mana Up", player) + \
|
||||
|
|
|
@ -44,7 +44,7 @@ class RLWorld(World):
|
|||
prefill_items: List[RLItem] = []
|
||||
|
||||
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:
|
||||
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":
|
||||
continue
|
||||
if self.setting("architect") == "start_unlocked":
|
||||
self.world.push_precollected(self.create_item(name))
|
||||
self.multiworld.push_precollected(self.create_item(name))
|
||||
continue
|
||||
|
||||
# Blacksmith and Enchantress
|
||||
if name == "Blacksmith" or name == "Enchantress":
|
||||
if self.setting("vendors") == "start_unlocked":
|
||||
self.world.push_precollected(self.create_item(name))
|
||||
self.multiworld.push_precollected(self.create_item(name))
|
||||
continue
|
||||
if self.setting("vendors") == "early":
|
||||
continue
|
||||
|
@ -195,11 +195,11 @@ class RLWorld(World):
|
|||
while len(self.item_pool) + len(self.prefill_items) < total_locations:
|
||||
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:
|
||||
reachable = [loc for loc in self.world.get_reachable_locations(player=self.player) if not loc.item]
|
||||
self.world.random.shuffle(reachable)
|
||||
reachable = [loc for loc in self.multiworld.get_reachable_locations(player=self.player) if not loc.item]
|
||||
self.multiworld.random.shuffle(reachable)
|
||||
items = self.prefill_items.copy()
|
||||
for item in items:
|
||||
reachable.pop().place_locked_item(item)
|
||||
|
@ -210,7 +210,7 @@ class RLWorld(World):
|
|||
def get_filler_item_name(self) -> str:
|
||||
fillers = get_items_by_category("Filler")
|
||||
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:
|
||||
data = item_table[name]
|
||||
|
@ -221,45 +221,45 @@ class RLWorld(World):
|
|||
return RLItem(name, data.classification, data.code, self.player)
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player)
|
||||
create_regions(self.multiworld, self.player)
|
||||
self._place_events()
|
||||
|
||||
def _place_events(self):
|
||||
# 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"))
|
||||
|
||||
# Khidr / Neo Khidr
|
||||
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"))
|
||||
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"))
|
||||
|
||||
# Alexander / Alexander IV
|
||||
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"))
|
||||
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"))
|
||||
|
||||
# Ponce de Leon / Ponce de Freon
|
||||
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"))
|
||||
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"))
|
||||
|
||||
# Herodotus / Astrodotus
|
||||
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"))
|
||||
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"))
|
||||
|
|
|
@ -42,40 +42,40 @@ class RiskOfRainWorld(World):
|
|||
|
||||
def generate_early(self) -> None:
|
||||
# figure out how many revivals should exist in the pool
|
||||
self.total_revivals = int(self.world.total_revivals[self.player].value / 100 *
|
||||
self.world.total_locations[self.player].value)
|
||||
self.total_revivals = int(self.multiworld.total_revivals[self.player].value / 100 *
|
||||
self.multiworld.total_locations[self.player].value)
|
||||
|
||||
def generate_basic(self) -> None:
|
||||
# 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:
|
||||
self.world.push_precollected(self.world.create_item("Dio's Best Friend", self.player))
|
||||
if self.multiworld.start_with_revive[self.player].value:
|
||||
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
|
||||
pool_option = self.world.item_weights[self.player].value
|
||||
pool_option = self.multiworld.item_weights[self.player].value
|
||||
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
|
||||
if pool_option == ItemWeights.option_chaos:
|
||||
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:
|
||||
junk_pool = item_pool_weights[pool_option].copy()
|
||||
else: # generate junk pool from user created presets
|
||||
junk_pool = {
|
||||
"Item Scrap, Green": self.world.green_scrap[self.player].value,
|
||||
"Item Scrap, Red": self.world.red_scrap[self.player].value,
|
||||
"Item Scrap, Yellow": self.world.yellow_scrap[self.player].value,
|
||||
"Item Scrap, White": self.world.white_scrap[self.player].value,
|
||||
"Common Item": self.world.common_item[self.player].value,
|
||||
"Uncommon Item": self.world.uncommon_item[self.player].value,
|
||||
"Legendary Item": self.world.legendary_item[self.player].value,
|
||||
"Boss Item": self.world.boss_item[self.player].value,
|
||||
"Lunar Item": self.world.lunar_item[self.player].value,
|
||||
"Equipment": self.world.equipment[self.player].value
|
||||
"Item Scrap, Green": self.multiworld.green_scrap[self.player].value,
|
||||
"Item Scrap, Red": self.multiworld.red_scrap[self.player].value,
|
||||
"Item Scrap, Yellow": self.multiworld.yellow_scrap[self.player].value,
|
||||
"Item Scrap, White": self.multiworld.white_scrap[self.player].value,
|
||||
"Common Item": self.multiworld.common_item[self.player].value,
|
||||
"Uncommon Item": self.multiworld.uncommon_item[self.player].value,
|
||||
"Legendary Item": self.multiworld.legendary_item[self.player].value,
|
||||
"Boss Item": self.multiworld.boss_item[self.player].value,
|
||||
"Lunar Item": self.multiworld.lunar_item[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
|
||||
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")
|
||||
|
||||
# Generate item pool
|
||||
|
@ -84,38 +84,38 @@ class RiskOfRainWorld(World):
|
|||
itempool += ["Dio's Best Friend"] * self.total_revivals
|
||||
|
||||
# Fill remaining items with randomly generated junk
|
||||
itempool += self.world.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()),
|
||||
k=self.world.total_locations[self.player].value - self.total_revivals)
|
||||
itempool += self.multiworld.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()),
|
||||
k=self.multiworld.total_locations[self.player].value - self.total_revivals)
|
||||
|
||||
# Convert itempool into real items
|
||||
itempool = list(map(lambda name: self.create_item(name), itempool))
|
||||
|
||||
self.world.itempool += itempool
|
||||
self.multiworld.itempool += itempool
|
||||
|
||||
def set_rules(self) -> None:
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
def create_regions(self) -> None:
|
||||
menu = create_region(self.world, self.player, "Menu")
|
||||
petrichor = create_region(self.world, self.player, "Petrichor V",
|
||||
[f"ItemPickup{i + 1}" for i in range(self.world.total_locations[self.player].value)])
|
||||
menu = create_region(self.multiworld, self.player, "Menu")
|
||||
petrichor = create_region(self.multiworld, self.player, "Petrichor V",
|
||||
[f"ItemPickup{i + 1}" for i in range(self.multiworld.total_locations[self.player].value)])
|
||||
|
||||
connection = Entrance(self.player, "Lobby", menu)
|
||||
menu.exits.append(connection)
|
||||
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):
|
||||
return {
|
||||
"itemPickupStep": self.world.item_pickup_step[self.player].value,
|
||||
"seed": "".join(self.world.slot_seeds[self.player].choice(string.digits) for _ in range(16)),
|
||||
"totalLocations": self.world.total_locations[self.player].value,
|
||||
"totalRevivals": self.world.total_revivals[self.player].value,
|
||||
"startWithDio": self.world.start_with_revive[self.player].value,
|
||||
"FinalStageDeath": self.world.final_stage_death[self.player].value
|
||||
"itemPickupStep": self.multiworld.item_pickup_step[self.player].value,
|
||||
"seed": "".join(self.multiworld.slot_seeds[self.player].choice(string.digits) for _ in range(16)),
|
||||
"totalLocations": self.multiworld.total_locations[self.player].value,
|
||||
"totalRevivals": self.multiworld.total_revivals[self.player].value,
|
||||
"startWithDio": self.multiworld.start_with_revive[self.player].value,
|
||||
"FinalStageDeath": self.multiworld.final_stage_death[self.player].value
|
||||
}
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
|
|
|
@ -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):
|
||||
# Shamelessly stolen from the ROR2 definition
|
||||
ret = Region(name, None, name, player)
|
||||
ret.world = world
|
||||
ret.multiworld = world
|
||||
if locations:
|
||||
for location in locations:
|
||||
loc_id = active_locations.get(location, 0)
|
||||
|
|
|
@ -69,15 +69,15 @@ class SA2BWorld(World):
|
|||
return {
|
||||
"ModVersion": 101,
|
||||
"MusicMap": self.music_map,
|
||||
"MusicShuffle": self.world.music_shuffle[self.player].value,
|
||||
"RequiredRank": self.world.required_rank[self.player].value,
|
||||
"ChaoRaceChecks": self.world.chao_race_checks[self.player].value,
|
||||
"ChaoGardenDifficulty": self.world.chao_garden_difficulty[self.player].value,
|
||||
"DeathLink": self.world.death_link[self.player].value,
|
||||
"IncludeMissions": self.world.include_missions[self.player].value,
|
||||
"EmblemPercentageForCannonsCore": self.world.emblem_percentage_for_cannons_core[self.player].value,
|
||||
"NumberOfLevelGates": self.world.number_of_level_gates[self.player].value,
|
||||
"LevelGateDistribution": self.world.level_gate_distribution[self.player].value,
|
||||
"MusicShuffle": self.multiworld.music_shuffle[self.player].value,
|
||||
"RequiredRank": self.multiworld.required_rank[self.player].value,
|
||||
"ChaoRaceChecks": self.multiworld.chao_race_checks[self.player].value,
|
||||
"ChaoGardenDifficulty": self.multiworld.chao_garden_difficulty[self.player].value,
|
||||
"DeathLink": self.multiworld.death_link[self.player].value,
|
||||
"IncludeMissions": self.multiworld.include_missions[self.player].value,
|
||||
"EmblemPercentageForCannonsCore": self.multiworld.emblem_percentage_for_cannons_core[self.player].value,
|
||||
"NumberOfLevelGates": self.multiworld.number_of_level_gates[self.player].value,
|
||||
"LevelGateDistribution": self.multiworld.level_gate_distribution[self.player].value,
|
||||
"EmblemsForCannonsCore": self.emblems_for_cannons_core,
|
||||
"RegionEmblemMap": self.region_emblem_map,
|
||||
"GateCosts": self.gate_costs,
|
||||
|
@ -92,14 +92,14 @@ class SA2BWorld(World):
|
|||
slot_data = self._get_slot_data()
|
||||
slot_data["MusicMap"] = self.music_map
|
||||
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
|
||||
|
||||
return slot_data
|
||||
|
||||
def get_levels_per_gate(self) -> 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)
|
||||
levels_added = 0
|
||||
|
||||
|
@ -112,8 +112,8 @@ class SA2BWorld(World):
|
|||
levels_added += 1
|
||||
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:
|
||||
early_distribution = self.world.level_gate_distribution[self.player] == 0
|
||||
if self.multiworld.level_gate_distribution[self.player] == 0 or self.multiworld.level_gate_distribution[self.player] == 2:
|
||||
early_distribution = self.multiworld.level_gate_distribution[self.player] == 0
|
||||
levels_to_distribute = 5
|
||||
gate_index_offset = 0
|
||||
while levels_to_distribute > 0:
|
||||
|
@ -134,10 +134,10 @@ class SA2BWorld(World):
|
|||
return levels_per_gate
|
||||
|
||||
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):
|
||||
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] = []
|
||||
|
||||
|
@ -155,20 +155,20 @@ class SA2BWorld(World):
|
|||
extra_junk_count = raw_emblem_count - total_emblem_count
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
shuffled_region_list = list(range(30))
|
||||
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()
|
||||
|
||||
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
|
||||
total_levels_added = 0
|
||||
current_gate = 0
|
||||
|
@ -184,8 +184,8 @@ class SA2BWorld(World):
|
|||
total_levels_added += 1
|
||||
if levels_added_to_gate >= levels_per_gate[current_gate]:
|
||||
current_gate += 1
|
||||
if current_gate > self.world.number_of_level_gates[self.player].value:
|
||||
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.multiworld.number_of_level_gates[self.player].value
|
||||
else:
|
||||
current_gate_emblems = max(
|
||||
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))
|
||||
|
||||
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)
|
||||
itempool += [self.create_item(ItemName.emblem) for _ in range(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)]
|
||||
|
||||
# Carve Traps out of junk_count
|
||||
trap_weights = []
|
||||
trap_weights += ([ItemName.omochao_trap] * self.world.omochao_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.timestop_trap] * self.world.timestop_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.confuse_trap] * self.world.confusion_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.tiny_trap] * self.world.tiny_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.omochao_trap] * self.multiworld.omochao_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.timestop_trap] * self.multiworld.timestop_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.confuse_trap] * self.multiworld.confusion_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.tiny_trap] * self.multiworld.tiny_trap_weight[self.player].value)
|
||||
|
||||
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_pool = []
|
||||
junk_keys = list(junk_table.keys())
|
||||
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))
|
||||
|
||||
itempool += junk_pool
|
||||
|
||||
trap_pool = []
|
||||
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))
|
||||
|
||||
itempool += trap_pool
|
||||
|
||||
self.world.itempool += itempool
|
||||
self.multiworld.itempool += itempool
|
||||
|
||||
# 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_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))
|
||||
elif self.world.music_shuffle[self.player] == "full":
|
||||
elif self.multiworld.music_shuffle[self.player] == "full":
|
||||
musiclist_o = list(range(0, 78))
|
||||
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))
|
||||
else:
|
||||
self.music_map = dict()
|
||||
|
||||
def create_regions(self):
|
||||
self.location_table = setup_locations(self.world, self.player)
|
||||
create_regions(self.world, self.player, self.location_table)
|
||||
self.location_table = setup_locations(self.multiworld, self.player)
|
||||
create_regions(self.multiworld, self.player, self.location_table)
|
||||
|
||||
def create_item(self, name: str, force_non_progression=False) -> Item:
|
||||
data = item_table[name]
|
||||
|
@ -269,12 +269,12 @@ class SA2BWorld(World):
|
|||
return created_item
|
||||
|
||||
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):
|
||||
spoiler_handle.write("\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)
|
||||
for x in range(len(self.gate_bosses.values())):
|
||||
text = "Gate {0} Boss: {1}\n"
|
||||
|
|
|
@ -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]],
|
||||
location_cache: List[Location], name: str) -> Region:
|
||||
region = Region(name, RegionType.Generic, name, player)
|
||||
region.world = world
|
||||
region.multiworld = world
|
||||
|
||||
if name in locations_per_region:
|
||||
for location_data in locations_per_region[name]:
|
||||
|
|
|
@ -59,31 +59,31 @@ class SC2WoLWorld(World):
|
|||
|
||||
def create_regions(self):
|
||||
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):
|
||||
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):
|
||||
setup_events(self.world, self.player, self.locked_locations, self.location_cache)
|
||||
self.world.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player)
|
||||
setup_events(self.multiworld, self.player, self.locked_locations, self.location_cache)
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player)
|
||||
|
||||
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):
|
||||
slot_data = {}
|
||||
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}:
|
||||
slot_data[option_name] = int(option.value)
|
||||
slot_req_table = {}
|
||||
|
|
|
@ -111,27 +111,27 @@ class SMWorld(World):
|
|||
def generate_early(self):
|
||||
Logic.factory('vanilla')
|
||||
|
||||
self.variaRando = VariaRandomizer(self.world, get_base_rom_path(), self.player)
|
||||
self.world.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty)
|
||||
self.variaRando = VariaRandomizer(self.multiworld, get_base_rom_path(), self.player)
|
||||
self.multiworld.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty)
|
||||
|
||||
# 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
|
||||
self.world.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('Nothing')
|
||||
self.multiworld.local_items[self.player].value.add('No Energy')
|
||||
|
||||
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):
|
||||
self.world.accessibility[self.player] = self.world.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")
|
||||
self.multiworld.accessibility[self.player] = self.multiworld.accessibility[self.player].from_text("minimal")
|
||||
logger.warning(f"accessibility forced to 'minimal' for player {self.multiworld.get_player_name(self.player)} because of 'fun' settings")
|
||||
|
||||
def generate_basic(self):
|
||||
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]
|
||||
if self.world.start_inventory_removes_from_pool[self.player]:
|
||||
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.multiworld.start_inventory_removes_from_pool[self.player]:
|
||||
for item in self.startItems:
|
||||
if (item in itemPool):
|
||||
itemPool.remove(item)
|
||||
|
@ -175,31 +175,31 @@ class SMWorld(World):
|
|||
else:
|
||||
pool.append(smitem)
|
||||
|
||||
self.world.itempool += pool
|
||||
self.multiworld.itempool += pool
|
||||
|
||||
for (location, item) in self.locked_items.items():
|
||||
self.world.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).place_locked_item(item)
|
||||
self.multiworld.get_location(location, self.player).address = None
|
||||
|
||||
startAP = self.world.get_entrance('StartAP', self.player)
|
||||
startAP.connect(self.world.get_region(self.variaRando.args.startLocation, self.player))
|
||||
startAP = self.multiworld.get_entrance('StartAP', self.player)
|
||||
startAP.connect(self.multiworld.get_region(self.variaRando.args.startLocation, self.player))
|
||||
|
||||
for src, dest in self.variaRando.randoExec.areaGraph.InterAreaTransitions:
|
||||
src_region = self.world.get_region(src.Name, self.player)
|
||||
dest_region = self.world.get_region(dest.Name, self.player)
|
||||
if ((src.Name + "->" + dest.Name, self.player) not in self.world._entrance_cache):
|
||||
src_region = self.multiworld.get_region(src.Name, self.player)
|
||||
dest_region = self.multiworld.get_region(dest.Name, self.player)
|
||||
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))
|
||||
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)
|
||||
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):
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
|
||||
def create_regions(self):
|
||||
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
|
||||
return [w & 0x00FF, (w & 0xFF00) >> 8]
|
||||
|
@ -289,7 +289,7 @@ class SMWorld(World):
|
|||
self.playerIDMap = {}
|
||||
playerIDCount = 0 # 0 is for "Archipelago" server; highest possible = 200 (201 entries)
|
||||
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:
|
||||
# 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
|
||||
|
@ -345,7 +345,7 @@ class SMWorld(World):
|
|||
|
||||
deathLink = [{"sym": symbols["config_deathlink"],
|
||||
"offset": 0,
|
||||
"values": [self.world.death_link[self.player].value]}]
|
||||
"values": [self.multiworld.death_link[self.player].value]}]
|
||||
remoteItem = [{"sym": symbols["config_remote_items"],
|
||||
"offset": 0,
|
||||
"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():
|
||||
playerNames.append({"sym": symbols["rando_player_table"],
|
||||
"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"],
|
||||
"offset": value * 2,
|
||||
"values": self.getWordArray(key)})
|
||||
|
@ -399,7 +399,7 @@ class SMWorld(World):
|
|||
# set rom name
|
||||
# 21 bytes
|
||||
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)))
|
||||
# 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
|
||||
|
@ -493,12 +493,12 @@ class SMWorld(World):
|
|||
if isinstance(itemLoc.item, SMItem) and itemLoc.item.type in ItemManager.Items else
|
||||
'ArchipelagoItem'],
|
||||
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)
|
||||
|
||||
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]
|
||||
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]
|
||||
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.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]
|
||||
|
||||
# romPatcher.writeSplitLocs(self.variaRando.args.majorsSplit, itemLocs, progItemLocs)
|
||||
|
@ -506,7 +506,7 @@ class SMWorld(World):
|
|||
romPatcher.writeRandoSettings(self.variaRando.randoExec.randoSettings, itemLocs)
|
||||
|
||||
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")
|
||||
|
||||
try:
|
||||
|
@ -516,8 +516,8 @@ class SMWorld(World):
|
|||
except:
|
||||
raise
|
||||
else:
|
||||
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)
|
||||
patch = SMDeltaPatch(os.path.splitext(outputFilename)[0] + SMDeltaPatch.patch_file_ending, player=self.player,
|
||||
player_name=self.multiworld.player_name[self.player], patched_path=outputFilename)
|
||||
patch.write()
|
||||
finally:
|
||||
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
|
||||
if rom_name:
|
||||
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):
|
||||
slot_data = {}
|
||||
if not self.world.is_race:
|
||||
if not self.multiworld.is_race:
|
||||
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["Preset"] = { "Knows": {},
|
||||
|
@ -606,11 +606,11 @@ class SMWorld(World):
|
|||
player=self.player)
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
if self.world.random.randint(0, 100) < self.world.minor_qty[self.player].value:
|
||||
power_bombs = self.world.power_bomb_qty[self.player].value
|
||||
missiles = self.world.missile_qty[self.player].value
|
||||
super_missiles = self.world.super_qty[self.player].value
|
||||
roll = self.world.random.randint(1, power_bombs + missiles + super_missiles)
|
||||
if self.multiworld.random.randint(0, 100) < self.multiworld.minor_qty[self.player].value:
|
||||
power_bombs = self.multiworld.power_bomb_qty[self.player].value
|
||||
missiles = self.multiworld.missile_qty[self.player].value
|
||||
super_missiles = self.multiworld.super_qty[self.player].value
|
||||
roll = self.multiworld.random.randint(1, power_bombs + missiles + super_missiles)
|
||||
if roll <= power_bombs:
|
||||
return "Power Bomb"
|
||||
elif roll <= power_bombs + missiles:
|
||||
|
@ -621,20 +621,20 @@ class SMWorld(World):
|
|||
return "Nothing"
|
||||
|
||||
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 = []
|
||||
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):
|
||||
and location.can_reach(self.multiworld.state):
|
||||
viable.append(location)
|
||||
self.world.random.shuffle(viable)
|
||||
key = self.world.create_item("Morph Ball", self.player)
|
||||
self.multiworld.random.shuffle(viable)
|
||||
key = self.multiworld.create_item("Morph Ball", self.player)
|
||||
loc = viable.pop()
|
||||
loc.place_locked_item(key)
|
||||
self.world.itempool[:] = [item for item in self.world.itempool if
|
||||
item.player != self.player or
|
||||
item.name != "Morph Ball"]
|
||||
self.multiworld.itempool[:] = [item for item in self.multiworld.itempool if
|
||||
item.player != self.player or
|
||||
item.name != "Morph Ball"]
|
||||
|
||||
if len(self.NothingPool) > 0:
|
||||
nonChozoLoc = []
|
||||
|
@ -647,8 +647,8 @@ class SMWorld(World):
|
|||
else:
|
||||
nonChozoLoc.append(loc)
|
||||
|
||||
self.world.random.shuffle(nonChozoLoc)
|
||||
self.world.random.shuffle(chozoLoc)
|
||||
self.multiworld.random.shuffle(nonChozoLoc)
|
||||
self.multiworld.random.shuffle(chozoLoc)
|
||||
missingCount = len(self.NothingPool) - len(nonChozoLoc)
|
||||
locations = nonChozoLoc
|
||||
if (missingCount > 0):
|
||||
|
@ -677,17 +677,17 @@ class SMWorld(World):
|
|||
break
|
||||
|
||||
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'.join(['%s%s %s %s' % (f'{self.world.get_player_name(self.player)}: '
|
||||
if self.world.players > 1 else '', src.Name,
|
||||
spoiler_handle.write('\n'.join(['%s%s %s %s' % (f'{self.multiworld.get_player_name(self.player)}: '
|
||||
if self.multiworld.players > 1 else '', src.Name,
|
||||
'<=>',
|
||||
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'.join(['%s%s %s %s' % (f'{self.world.get_player_name(self.player)}: '
|
||||
if self.world.players > 1 else '', src.Name,
|
||||
spoiler_handle.write('\n'.join(['%s%s %s %s' % (f'{self.multiworld.get_player_name(self.player)}: '
|
||||
if self.multiworld.players > 1 else '', src.Name,
|
||||
'<=>',
|
||||
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):
|
||||
ret = Region(name, RegionType.LightWorld, name, player)
|
||||
ret.world = world
|
||||
ret.multiworld = world
|
||||
if locations:
|
||||
for loc in locations:
|
||||
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))))
|
||||
|
||||
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():
|
||||
if (randoExec.areaGraph.canAccessList( state.smbm[self.player],
|
||||
key,
|
||||
|
|
|
@ -43,18 +43,18 @@ class SM64World(World):
|
|||
option_definitions = sm64_options
|
||||
|
||||
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):
|
||||
create_regions(self.world,self.player)
|
||||
create_regions(self.multiworld, self.player)
|
||||
|
||||
def set_rules(self):
|
||||
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:
|
||||
# Write area_connections to spoiler log
|
||||
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[destination],
|
||||
'entrance', self.player)
|
||||
|
@ -72,74 +72,74 @@ class SM64World(World):
|
|||
return item
|
||||
|
||||
def generate_basic(self):
|
||||
starcount = self.world.AmountOfStars[self.player].value
|
||||
if (not self.world.EnableCoinStars[self.player].value):
|
||||
starcount = max(35,self.world.AmountOfStars[self.player].value-15)
|
||||
starcount = max(starcount, self.world.FirstBowserStarDoorCost[self.player].value,
|
||||
self.world.BasementStarDoorCost[self.player].value, self.world.SecondFloorStarDoorCost[self.player].value,
|
||||
self.world.MIPS1Cost[self.player].value, self.world.MIPS2Cost[self.player].value,
|
||||
self.world.StarsToFinish[self.player].value)
|
||||
self.world.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))]
|
||||
starcount = self.multiworld.AmountOfStars[self.player].value
|
||||
if (not self.multiworld.EnableCoinStars[self.player].value):
|
||||
starcount = max(35,self.multiworld.AmountOfStars[self.player].value-15)
|
||||
starcount = max(starcount, self.multiworld.FirstBowserStarDoorCost[self.player].value,
|
||||
self.multiworld.BasementStarDoorCost[self.player].value, self.multiworld.SecondFloorStarDoorCost[self.player].value,
|
||||
self.multiworld.MIPS1Cost[self.player].value, self.multiworld.MIPS2Cost[self.player].value,
|
||||
self.multiworld.StarsToFinish[self.player].value)
|
||||
self.multiworld.itempool += [self.create_item("Power Star") for i in range(0,starcount)]
|
||||
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")
|
||||
key2 = self.create_item("Second Floor Key")
|
||||
self.world.itempool += [key1,key2]
|
||||
self.multiworld.itempool += [key1, key2]
|
||||
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")
|
||||
metalcap = self.create_item("Metal 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):
|
||||
self.world.itempool += [self.create_item(name) for name, id in cannon_item_table.items()]
|
||||
if (self.multiworld.BuddyChecks[self.player].value):
|
||||
self.multiworld.itempool += [self.create_item(name) for name, id in cannon_item_table.items()]
|
||||
else:
|
||||
self.world.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.world.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.world.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.world.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.world.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("BoB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock BoB"))
|
||||
self.multiworld.get_location("WF: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock WF"))
|
||||
self.multiworld.get_location("JRB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock JRB"))
|
||||
self.multiworld.get_location("CCM: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock CCM"))
|
||||
self.multiworld.get_location("SSL: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock SSL"))
|
||||
self.multiworld.get_location("SL: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock SL"))
|
||||
self.multiworld.get_location("WDW: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock WDW"))
|
||||
self.multiworld.get_location("TTM: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock TTM"))
|
||||
self.multiworld.get_location("THI: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock THI"))
|
||||
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):
|
||||
self.world.itempool += [self.create_item("1Up Mushroom") for i in range(0,29)]
|
||||
if (self.multiworld.ExclamationBoxes[self.player].value > 0):
|
||||
self.multiworld.itempool += [self.create_item("1Up Mushroom") for i in range(0,29)]
|
||||
else:
|
||||
self.world.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.world.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.world.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.world.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.world.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.world.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.world.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.world.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.world.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.world.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.world.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.world.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.world.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.world.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.world.get_location("Bowser in the Sky 1Up Block", 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.multiworld.get_location("CCM: 1Up Block Ice Pillar", 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.multiworld.get_location("BBH: 1Up Block Top of Mansion", 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.multiworld.get_location("HMC: 1Up Block Past Rolling Rocks", 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.multiworld.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 Back", 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.multiworld.get_location("SL: 1Up Block inside Igloo", 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.multiworld.get_location("TTM: 1Up Block on Red Mushroom", 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.multiworld.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 Windy Area", 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.multiworld.get_location("TTC: 1Up Block at the Top", 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.multiworld.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 On House in the Sky", 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.multiworld.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("Cavern of the Metal Cap 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.multiworld.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 Near Poles", 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.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:
|
||||
return "1Up Mushroom"
|
||||
|
@ -147,21 +147,21 @@ class SM64World(World):
|
|||
def fill_slot_data(self):
|
||||
return {
|
||||
"AreaRando": self.area_connections,
|
||||
"FirstBowserDoorCost": self.world.FirstBowserStarDoorCost[self.player].value,
|
||||
"BasementDoorCost": self.world.BasementStarDoorCost[self.player].value,
|
||||
"SecondFloorDoorCost": self.world.SecondFloorStarDoorCost[self.player].value,
|
||||
"MIPS1Cost": self.world.MIPS1Cost[self.player].value,
|
||||
"MIPS2Cost": self.world.MIPS2Cost[self.player].value,
|
||||
"StarsToFinish": self.world.StarsToFinish[self.player].value,
|
||||
"DeathLink": self.world.death_link[self.player].value,
|
||||
"FirstBowserDoorCost": self.multiworld.FirstBowserStarDoorCost[self.player].value,
|
||||
"BasementDoorCost": self.multiworld.BasementStarDoorCost[self.player].value,
|
||||
"SecondFloorDoorCost": self.multiworld.SecondFloorStarDoorCost[self.player].value,
|
||||
"MIPS1Cost": self.multiworld.MIPS1Cost[self.player].value,
|
||||
"MIPS2Cost": self.multiworld.MIPS2Cost[self.player].value,
|
||||
"StarsToFinish": self.multiworld.StarsToFinish[self.player].value,
|
||||
"DeathLink": self.multiworld.death_link[self.player].value,
|
||||
}
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
if self.world.players != 1:
|
||||
if self.multiworld.players != 1:
|
||||
return
|
||||
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": {
|
||||
"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:
|
||||
json.dump(data, f)
|
||||
|
||||
|
@ -182,7 +182,7 @@ class SM64World(World):
|
|||
er_hint_data = {}
|
||||
for entrance, destination in self.area_connections.items():
|
||||
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:
|
||||
er_hint_data[location.address] = sm64_internalloc_to_string[entrance]
|
||||
multidata['er_hint_data'][self.player] = er_hint_data
|
||||
|
|
|
@ -62,14 +62,14 @@ class SMWWorld(World):
|
|||
|
||||
def _get_slot_data(self):
|
||||
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,
|
||||
}
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_slot_data()
|
||||
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
|
||||
|
||||
return slot_data
|
||||
|
@ -77,20 +77,20 @@ class SMWWorld(World):
|
|||
def generate_basic(self):
|
||||
itempool: typing.List[SMWItem] = []
|
||||
|
||||
self.active_level_dict = dict(zip(generate_level_list(self.world, self.player), full_level_list))
|
||||
self.topology_present = self.world.level_shuffle[self.player]
|
||||
self.active_level_dict = dict(zip(generate_level_list(self.multiworld, self.player), full_level_list))
|
||||
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_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.world.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.world.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.donut_plains_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 1))
|
||||
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.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.multiworld.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.valley_of_bowser_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 6))
|
||||
|
||||
total_required_locations = 96
|
||||
if self.world.dragon_coin_checks[self.player]:
|
||||
if self.multiworld.dragon_coin_checks[self.player]:
|
||||
total_required_locations += 49
|
||||
|
||||
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.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)
|
||||
for _ in range(self.world.number_of_yoshi_eggs[self.player])]
|
||||
self.world.get_location(LocationName.yoshis_house, self.player).place_locked_item(self.create_item(ItemName.victory))
|
||||
for _ in range(self.multiworld.number_of_yoshi_eggs[self.player])]
|
||||
self.multiworld.get_location(LocationName.yoshis_house, self.player).place_locked_item(self.create_item(ItemName.victory))
|
||||
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)
|
||||
trap_weights = []
|
||||
trap_weights += ([ItemName.ice_trap] * self.world.ice_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.stun_trap] * self.world.stun_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.literature_trap] * self.world.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_weights += ([ItemName.ice_trap] * self.multiworld.ice_trap_weight[self.player].value)
|
||||
trap_weights += ([ItemName.stun_trap] * self.multiworld.stun_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.multiworld.trap_fill_percentage[self.player].value / 100.0))
|
||||
junk_count -= trap_count
|
||||
|
||||
trap_pool = []
|
||||
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))
|
||||
|
||||
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]
|
||||
|
||||
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):
|
||||
rompath = "" # if variable is not declared finally clause may fail
|
||||
try:
|
||||
world = self.world
|
||||
world = self.multiworld
|
||||
player = self.player
|
||||
|
||||
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)
|
||||
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
|
||||
if rom_name:
|
||||
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]]):
|
||||
if self.topology_present:
|
||||
|
@ -212,18 +212,18 @@ class SMWWorld(World):
|
|||
if level_index >= world_cutoffs[i]:
|
||||
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
|
||||
|
||||
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]
|
||||
break
|
||||
|
||||
hint_data[self.player] = er_hint_data
|
||||
|
||||
def create_regions(self):
|
||||
location_table = setup_locations(self.world, self.player)
|
||||
create_regions(self.world, self.player, location_table)
|
||||
location_table = setup_locations(self.multiworld, self.player)
|
||||
create_regions(self.multiworld, self.player, location_table)
|
||||
|
||||
def create_item(self, name: str, force_non_progression=False) -> Item:
|
||||
data = item_table[name]
|
||||
|
@ -244,4 +244,4 @@ class SMWWorld(World):
|
|||
return created_item
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
|
|
@ -190,24 +190,24 @@ class SMZ3World(World):
|
|||
self.config = Config()
|
||||
self.config.GameMode = GameMode.Multiworld
|
||||
self.config.Z3Logic = Z3Logic.Normal
|
||||
self.config.SMLogic = SMLogic(self.world.sm_logic[self.player].value)
|
||||
self.config.SwordLocation = SwordLocation(self.world.sword_location[self.player].value)
|
||||
self.config.MorphLocation = MorphLocation(self.world.morph_location[self.player].value)
|
||||
self.config.Goal = Goal(self.world.goal[self.player].value)
|
||||
self.config.KeyShuffle = KeyShuffle(self.world.key_shuffle[self.player].value)
|
||||
self.config.OpenTower = OpenTower(self.world.open_tower[self.player].value)
|
||||
self.config.GanonVulnerable = GanonVulnerable(self.world.ganon_vulnerable[self.player].value)
|
||||
self.config.OpenTourian = OpenTourian(self.world.open_tourian[self.player].value)
|
||||
self.config.SMLogic = SMLogic(self.multiworld.sm_logic[self.player].value)
|
||||
self.config.SwordLocation = SwordLocation(self.multiworld.sword_location[self.player].value)
|
||||
self.config.MorphLocation = MorphLocation(self.multiworld.morph_location[self.player].value)
|
||||
self.config.Goal = Goal(self.multiworld.goal[self.player].value)
|
||||
self.config.KeyShuffle = KeyShuffle(self.multiworld.key_shuffle[self.player].value)
|
||||
self.config.OpenTower = OpenTower(self.multiworld.open_tower[self.player].value)
|
||||
self.config.GanonVulnerable = GanonVulnerable(self.multiworld.ganon_vulnerable[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.smz3World = TotalSMZ3World(self.config, self.world.get_player_name(self.player), self.player, self.world.seed_name)
|
||||
self.local_random = random.Random(self.multiworld.random.randint(0, 1000))
|
||||
self.smz3World = TotalSMZ3World(self.config, self.multiworld.get_player_name(self.player), self.player, self.multiworld.seed_name)
|
||||
self.smz3DungeonItems = []
|
||||
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):
|
||||
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.reverse()
|
||||
self.progression = TotalSMZ3Item.Item.CreateProgressionPool(self.smz3World)
|
||||
|
@ -224,25 +224,25 @@ class SMZ3World(World):
|
|||
else:
|
||||
progressionItems = self.progression
|
||||
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] + \
|
||||
[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.world.itempool += itemPool
|
||||
self.multiworld.itempool += itemPool
|
||||
|
||||
def set_rules(self):
|
||||
# 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").TowerAscend(state.smz3state[self.player])
|
||||
|
||||
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]))
|
||||
for loc in region.Locations:
|
||||
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: \
|
||||
item.game == "SMZ3" and \
|
||||
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):
|
||||
self.create_locations(self.player)
|
||||
startRegion = self.create_region(self.world, self.player, 'Menu')
|
||||
self.world.regions.append(startRegion)
|
||||
startRegion = self.create_region(self.multiworld, self.player, 'Menu')
|
||||
self.multiworld.regions.append(startRegion)
|
||||
|
||||
for region in self.smz3World.Regions:
|
||||
currentRegion = self.create_region(self.world, self.player, region.Name, region.locationLookup.keys(), [region.Name + "->" + 'Menu'])
|
||||
self.world.regions.append(currentRegion)
|
||||
entrance = self.world.get_entrance(region.Name + "->" + 'Menu', self.player)
|
||||
currentRegion = self.create_region(self.multiworld, self.player, region.Name, region.locationLookup.keys(), [region.Name + "->" + 'Menu'])
|
||||
self.multiworld.regions.append(currentRegion)
|
||||
entrance = self.multiworld.get_entrance(region.Name + "->" + 'Menu', self.player)
|
||||
entrance.connect(startRegion)
|
||||
exit = Entrance(self.player, 'Menu' + "->" + region.Name, startRegion)
|
||||
startRegion.exits.append(exit)
|
||||
|
@ -347,7 +347,7 @@ class SMZ3World(World):
|
|||
sm_remote_idx = 0
|
||||
lttp_remote_idx = 0
|
||||
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:
|
||||
patch[0x390000 + sm_remote_idx*64] = self.convert_to_sm_item_name(location.APLocation.item.name)
|
||||
sm_remote_idx += 1
|
||||
|
@ -369,12 +369,12 @@ class SMZ3World(World):
|
|||
patch = {}
|
||||
|
||||
# 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])
|
||||
|
||||
# z3HeartBeep
|
||||
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]])
|
||||
|
||||
# z3HeartColor
|
||||
|
@ -384,17 +384,17 @@ class SMZ3World(World):
|
|||
[0x2C, [0xC9, 0x69]],
|
||||
[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]
|
||||
for i in range(0, 20, 2):
|
||||
patch[self.SnesCustomization(0xDFA1E + i)] = bytearray([hud])
|
||||
patch[self.SnesCustomization(0x1BD6AA)] = bytearray(fileSelect)
|
||||
|
||||
# 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
|
||||
if (self.world.energy_beep[self.player].value == 0):
|
||||
if (self.multiworld.energy_beep[self.player].value == 0):
|
||||
for ([addr, value]) in [
|
||||
[0x90EA9B, 0x80],
|
||||
[0x90F337, 0x80],
|
||||
|
@ -411,12 +411,12 @@ class SMZ3World(World):
|
|||
base_combined_rom = basepatch.apply(base_combined_rom)
|
||||
|
||||
patcher = TotalSMZ3Patch(self.smz3World,
|
||||
[world.smz3World for key, world in self.world.worlds.items() if isinstance(world, SMZ3World) and hasattr(world, "smz3World")],
|
||||
self.world.seed_name,
|
||||
self.world.seed,
|
||||
self.local_random,
|
||||
{v: k for k, v in self.world.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))))
|
||||
[world.smz3World for key, world in self.multiworld.worlds.items() if isinstance(world, SMZ3World) and hasattr(world, "smz3World")],
|
||||
self.multiworld.seed_name,
|
||||
self.multiworld.seed,
|
||||
self.local_random,
|
||||
{v: k for k, v in self.multiworld.player_name.items()},
|
||||
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.update(self.apply_sm_custom_sprite())
|
||||
patches.update(self.apply_item_names())
|
||||
|
@ -427,13 +427,13 @@ class SMZ3World(World):
|
|||
base_combined_rom[addr + offset] = byte
|
||||
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")
|
||||
with open(filename, "wb") as binary_file:
|
||||
binary_file.write(base_combined_rom)
|
||||
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)
|
||||
patch = SMZ3DeltaPatch(os.path.splitext(filename)[0] + SMZ3DeltaPatch.patch_file_ending, player=self.player,
|
||||
player_name=self.multiworld.player_name[self.player], patched_path=filename)
|
||||
patch.write()
|
||||
os.remove(filename)
|
||||
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
|
||||
if rom_name:
|
||||
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
|
||||
|
||||
def fill_slot_data(self):
|
||||
|
@ -495,18 +495,18 @@ class SMZ3World(World):
|
|||
|
||||
if (not self.smz3World.Config.Keysanity):
|
||||
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:
|
||||
all_state.remove(item)
|
||||
|
||||
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
|
||||
# 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
|
||||
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]
|
||||
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):
|
||||
|
@ -523,18 +523,18 @@ class SMZ3World(World):
|
|||
return []
|
||||
|
||||
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):
|
||||
self.world.spoiler.unreachables.update(self.unreachable)
|
||||
self.multiworld.spoiler.unreachables.update(self.unreachable)
|
||||
|
||||
def JunkFillGT(self, factor):
|
||||
poolLength = len(self.world.itempool)
|
||||
playerGroups = self.world.get_player_groups(self.player)
|
||||
poolLength = len(self.multiworld.itempool)
|
||||
playerGroups = self.multiworld.get_player_groups(self.player)
|
||||
playerGroups.add(self.player)
|
||||
junkPoolIdx = [i for i in range(0, poolLength)
|
||||
if self.world.itempool[i].classification in (ItemClassification.filler, ItemClassification.trap) and
|
||||
self.world.itempool[i].player in playerGroups]
|
||||
junkPoolIdx = [i for i in range(0, poolLength)
|
||||
if self.multiworld.itempool[i].classification in (ItemClassification.filler, ItemClassification.trap) and
|
||||
self.multiworld.itempool[i].player in playerGroups]
|
||||
toRemove = []
|
||||
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
|
||||
|
@ -544,19 +544,19 @@ class SMZ3World(World):
|
|||
if loc.name in self.locationNamesGT and loc.item is None:
|
||||
poolLength = len(junkPoolIdx)
|
||||
# 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):
|
||||
i = (start + off) % poolLength
|
||||
candidate = self.world.itempool[junkPoolIdx[i]]
|
||||
if junkPoolIdx[i] not in toRemove and loc.can_fill(self.world.state, candidate, False):
|
||||
candidate = self.multiworld.itempool[junkPoolIdx[i]]
|
||||
if junkPoolIdx[i] not in toRemove and loc.can_fill(self.multiworld.state, candidate, False):
|
||||
itemFromPool = candidate
|
||||
toRemove.append(junkPoolIdx[i])
|
||||
break
|
||||
self.world.push_item(loc, itemFromPool, False)
|
||||
self.multiworld.push_item(loc, itemFromPool, False)
|
||||
loc.event = False
|
||||
toRemove.sort(reverse = True)
|
||||
for i in toRemove:
|
||||
self.world.itempool.pop(i)
|
||||
self.multiworld.itempool.pop(i)
|
||||
|
||||
def FillItemAtLocation(self, itemPool, itemType, location):
|
||||
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")
|
||||
else:
|
||||
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:
|
||||
self.world.get_location(location.Name, self.player).place_locked_item(itemFromPool)
|
||||
self.world.itempool.remove(itemFromPool)
|
||||
self.multiworld.get_location(location.Name, self.player).place_locked_item(itemFromPool)
|
||||
self.multiworld.itempool.remove(itemFromPool)
|
||||
else:
|
||||
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:
|
||||
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)
|
||||
itemPool.remove(itemToPlace)
|
||||
|
||||
def FrontFillItemInOwnWorld(self, itemPool, itemType):
|
||||
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):
|
||||
raise Exception(f"Tried to front fill {item.Name} in, but no location was available")
|
||||
|
||||
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:
|
||||
self.world.get_location(location.Name, self.player).place_locked_item(itemFromPool)
|
||||
self.world.itempool.remove(itemFromPool)
|
||||
self.multiworld.get_location(location.Name, self.player).place_locked_item(itemFromPool)
|
||||
self.multiworld.itempool.remove(itemFromPool)
|
||||
itemPool.remove(item)
|
||||
|
||||
def InitialFillInOwnWorld(self):
|
||||
|
@ -621,7 +621,7 @@ class SMZ3World(World):
|
|||
|
||||
def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
ret = Region(name, RegionType.LightWorld, name, player)
|
||||
ret.world = world
|
||||
ret.multiworld = world
|
||||
if locations:
|
||||
for loc in locations:
|
||||
location = self.locations[loc]
|
||||
|
|
|
@ -178,11 +178,11 @@ class SoEWorld(World):
|
|||
|
||||
def generate_early(self) -> None:
|
||||
# store option values that change logic
|
||||
self.energy_core = self.world.energy_core[self.player].value
|
||||
self.required_fragments = self.world.required_fragments[self.player].value
|
||||
if self.required_fragments > self.world.available_fragments[self.player].value:
|
||||
self.world.available_fragments[self.player].value = self.required_fragments
|
||||
self.available_fragments = self.world.available_fragments[self.player].value
|
||||
self.energy_core = self.multiworld.energy_core[self.player].value
|
||||
self.required_fragments = self.multiworld.required_fragments[self.player].value
|
||||
if self.required_fragments > self.multiworld.available_fragments[self.player].value:
|
||||
self.multiworld.available_fragments[self.player].value = self.required_fragments
|
||||
self.available_fragments = self.multiworld.available_fragments[self.player].value
|
||||
|
||||
def create_event(self, event: str) -> Item:
|
||||
return SoEItem(event, ItemClassification.progression, None, self.player)
|
||||
|
@ -209,12 +209,12 @@ class SoEWorld(World):
|
|||
|
||||
def create_regions(self):
|
||||
# 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?
|
||||
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)]
|
||||
self.world.regions += [r]
|
||||
self.multiworld.regions += [r]
|
||||
|
||||
# group locations into spheres (1, 2, 3+ at index 0, 1, 2)
|
||||
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
|
||||
for trash_sphere, fills in trash_fills.items():
|
||||
for typ, counts in fills.items():
|
||||
count = counts[self.world.difficulty[self.player].value]
|
||||
for location in self.world.random.sample(spheres[trash_sphere][typ], count):
|
||||
count = counts[self.multiworld.difficulty[self.player].value]
|
||||
for location in self.multiworld.random.sample(spheres[trash_sphere][typ], count):
|
||||
location.progress_type = LocationProgressType.EXCLUDED
|
||||
# TODO: do we need to set an item rule?
|
||||
|
||||
|
@ -243,7 +243,7 @@ class SoEWorld(World):
|
|||
if item.name in {"Gauge", "Wheel"}:
|
||||
return False
|
||||
# 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"}:
|
||||
return False
|
||||
return True
|
||||
|
@ -253,16 +253,16 @@ class SoEWorld(World):
|
|||
add_item_rule(location, sphere1_blocked_items_rule)
|
||||
|
||||
# make some logically late(r) bosses priority locations to increase complexity
|
||||
if self.world.difficulty[self.player] == Difficulty.option_mystery:
|
||||
late_count = self.world.random.randint(0, 2)
|
||||
if self.multiworld.difficulty[self.player] == Difficulty.option_mystery:
|
||||
late_count = self.multiworld.random.randint(0, 2)
|
||||
else:
|
||||
late_count = self.world.difficulty[self.player].value
|
||||
late_count = self.multiworld.difficulty[self.player].value
|
||||
late_bosses = ("Tiny", "Aquagoth", "Megataur", "Rimsala",
|
||||
"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
|
||||
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 locations in sphere.values():
|
||||
for location in locations:
|
||||
|
@ -271,9 +271,9 @@ class SoEWorld(World):
|
|||
location.progress_type = LocationProgressType.PRIORITY
|
||||
|
||||
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):
|
||||
# add regular items to the pool
|
||||
|
@ -298,17 +298,17 @@ class SoEWorld(World):
|
|||
for _ in range(self.available_fragments - 1):
|
||||
if len(ingredients) < 1:
|
||||
break # out of ingredients to replace
|
||||
r = self.world.random.choice(ingredients)
|
||||
r = self.multiworld.random.choice(ingredients)
|
||||
ingredients.remove(r)
|
||||
items[r] = self.create_item("Energy Core Fragment")
|
||||
|
||||
# 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_names = {}
|
||||
if trap_count > 0:
|
||||
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_names[trap_type] = trap_option.item_name
|
||||
trap_chances_total = sum(trap_chances.values())
|
||||
|
@ -318,7 +318,7 @@ class SoEWorld(World):
|
|||
trap_chances_total = len(trap_chances)
|
||||
|
||||
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():
|
||||
if v < c:
|
||||
return self.create_item(trap_names[t])
|
||||
|
@ -328,26 +328,26 @@ class SoEWorld(World):
|
|||
for _ in range(trap_count):
|
||||
if len(ingredients) < 1:
|
||||
break # out of ingredients to replace
|
||||
r = self.world.random.choice(ingredients)
|
||||
r = self.multiworld.random.choice(ingredients)
|
||||
ingredients.remove(r)
|
||||
items[r] = create_trap()
|
||||
|
||||
self.world.itempool += items
|
||||
self.multiworld.itempool += items
|
||||
|
||||
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_rule(self.world.get_location('Done', self.player),
|
||||
lambda state: state.soe_has(pyevermizer.P_FINAL_BOSS, self.world, self.player))
|
||||
set_rule(self.world.get_entrance('New Game', self.player), lambda state: True)
|
||||
set_rule(self.multiworld.get_location('Done', self.player),
|
||||
lambda state: state.soe_has(pyevermizer.P_FINAL_BOSS, self.multiworld, self.player))
|
||||
set_rule(self.multiworld.get_entrance('New Game', self.player), lambda state: True)
|
||||
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))
|
||||
|
||||
def make_rule(self, requires: typing.List[typing.Tuple[int, int]]) -> typing.Callable[[typing.Any], bool]:
|
||||
def rule(state) -> bool:
|
||||
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 True
|
||||
|
||||
|
@ -358,20 +358,20 @@ class SoEWorld(World):
|
|||
|
||||
def generate_basic(self):
|
||||
# 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
|
||||
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')
|
||||
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
|
||||
if self.energy_core == EnergyCore.option_vanilla:
|
||||
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
|
||||
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):
|
||||
player_name = self.world.get_player_name(self.player)
|
||||
player_name = self.multiworld.get_player_name(self.player)
|
||||
self.connect_name = player_name[:32]
|
||||
while len(self.connect_name.encode('utf-8')) > 32:
|
||||
self.connect_name = self.connect_name[:-1]
|
||||
|
@ -379,27 +379,27 @@ class SoEWorld(World):
|
|||
placement_file = ""
|
||||
out_file = ""
|
||||
try:
|
||||
money = self.world.money_modifier[self.player].value
|
||||
exp = self.world.exp_modifier[self.player].value
|
||||
money = self.multiworld.money_modifier[self.player].value
|
||||
exp = self.multiworld.exp_modifier[self.player].value
|
||||
switches: typing.List[str] = []
|
||||
if self.world.death_link[self.player].value:
|
||||
if self.multiworld.death_link[self.player].value:
|
||||
switches.append("--death-link")
|
||||
if self.energy_core == EnergyCore.option_fragments:
|
||||
switches.extend(('--available-fragments', str(self.available_fragments),
|
||||
'--required-fragments', str(self.required_fragments)))
|
||||
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'
|
||||
placement_file = out_base + '.txt'
|
||||
patch_file = out_base + '.apsoe'
|
||||
flags = 'l' # spoiler log
|
||||
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'):
|
||||
flags += option.to_flag()
|
||||
|
||||
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
|
||||
assert item is not None, "Can't handle unfilled location"
|
||||
if item.code is None or location.address is None:
|
||||
|
@ -414,7 +414,7 @@ class SoEWorld(World):
|
|||
|
||||
if not os.path.exists(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)):
|
||||
raise RuntimeError()
|
||||
patch = SoEDeltaPatch(patch_file, player=self.player,
|
||||
|
@ -434,12 +434,12 @@ class SoEWorld(World):
|
|||
# wait for self.connect_name to be available.
|
||||
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
|
||||
if self.connect_name and self.connect_name != self.world.player_name[self.player]:
|
||||
payload = multidata["connect_names"][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.multiworld.player_name[self.player]]
|
||||
multidata["connect_names"][self.connect_name] = payload
|
||||
|
||||
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):
|
||||
|
|
|
@ -40,10 +40,10 @@ class SpireWorld(World):
|
|||
|
||||
def _get_slot_data(self):
|
||||
return {
|
||||
'seed': "".join(self.world.slot_seeds[self.player].choice(string.ascii_letters) for i in range(16)),
|
||||
'character': self.world.character[self.player],
|
||||
'ascension': self.world.ascension[self.player],
|
||||
'heart_run': self.world.heart_run[self.player]
|
||||
'seed': "".join(self.multiworld.slot_seeds[self.player].choice(string.ascii_letters) for i in range(16)),
|
||||
'character': self.multiworld.character[self.player],
|
||||
'ascension': self.multiworld.ascension[self.player],
|
||||
'heart_run': self.multiworld.heart_run[self.player]
|
||||
}
|
||||
|
||||
def generate_basic(self):
|
||||
|
@ -55,40 +55,40 @@ class SpireWorld(World):
|
|||
item = SpireItem(name, self.player)
|
||||
pool.append(item)
|
||||
|
||||
self.world.itempool += pool
|
||||
self.multiworld.itempool += pool
|
||||
|
||||
# Pair up our event locations with our event items
|
||||
for event, item in event_item_pairs.items():
|
||||
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':
|
||||
self.world.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
|
||||
if self.multiworld.logic[self.player] != 'no logic':
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
|
||||
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return SpireItem(name, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player)
|
||||
create_regions(self.multiworld, self.player)
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_slot_data()
|
||||
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)
|
||||
return slot_data
|
||||
|
||||
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):
|
||||
ret = Region(name, RegionType.Generic, name, player)
|
||||
ret.world = world
|
||||
ret.multiworld = world
|
||||
if locations:
|
||||
for location in locations:
|
||||
loc_id = location_table.get(location, 0)
|
||||
|
|
|
@ -277,7 +277,7 @@ aggression_rules: Dict[int, Callable[["CollectionState", int], bool]] = {
|
|||
|
||||
def set_rules(subnautica_world: "SubnauticaWorld"):
|
||||
player = subnautica_world.player
|
||||
world = subnautica_world.world
|
||||
world = subnautica_world.multiworld
|
||||
|
||||
for loc in location_table.values():
|
||||
set_location_rule(world, player, loc)
|
||||
|
|
|
@ -47,22 +47,22 @@ class SubnauticaWorld(World):
|
|||
creatures_to_scan: List[str]
|
||||
|
||||
def generate_early(self) -> None:
|
||||
if "Seaglide Fragment" not in self.world.early_items[self.player]:
|
||||
self.world.early_items[self.player].value["Seaglide Fragment"] = 2
|
||||
if "Seaglide Fragment" not in self.multiworld.early_items[self.player]:
|
||||
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()
|
||||
|
||||
self.world.creature_scans[self.player].value = min(
|
||||
self.multiworld.creature_scans[self.player].value = min(
|
||||
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.world.creature_scans[self.player].value)
|
||||
self.creatures_to_scan = self.multiworld.random.sample(creature_pool,
|
||||
self.multiworld.creature_scans[self.player].value)
|
||||
|
||||
def create_regions(self):
|
||||
self.world.regions += [
|
||||
self.multiworld.regions += [
|
||||
self.create_region("Menu", None, ["Lifepod 5"]),
|
||||
self.create_region("Planet 4546B",
|
||||
Locations.events +
|
||||
|
@ -75,13 +75,13 @@ class SubnauticaWorld(World):
|
|||
|
||||
def generate_basic(self):
|
||||
# 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
|
||||
pool = []
|
||||
neptune_launch_platform = None
|
||||
extras = self.world.creature_scans[self.player].value
|
||||
valuable = self.world.item_pool[self.player] == Options.ItemPool.option_valuable
|
||||
extras = self.multiworld.creature_scans[self.player].value
|
||||
valuable = self.multiworld.item_pool[self.player] == Options.ItemPool.option_valuable
|
||||
for item in item_table.values():
|
||||
for i in range(item["count"]):
|
||||
subnautica_item = self.create_item(item["name"])
|
||||
|
@ -92,26 +92,26 @@ class SubnauticaWorld(World):
|
|||
else:
|
||||
pool.append(subnautica_item)
|
||||
|
||||
for item_name in self.world.random.choices(sorted(Items.advancement_item_names - {"Neptune Launch Platform"}),
|
||||
k=extras):
|
||||
for item_name in self.multiworld.random.choices(sorted(Items.advancement_item_names - {"Neptune Launch Platform"}),
|
||||
k=extras):
|
||||
item = self.create_item(item_name)
|
||||
item.classification = ItemClassification.filler # as it's an extra, just fast-fill it somewhere
|
||||
pool.append(item)
|
||||
|
||||
self.world.itempool += pool
|
||||
self.multiworld.itempool += pool
|
||||
|
||||
# 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)
|
||||
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))
|
||||
# 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]:
|
||||
goal: Options.Goal = self.world.goal[self.player]
|
||||
item_pool: Options.ItemPool = self.world.item_pool[self.player]
|
||||
goal: Options.Goal = self.multiworld.goal[self.player]
|
||||
item_pool: Options.ItemPool = self.multiworld.item_pool[self.player]
|
||||
vanilla_tech: List[str] = []
|
||||
if item_pool == Options.ItemPool.option_valuable:
|
||||
for item in Items.item_table.values():
|
||||
|
@ -122,7 +122,7 @@ class SubnauticaWorld(World):
|
|||
"goal": goal.current_key,
|
||||
"vanilla_tech": vanilla_tech,
|
||||
"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
|
||||
|
@ -136,7 +136,7 @@ class SubnauticaWorld(World):
|
|||
|
||||
def create_region(self, name: str, locations=None, exits=None):
|
||||
ret = Region(name, RegionType.Generic, name, self.player)
|
||||
ret.world = self.world
|
||||
ret.multiworld = self.multiworld
|
||||
if locations:
|
||||
for location in locations:
|
||||
loc_id = self.location_name_to_id.get(location, None)
|
||||
|
|
|
@ -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:
|
||||
region = Region(name, RegionType.Generic, name, player)
|
||||
region.world = world
|
||||
region.multiworld = world
|
||||
|
||||
if name in locations_per_region:
|
||||
for location_data in locations_per_region[name]:
|
||||
|
|
|
@ -64,47 +64,47 @@ class TimespinnerWorld(World):
|
|||
|
||||
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
|
||||
if self.world.start_inventory[self.player].value.pop('Meyef', 0) > 0:
|
||||
self.world.StartWithMeyef[self.player].value = self.world.StartWithMeyef[self.player].option_true
|
||||
if self.world.start_inventory[self.player].value.pop('Talaria Attachment', 0) > 0:
|
||||
self.world.QuickSeed[self.player].value = self.world.QuickSeed[self.player].option_true
|
||||
if self.world.start_inventory[self.player].value.pop('Jewelry Box', 0) > 0:
|
||||
self.world.StartWithJewelryBox[self.player].value = self.world.StartWithJewelryBox[self.player].option_true
|
||||
if self.multiworld.start_inventory[self.player].value.pop('Meyef', 0) > 0:
|
||||
self.multiworld.StartWithMeyef[self.player].value = self.multiworld.StartWithMeyef[self.player].option_true
|
||||
if self.multiworld.start_inventory[self.player].value.pop('Talaria Attachment', 0) > 0:
|
||||
self.multiworld.QuickSeed[self.player].value = self.multiworld.QuickSeed[self.player].option_true
|
||||
if self.multiworld.start_inventory[self.player].value.pop('Jewelry Box', 0) > 0:
|
||||
self.multiworld.StartWithJewelryBox[self.player].value = self.multiworld.StartWithJewelryBox[self.player].option_true
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player, get_locations(self.world, self.player),
|
||||
self.location_cache, self.pyramid_keys_unlock)
|
||||
create_regions(self.multiworld, self.player, get_locations(self.multiworld, self.player),
|
||||
self.location_cache, self.pyramid_keys_unlock)
|
||||
|
||||
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:
|
||||
return self.world.random.choice(filler_items)
|
||||
return self.multiworld.random.choice(filler_items)
|
||||
|
||||
def set_rules(self):
|
||||
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):
|
||||
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"):
|
||||
place_first_progression_item(self.world, self.player, excluded_items, self.locked_locations)
|
||||
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.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]:
|
||||
slot_data: Dict[str, object] = {}
|
||||
|
||||
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["ProgressiveVerticalMovement"] = False
|
||||
|
|
|
@ -44,42 +44,42 @@ class V6World(World):
|
|||
option_definitions = v6_options
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world,self.player)
|
||||
create_regions(self.multiworld, self.player)
|
||||
|
||||
def set_rules(self):
|
||||
self.area_connections = {}
|
||||
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:
|
||||
return V6Item(name, ItemClassification.progression, item_table[name], self.player)
|
||||
|
||||
def generate_basic(self):
|
||||
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_s = musiclist_o.copy()
|
||||
if self.world.MusicRandomizer[self.player].value:
|
||||
self.world.random.shuffle(musiclist_s)
|
||||
if self.multiworld.MusicRandomizer[self.player].value:
|
||||
self.multiworld.random.shuffle(musiclist_s)
|
||||
self.music_map = dict(zip(musiclist_o, musiclist_s))
|
||||
|
||||
def fill_slot_data(self):
|
||||
return {
|
||||
"MusicRando": self.music_map,
|
||||
"AreaRando": self.area_connections,
|
||||
"DoorCost": self.world.DoorCost[self.player].value,
|
||||
"DoorCost": self.multiworld.DoorCost[self.player].value,
|
||||
"AreaCostRando": self.area_cost_map,
|
||||
"DeathLink": self.world.death_link[self.player].value,
|
||||
"DeathLink_Amnesty": self.world.DeathLinkAmnesty[self.player].value
|
||||
"DeathLink": self.multiworld.death_link[self.player].value,
|
||||
"DeathLink_Amnesty": self.multiworld.DeathLinkAmnesty[self.player].value
|
||||
}
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
if self.world.players != 1:
|
||||
if self.multiworld.players != 1:
|
||||
return
|
||||
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": {
|
||||
"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:
|
||||
json.dump(data, f)
|
||||
|
|
|
@ -54,7 +54,7 @@ class WitnessWorld(World):
|
|||
|
||||
def _get_slot_data(self):
|
||||
return {
|
||||
'seed': self.world.random.randint(0, 1000000),
|
||||
'seed': self.multiworld.random.randint(0, 1000000),
|
||||
'victory_location': int(self.player_logic.VICTORY_LOCATION, 16),
|
||||
'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID,
|
||||
'item_id_to_door_hexes': self.items.ITEM_ID_TO_DOOR_HEX,
|
||||
|
@ -66,19 +66,19 @@ class WitnessWorld(World):
|
|||
}
|
||||
|
||||
def generate_early(self):
|
||||
if not (is_option_enabled(self.world, self.player, "shuffle_symbols")
|
||||
or get_option_value(self.world, self.player, "shuffle_doors")
|
||||
or is_option_enabled(self.world, self.player, "shuffle_lasers")):
|
||||
if self.world.players == 1:
|
||||
if not (is_option_enabled(self.multiworld, self.player, "shuffle_symbols")
|
||||
or get_option_value(self.multiworld, self.player, "shuffle_doors")
|
||||
or is_option_enabled(self.multiworld, self.player, "shuffle_lasers")):
|
||||
if self.multiworld.players == 1:
|
||||
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.")
|
||||
else:
|
||||
raise Exception("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle,"
|
||||
" Door Shuffle or Laser Shuffle.")
|
||||
|
||||
self.player_logic = WitnessPlayerLogic(self.world, self.player)
|
||||
self.locat = WitnessPlayerLocations(self.world, self.player, self.player_logic)
|
||||
self.items = WitnessPlayerItems(self.locat, self.world, self.player, self.player_logic)
|
||||
self.player_logic = WitnessPlayerLogic(self.multiworld, self.player)
|
||||
self.locat = WitnessPlayerLocations(self.multiworld, self.player, self.player_logic)
|
||||
self.items = WitnessPlayerItems(self.locat, self.multiworld, self.player, self.player_logic)
|
||||
self.regio = WitnessRegions(self.locat)
|
||||
|
||||
self.log_ids_to_hints = dict()
|
||||
|
@ -99,12 +99,12 @@ class WitnessWorld(World):
|
|||
less_junk = 0
|
||||
|
||||
# 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:
|
||||
random_good_item = self.world.random.choice(self.items.GOOD_ITEMS)
|
||||
if symbols and get_option_value(self.multiworld, self.player, "puzzle_randomization") != 1:
|
||||
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
|
||||
)
|
||||
first_check.place_locked_item(items_by_name[random_good_item])
|
||||
|
@ -113,7 +113,7 @@ class WitnessWorld(World):
|
|||
less_junk = 1
|
||||
|
||||
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])
|
||||
|
||||
for item in self.items.EXTRA_AMOUNTS:
|
||||
|
@ -133,28 +133,28 @@ class WitnessWorld(World):
|
|||
item_obj = self.create_item(
|
||||
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)
|
||||
|
||||
self.world.itempool += pool
|
||||
self.multiworld.itempool += pool
|
||||
|
||||
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):
|
||||
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:
|
||||
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)
|
||||
|
||||
audio_logs = get_audio_logs().copy()
|
||||
|
||||
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
|
||||
|
||||
|
@ -169,7 +169,7 @@ class WitnessWorld(World):
|
|||
audio_log = audio_logs.pop()
|
||||
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:
|
||||
audio_log = audio_logs.pop()
|
||||
|
@ -181,7 +181,7 @@ class WitnessWorld(World):
|
|||
|
||||
for option_name in the_witness_options:
|
||||
slot_data[option_name] = get_option_value(
|
||||
self.world, self.player, option_name
|
||||
self.multiworld, self.player, option_name
|
||||
)
|
||||
|
||||
return slot_data
|
||||
|
@ -234,7 +234,7 @@ def create_region(world: MultiWorld, player: int, name: str,
|
|||
"""
|
||||
|
||||
ret = Region(name, RegionType.Generic, name, player)
|
||||
ret.world = world
|
||||
ret.multiworld = world
|
||||
if region_locations:
|
||||
for location in region_locations:
|
||||
loc_id = locat.CHECK_LOCATION_TABLE[location]
|
||||
|
|
|
@ -121,10 +121,10 @@ class ZillionWorld(World):
|
|||
raise FileNotFoundError(rom_file)
|
||||
|
||||
def generate_early(self) -> None:
|
||||
if not hasattr(self.world, "zillion_logic_cache"):
|
||||
setattr(self.world, "zillion_logic_cache", {})
|
||||
if not hasattr(self.multiworld, "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
|
||||
|
||||
|
@ -148,7 +148,7 @@ class ZillionWorld(World):
|
|||
assert self.zz_system.randomizer, "generate_early hasn't been called"
|
||||
assert self.id_to_zz_item, "generate_early hasn't been called"
|
||||
p = self.player
|
||||
w = self.world
|
||||
w = self.multiworld
|
||||
self.my_locations = []
|
||||
|
||||
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():
|
||||
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)
|
||||
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)
|
||||
queue = deque([start])
|
||||
|
@ -191,7 +191,7 @@ class ZillionWorld(World):
|
|||
loc.access_rule = access_rule
|
||||
if not (limited_skill >= zz_loc.req):
|
||||
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)
|
||||
self.my_locations.append(loc)
|
||||
|
||||
|
@ -222,11 +222,11 @@ class ZillionWorld(World):
|
|||
if item_name in item_counts:
|
||||
count = item_counts[item_name]
|
||||
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:
|
||||
# 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.world.itempool.append(self.create_item(item_name))
|
||||
self.multiworld.itempool.append(self.create_item(item_name))
|
||||
|
||||
def set_rules(self) -> None:
|
||||
# logic for this game is in create_regions
|
||||
|
@ -237,9 +237,9 @@ class ZillionWorld(World):
|
|||
# main location name is an alias
|
||||
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"))
|
||||
self.world.completion_condition[self.player] = \
|
||||
self.multiworld.completion_condition[self.player] = \
|
||||
lambda state: state.has("Win", self.player)
|
||||
|
||||
@staticmethod
|
||||
|
@ -295,7 +295,7 @@ class ZillionWorld(World):
|
|||
empty = zz_items[4]
|
||||
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)
|
||||
for loc in self.world.get_locations():
|
||||
for loc in self.multiworld.get_locations():
|
||||
if loc.player == self.player:
|
||||
z_loc = cast(ZillionLocation, 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
|
||||
multi_items[z_loc.zz_loc.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()
|
||||
# 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.set_external_item_interface(zz_options.start_char, zz_options.max_level)
|
||||
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)
|
||||
|
||||
def generate_output(self, output_directory: str) -> None:
|
||||
|
@ -352,7 +352,7 @@ class ZillionWorld(World):
|
|||
# original_rom_bytes = self.zz_patcher.rom
|
||||
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(
|
||||
output_directory,
|
||||
|
@ -363,7 +363,7 @@ class ZillionWorld(World):
|
|||
patch = ZillionDeltaPatch(
|
||||
os.path.splitext(filename)[0] + ZillionDeltaPatch.patch_file_ending,
|
||||
player=self.player,
|
||||
player_name=self.world.player_name[self.player],
|
||||
player_name=self.multiworld.player_name[self.player],
|
||||
patched_path=filename
|
||||
)
|
||||
patch.write()
|
||||
|
@ -401,7 +401,7 @@ class ZillionWorld(World):
|
|||
# def modify_multidata(self, multidata: Dict[str, Any]) -> None:
|
||||
# """For deeper modification of server multidata."""
|
||||
# # 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
|
||||
# print(sys.getsizeof(cache))
|
||||
|
||||
|
@ -409,7 +409,7 @@ class ZillionWorld(World):
|
|||
|
||||
def create_item(self, name: str) -> Item:
|
||||
"""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]
|
||||
|
||||
if not self.id_to_zz_item:
|
||||
|
|
|
@ -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
|
||||
"""
|
||||
z_world = cs.world.worlds[p]
|
||||
z_world = cs.multiworld.worlds[p]
|
||||
my_locations = cast(List[ZillionLocation], getattr(z_world, "my_locations"))
|
||||
|
||||
_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
|
||||
"""
|
||||
# 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)
|
||||
counts = item_counts(cs, p)
|
||||
_hash += hash(counts)
|
||||
|
|
Loading…
Reference in New Issue