move ALTTP to its own world folder

This commit is contained in:
Fabian Dill 2020-10-24 05:38:56 +02:00
parent 4f8c737eec
commit 1d58f54101
36 changed files with 253 additions and 228 deletions

View File

@ -6,7 +6,7 @@ import textwrap
import sys
from AdjusterMain import adjust
from Rom import get_sprite_from_name
from worlds.alttp.Rom import get_sprite_from_name
class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):

View File

@ -3,7 +3,7 @@ import time
import logging
from Utils import output_path
from Rom import LocalRom, apply_rom_settings
from worlds.alttp.Rom import LocalRom, apply_rom_settings
def adjust(args):

View File

@ -5,16 +5,20 @@ from enum import Enum, unique
import logging
import json
from collections import OrderedDict, Counter, deque
from typing import Union, Optional, List, Set, Dict
from typing import Union, Optional, List, Dict
import secrets
import random
from EntranceShuffle import door_addresses, indirect_connections
import worlds.alttp
from worlds.alttp.EntranceShuffle import door_addresses, indirect_connections
from Utils import int16_as_bytes
from Items import item_name_groups
from worlds.alttp.Items import item_name_groups
class World(object):
class World():
pass
class MultiWorld():
debug_types = False
player_names: list
_region_cache: dict
@ -26,15 +30,7 @@ class World(object):
def __init__(self, players: int, shuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer,
progressive,
goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
if self.debug_types:
import inspect
methods = inspect.getmembers(self, predicate=inspect.ismethod)
for name, method in methods:
if name.startswith("_debug_"):
setattr(self, name[7:], method)
logging.debug(f"Set {self}.{name[7:]} to {method}")
self.get_location = self._debug_get_location
self.random = random.Random() # world-local random state is saved in case of future use a
# persistently running program with multiple worlds rolling concurrently
self.players = players
@ -131,6 +127,10 @@ class World(object):
set_player_attr('dark_room_logic', "lamp")
set_player_attr('restrict_dungeon_item_on_boss', False)
self.worlds = []
#for i in range(players):
# self.worlds.append(worlds.alttp.ALTTPWorld({}, i))
def secure(self):
self.random = secrets.SystemRandom()
@ -170,18 +170,6 @@ class World(object):
self._recache()
return self._region_cache[player][regionname]
def _debug_get_region(self, regionname: str, player: int) -> Region:
if type(regionname) != str:
raise TypeError(f"expected str, got {type(regionname)} instead")
try:
return self._region_cache[player][regionname]
except KeyError:
for region in self.regions:
if region.name == regionname and region.player == player:
assert not region.world # this should only happen before initialization
self._region_cache[player][regionname] = region
return region
raise KeyError('No such region %s for player %d' % (regionname, player))
def get_entrance(self, entrance: str, player: int) -> Entrance:
try:
@ -190,19 +178,6 @@ class World(object):
self._recache()
return self._entrance_cache[entrance, player]
def _debug_get_entrance(self, entrance: str, player: int) -> Entrance:
if type(entrance) != str:
raise TypeError(f"expected str, got {type(entrance)} instead")
try:
return self._entrance_cache[(entrance, player)]
except KeyError:
for region in self.regions:
for exit in region.exits:
if exit.name == entrance and exit.player == player:
self._entrance_cache[(entrance, player)] = exit
return exit
raise KeyError('No such entrance %s for player %d' % (entrance, player))
def get_location(self, location: str, player: int) -> Location:
try:
@ -211,19 +186,6 @@ class World(object):
self._recache()
return self._location_cache[location, player]
def _debug_get_location(self, location: str, player: int) -> Location:
if type(location) != str:
raise TypeError(f"expected str, got {type(location)} instead")
try:
return self._location_cache[(location, player)]
except KeyError:
for region in self.regions:
for r_location in region.locations:
if r_location.name == location and r_location.player == player:
self._location_cache[(location, player)] = r_location
return r_location
raise KeyError('No such location %s for player %d' % (location, player))
def get_dungeon(self, dungeonname: str, player: int) -> Dungeon:
for dungeon in self.dungeons:
@ -231,13 +193,6 @@ class World(object):
return dungeon
raise KeyError('No such dungeon %s for player %d' % (dungeonname, player))
def _debug_get_dungeon(self, dungeonname: str, player: int) -> Dungeon:
if type(dungeonname) != str:
raise TypeError(f"expected str, got {type(dungeonname)} instead")
for dungeon in self.dungeons:
if dungeon.name == dungeonname and dungeon.player == player:
return dungeon
raise KeyError('No such dungeon %s for player %d' % (dungeonname, player))
def get_all_state(self, keys=False) -> CollectionState:
ret = CollectionState(self)
@ -291,7 +246,7 @@ class World(object):
if keys:
for p in range(1, self.players + 1):
from Items import ItemFactory
from worlds.alttp.Items import ItemFactory
for item in ItemFactory(
['Small Key (Hyrule Castle)', 'Big Key (Eastern Palace)', 'Big Key (Desert Palace)',
'Small Key (Desert Palace)', 'Big Key (Tower of Hera)', 'Small Key (Tower of Hera)',
@ -432,7 +387,7 @@ class World(object):
class CollectionState(object):
def __init__(self, parent: World):
def __init__(self, parent: MultiWorld):
self.prog_items = Counter()
self.world = parent
self.reachable_regions = {player: set() for player in range(1, parent.players + 1)}
@ -1164,7 +1119,7 @@ class UpgradeShop(Shop):
class Spoiler(object):
world: World
world: MultiWorld
def __init__(self, world):
self.world = world

6
Gui.py
View File

@ -15,10 +15,10 @@ import ModuleUpdate
ModuleUpdate.update()
from AdjusterMain import adjust
from EntranceRandomizer import parse_arguments
from worlds.alttp.EntranceRandomizer import parse_arguments
from GuiUtils import ToolTips, set_icon, BackgroundTaskProgress
from Main import main, get_seed, __version__ as MWVersion
from Rom import Sprite
from worlds.alttp.Main import main, get_seed, __version__ as MWVersion
from worlds.alttp.Rom import Sprite
from Utils import is_bundled, local_path, output_path, open_file

View File

@ -33,7 +33,7 @@ from prompt_toolkit.patch_stdout import patch_stdout
from NetUtils import Endpoint
import WebUI
import Regions
from worlds.alttp import Regions
import Utils

View File

@ -24,8 +24,7 @@ import prompt_toolkit
from prompt_toolkit.patch_stdout import patch_stdout
from fuzzywuzzy import process as fuzzy_process
import Items
import Regions
from worlds.alttp import Items, Regions
import Utils
from Utils import get_item_name_from_id, get_location_name_from_address, \
ReceivedItem, _version_tuple, restricted_loads

View File

@ -11,11 +11,11 @@ import ModuleUpdate
ModuleUpdate.update()
from Utils import parse_yaml
from Rom import get_sprite_from_name
from EntranceRandomizer import parse_arguments
from Main import main as ERmain
from Main import get_seed, seeddigits
from Items import item_name_groups, item_table
from worlds.alttp.Rom import get_sprite_from_name
from worlds.alttp.EntranceRandomizer import parse_arguments
from worlds.alttp.Main import main as ERmain
from worlds.alttp.Main import get_seed, seeddigits
from worlds.alttp.Items import item_name_groups, item_table
def mystery_argparse():

View File

@ -10,8 +10,9 @@ import sys
from typing import Tuple, Optional
import Utils
from Rom import JAP10HASH
from worlds.alttp.Rom import JAP10HASH
current_patch_version = 1
def get_base_rom_path(file_name: str = "") -> str:
options = Utils.get_options()
@ -23,7 +24,7 @@ def get_base_rom_path(file_name: str = "") -> str:
def get_base_rom_bytes(file_name: str = "") -> bytes:
from Rom import read_rom
from worlds.alttp.Rom import read_rom
base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
if not base_rom_bytes:
file_name = get_base_rom_path(file_name)
@ -42,6 +43,8 @@ def generate_yaml(patch: bytes, metadata: Optional[dict] = None) -> bytes:
patch = yaml.dump({"meta": metadata,
"patch": patch,
"game": "alttp",
"compatible_version": 1, # minimum version of patch system expected for patching to be successful
"version": current_patch_version,
"base_checksum": JAP10HASH})
return patch.encode(encoding="utf-8-sig")
@ -63,6 +66,8 @@ def create_patch_file(rom_file_to_patch: str, server: str = "", destination: str
def create_rom_bytes(patch_file: str) -> Tuple[dict, str, bytearray]:
data = Utils.parse_yaml(lzma.decompress(load_bytes(patch_file)).decode("utf-8-sig"))
if data["compatible_version"] > current_patch_version:
raise RuntimeError("Patch file is incompatible with this patcher, likely an update is required.")
patched_data = bsdiff4.patch(get_base_rom_bytes(), data["patch"])
rom_hash = patched_data[int(0x7FC0):int(0x7FD5)]
data["meta"]["hash"] = "".join(chr(x) for x in rom_hash)

View File

@ -181,12 +181,12 @@ def get_options() -> dict:
def get_item_name_from_id(code):
import Items
from worlds.alttp import Items
return Items.lookup_id_to_name.get(code, f'Unknown item (ID:{code})')
def get_location_name_from_address(address):
import Regions
from worlds.alttp import Regions
return Regions.lookup_id_to_name.get(address, f'Unknown location (ID:{address})')

View File

@ -6,9 +6,9 @@ import json
from flask import request, flash, redirect, url_for, session, render_template
from EntranceRandomizer import parse_arguments
from Main import main as ERmain
from Main import get_seed, seeddigits
from worlds.alttp.EntranceRandomizer import parse_arguments
from worlds.alttp.Main import main as ERmain
from worlds.alttp.Main import get_seed, seeddigits
import pickle
from .models import *

View File

@ -3,11 +3,9 @@ import collections
from flask import render_template
from werkzeug.exceptions import abort
import datetime
import logging
from uuid import UUID
import Items
import Regions
from worlds.alttp import Items, Regions
from WebHostLib import app, cache, Room
from Utils import Hint

View File

@ -2,7 +2,7 @@ import argparse
import json
from os import listdir
from os.path import isfile, join
from Rom import Sprite
from worlds.alttp.Rom import Sprite
from Gui import get_image_for_sprite
parser = argparse.ArgumentParser(description='Dump sprite data and .png files to a directory.')

View File

@ -1,7 +1,7 @@
import unittest
from BaseClasses import CollectionState
from Items import ItemFactory
from worlds.alttp.Items import ItemFactory
class TestBase(unittest.TestCase):

View File

@ -1,18 +1,18 @@
import unittest
from BaseClasses import World, CollectionState
from Dungeons import create_dungeons, get_dungeon_item_pool
from EntranceShuffle import mandatory_connections, connect_simple
from ItemPool import difficulties, generate_itempool
from Items import ItemFactory
from Regions import create_regions, create_shops
from Rules import set_rules
from BaseClasses import MultiWorld, CollectionState
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
from worlds.alttp.EntranceShuffle import mandatory_connections, connect_simple
from worlds.alttp.ItemPool import difficulties, generate_itempool
from worlds.alttp.Items import ItemFactory
from worlds.alttp.Regions import create_regions, create_shops
from worlds.alttp.Rules import set_rules
class TestDungeon(unittest.TestCase):
def setUp(self):
self.world = World(1, {1:'vanilla'}, {1:'noglitches'}, {1:'open'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
True, {1:False}, False, None, {1:False})
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'noglitches'}, {1: 'open'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
True, {1:False}, False, None, {1:False})
self.starting_regions = [] # Where to start exploring
self.remove_exits = [] # Block dungeon exits
self.world.difficulty_requirements[1] = difficulties['normal']

View File

@ -1,18 +1,18 @@
from BaseClasses import World
from Dungeons import create_dungeons, get_dungeon_item_pool
from EntranceShuffle import link_inverted_entrances
from InvertedRegions import create_inverted_regions
from ItemPool import generate_itempool, difficulties
from Items import ItemFactory
from Regions import mark_light_world_regions, create_shops
from Rules import set_rules
from BaseClasses import MultiWorld
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
from worlds.alttp.EntranceShuffle import link_inverted_entrances
from worlds.alttp.InvertedRegions import create_inverted_regions
from worlds.alttp.ItemPool import generate_itempool, difficulties
from worlds.alttp.Items import ItemFactory
from worlds.alttp.Regions import mark_light_world_regions, create_shops
from worlds.alttp.Rules import set_rules
from test.TestBase import TestBase
class TestInverted(TestBase):
def setUp(self):
self.world = World(1, {1:'vanilla'}, {1:'noglitches'}, {1:'inverted'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
True, {1:False}, False, None, {1:False})
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'noglitches'}, {1: 'inverted'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
True, {1:False}, False, None, {1:False})
self.world.difficulty_requirements[1] = difficulties['normal']
create_inverted_regions(self.world, 1)
create_dungeons(self.world, 1)

View File

@ -1,19 +1,19 @@
import unittest
from BaseClasses import World
from Dungeons import create_dungeons
from EntranceShuffle import connect_entrance, Inverted_LW_Entrances, Inverted_LW_Dungeon_Entrances, Inverted_LW_Single_Cave_Doors, Inverted_Old_Man_Entrances, Inverted_DW_Entrances, Inverted_DW_Dungeon_Entrances, Inverted_DW_Single_Cave_Doors, \
from BaseClasses import MultiWorld
from worlds.alttp.Dungeons import create_dungeons
from worlds.alttp.EntranceShuffle import connect_entrance, Inverted_LW_Entrances, Inverted_LW_Dungeon_Entrances, Inverted_LW_Single_Cave_Doors, Inverted_Old_Man_Entrances, Inverted_DW_Entrances, Inverted_DW_Dungeon_Entrances, Inverted_DW_Single_Cave_Doors, \
Inverted_LW_Entrances_Must_Exit, Inverted_LW_Dungeon_Entrances_Must_Exit, Inverted_Bomb_Shop_Multi_Cave_Doors, Inverted_Bomb_Shop_Single_Cave_Doors, Blacksmith_Single_Cave_Doors, Inverted_Blacksmith_Multi_Cave_Doors
from InvertedRegions import create_inverted_regions
from ItemPool import difficulties
from Rules import set_inverted_big_bomb_rules
from worlds.alttp.InvertedRegions import create_inverted_regions
from worlds.alttp.ItemPool import difficulties
from worlds.alttp.Rules import set_inverted_big_bomb_rules
class TestInvertedBombRules(unittest.TestCase):
def setUp(self):
self.world = World(1, {1:'vanilla'}, {1:'noglitches'}, {1:'inverted'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
True, {1:False}, False, None, {1:False})
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'noglitches'}, {1: 'inverted'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
True, {1:False}, False, None, {1:False})
self.world.difficulty_requirements[1] = difficulties['normal']
create_inverted_regions(self.world, 1)
create_dungeons(self.world, 1)

View File

@ -1,18 +1,18 @@
from BaseClasses import World
from Dungeons import create_dungeons, get_dungeon_item_pool
from EntranceShuffle import link_inverted_entrances
from InvertedRegions import create_inverted_regions
from ItemPool import generate_itempool, difficulties
from Items import ItemFactory
from Regions import mark_light_world_regions, create_shops
from Rules import set_rules
from BaseClasses import MultiWorld
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
from worlds.alttp.EntranceShuffle import link_inverted_entrances
from worlds.alttp.InvertedRegions import create_inverted_regions
from worlds.alttp.ItemPool import generate_itempool, difficulties
from worlds.alttp.Items import ItemFactory
from worlds.alttp.Regions import mark_light_world_regions, create_shops
from worlds.alttp.Rules import set_rules
from test.TestBase import TestBase
class TestInvertedOWG(TestBase):
def setUp(self):
self.world = World(1, {1:'vanilla'}, {1:'owglitches'}, {1:'inverted'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
True, {1:False}, False, None, {1:False})
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'owglitches'}, {1: 'inverted'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
True, {1:False}, False, None, {1:False})
self.world.difficulty_requirements[1] = difficulties['normal']
create_inverted_regions(self.world, 1)
create_dungeons(self.world, 1)

View File

@ -1,4 +1,4 @@
from ItemPool import difficulties
from worlds.alttp.ItemPool import difficulties
from test.TestBase import TestBase
base_items = 41

View File

@ -1,18 +1,18 @@
from BaseClasses import World
from Dungeons import create_dungeons, get_dungeon_item_pool
from EntranceShuffle import link_entrances
from InvertedRegions import mark_dark_world_regions
from ItemPool import difficulties, generate_itempool
from Items import ItemFactory
from Regions import create_regions, create_shops
from Rules import set_rules
from BaseClasses import MultiWorld
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
from worlds.alttp.EntranceShuffle import link_entrances
from worlds.alttp.InvertedRegions import mark_dark_world_regions
from worlds.alttp.ItemPool import difficulties, generate_itempool
from worlds.alttp.Items import ItemFactory
from worlds.alttp.Regions import create_regions, create_shops
from worlds.alttp.Rules import set_rules
from test.TestBase import TestBase
class TestVanillaOWG(TestBase):
def setUp(self):
self.world = World(1, {1:'vanilla'}, {1:'owglitches'}, {1:'open'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
True, {1:False}, False, None, {1:False})
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'owglitches'}, {1: 'open'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
True, {1:False}, False, None, {1:False})
self.world.difficulty_requirements[1] = difficulties['normal']
create_regions(self.world, 1)
create_dungeons(self.world, 1)

View File

@ -1,18 +1,18 @@
from BaseClasses import World
from Dungeons import create_dungeons, get_dungeon_item_pool
from EntranceShuffle import link_entrances
from InvertedRegions import mark_dark_world_regions
from ItemPool import difficulties, generate_itempool
from Items import ItemFactory
from Regions import create_regions, create_shops
from Rules import set_rules
from BaseClasses import MultiWorld
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
from worlds.alttp.EntranceShuffle import link_entrances
from worlds.alttp.InvertedRegions import mark_dark_world_regions
from worlds.alttp.ItemPool import difficulties, generate_itempool
from worlds.alttp.Items import ItemFactory
from worlds.alttp.Regions import create_regions, create_shops
from worlds.alttp.Rules import set_rules
from test.TestBase import TestBase
class TestVanilla(TestBase):
def setUp(self):
self.world = World(1, {1:'vanilla'}, {1:'noglitches'}, {1:'open'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
True, {1:False}, False, None, {1:False})
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'noglitches'}, {1: 'open'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
True, {1:False}, False, None, {1:False})
self.world.difficulty_requirements[1] = difficulties['normal']
create_regions(self.world, 1)
create_dungeons(self.world, 1)

0
worlds/__init__.py Normal file
View File

View File

@ -1,8 +1,8 @@
from BaseClasses import Dungeon
from Bosses import BossFactory
from worlds.alttp.Bosses import BossFactory
from Fill import fill_restrictive
from Items import ItemFactory
from Regions import lookup_boss_drops
from worlds.alttp.Items import ItemFactory
from worlds.alttp.Regions import lookup_boss_drops
def create_dungeons(world, player):

View File

@ -7,8 +7,8 @@ import textwrap
import shlex
import sys
from Main import main, get_seed
from Rom import get_sprite_from_name
from worlds.alttp.Main import main, get_seed
from worlds.alttp.Rom import get_sprite_from_name
from Utils import is_bundled, close_console
@ -375,45 +375,4 @@ def parse_arguments(argv, no_defaults=False):
else:
getattr(ret, name)[player] = value
return ret
def start():
args = parse_arguments(None)
if is_bundled() and len(sys.argv) == 1:
# for the bundled builds, if we have no arguments, the user
# probably wants the gui. Users of the bundled build who want the command line
# interface shouuld specify at least one option, possibly setting a value to a
# default if they like all the defaults
from Gui import guiMain
close_console()
guiMain()
sys.exit(0)
# ToDo: Validate files further than mere existance
if not os.path.isfile(args.rom):
input(
'Could not find valid base rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom)
sys.exit(1)
if any([sprite is not None and not os.path.isfile(sprite) and not get_sprite_from_name(sprite) for sprite in
args.sprite.values()]):
input('Could not find link sprite sheet at given location. \nPress Enter to exit.')
sys.exit(1)
# set up logger
loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[args.loglevel]
logging.basicConfig(format='%(message)s', level=loglevel)
if args.gui:
from Gui import guiMain
guiMain(args)
elif args.count is not None:
seed = args.seed
for _ in range(args.count):
main(seed=seed, args=args)
seed = get_seed()
else:
main(seed=args.seed, args=args)
if __name__ == '__main__':
start()
return ret

View File

@ -1941,7 +1941,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player):
invalid_cave_connections = defaultdict(set)
if world.logic[player] in ['owglitches', 'nologic']:
import OverworldGlitchRules
from worlds.alttp import OverworldGlitchRules
for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'):
invalid_connections[entrance] = set()
if entrance in must_be_exits:

View File

@ -1,6 +1,6 @@
import collections
from BaseClasses import RegionType
from Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region
from worlds.alttp.Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region
def create_inverted_regions(world, player):

View File

@ -2,11 +2,11 @@ from collections import namedtuple
import logging
from BaseClasses import Region, RegionType, ShopType, Location, TakeAny
from Bosses import place_bosses
from Dungeons import get_dungeon_item_pool
from EntranceShuffle import connect_entrance
from worlds.alttp.Bosses import place_bosses
from worlds.alttp.Dungeons import get_dungeon_item_pool
from worlds.alttp.EntranceShuffle import connect_entrance
from Fill import FillError, fill_restrictive
from Items import ItemFactory
from worlds.alttp.Items import ItemFactory
# This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space.
# Some basic items that various modes require are placed here, including pendants and crystals. Medallion requirements for the two relevant entrances are also decided.

View File

@ -8,16 +8,16 @@ import time
import zlib
import concurrent.futures
from BaseClasses import World, CollectionState, Item, Region, Location, Shop
from Items import ItemFactory
from Regions import create_regions, create_shops, mark_light_world_regions, lookup_vanilla_location_to_entrance
from InvertedRegions import create_inverted_regions, mark_dark_world_regions
from EntranceShuffle import link_entrances, link_inverted_entrances
from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, get_hash_string
from Rules import set_rules
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
from BaseClasses import MultiWorld, CollectionState, Item, Region, Location
from worlds.alttp.Items import ItemFactory
from worlds.alttp.Regions import create_regions, create_shops, mark_light_world_regions, lookup_vanilla_location_to_entrance
from worlds.alttp.InvertedRegions import create_inverted_regions, mark_dark_world_regions
from worlds.alttp.EntranceShuffle import link_entrances, link_inverted_entrances
from worlds.alttp.Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, get_hash_string
from worlds.alttp.Rules import set_rules
from worlds.alttp.Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression
from ItemPool import generate_itempool, difficulties, fill_prizes
from worlds.alttp.ItemPool import generate_itempool, difficulties, fill_prizes
from Utils import output_path, parse_player_names, get_options, __version__, _version_tuple
import Patch
@ -39,9 +39,9 @@ def main(args, seed=None):
start = time.perf_counter()
# initialize the world
world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty,
args.item_functionality, args.timer, args.progressive.copy(), args.goal, args.algorithm,
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
world = MultiWorld(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty,
args.item_functionality, args.timer, args.progressive.copy(), args.goal, args.algorithm,
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
logger = logging.getLogger('')
world.seed = get_seed(seed)
@ -278,7 +278,7 @@ def main(args, seed=None):
# collect ER hint info
er_hint_data = {player: {} for player in range(1, world.players + 1) if world.shuffle[player] != "vanilla"}
from Regions import RegionType
from worlds.alttp.Regions import RegionType
for region in world.regions:
if region.player in er_hint_data and region.locations:
main_entrance = get_entrance_to_region(region)
@ -333,7 +333,7 @@ def main(args, seed=None):
def copy_world(world):
# ToDo: Not good yet
ret = World(world.players, world.shuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
ret = MultiWorld(world.players, world.shuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
ret.teams = world.teams
ret.player_names = copy.deepcopy(world.player_names)
ret.remote_items = world.remote_items.copy()

View File

@ -15,17 +15,17 @@ import concurrent.futures
from typing import Optional
from BaseClasses import CollectionState, ShopType, Region, Location
from Dungeons import dungeon_music_addresses
from Regions import location_table
from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable
from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
from worlds.alttp.Dungeons import dungeon_music_addresses
from worlds.alttp.Regions import location_table
from worlds.alttp.Text import MultiByteTextMapper, text_addresses, Credits, TextTable
from worlds.alttp.Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, \
from worlds.alttp.Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, \
LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, \
SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc, is_bundled
from Items import ItemFactory
from EntranceShuffle import door_addresses
from worlds.alttp.Items import ItemFactory
from worlds.alttp.EntranceShuffle import door_addresses
import Patch
try:
@ -455,7 +455,7 @@ class Sprite(object):
@staticmethod
def default_link_sprite():
return Sprite(local_path('data', 'default.zspr'))
return Sprite(local_path('../../data', 'default.zspr'))
def decode8(self, pos):
arr = [[0 for _ in range(8)] for _ in range(8)]
@ -1397,7 +1397,7 @@ def patch_rom(world, rom, player, team, enemized):
# set rom name
# 21 bytes
from Main import __version__
from worlds.alttp.Main import __version__
# TODO: Adjust Enemizer to accept AP and AD
rom.name = bytearray(f'BM{__version__.replace(".", "")[0:3]}_{team + 1}_{player}_{world.seed:09}\0', 'utf8')[:21]
rom.name.extend([0] * (21 - len(rom.name)))
@ -1544,7 +1544,7 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr
"randomize_overworld": ow_palettes == 'random'
}
if any(options.values()):
data_dir = local_path("data") if is_bundled() else None
data_dir = local_path("../../data") if is_bundled() else None
offsets_array = build_offset_collections(options, data_dir)
ColorF = z3pr.ColorF

View File

@ -1,10 +1,10 @@
import collections
import logging
import OverworldGlitchRules
from BaseClasses import RegionType, World, Entrance
from Items import ItemFactory, progression_items, item_name_groups
from OverworldGlitchRules import overworld_glitches_rules, no_logic_rules
from Bosses import GanonDefeatRule
from worlds.alttp import OverworldGlitchRules
from BaseClasses import RegionType, MultiWorld, Entrance
from worlds.alttp.Items import ItemFactory, progression_items, item_name_groups
from worlds.alttp.OverworldGlitchRules import overworld_glitches_rules, no_logic_rules
from worlds.alttp.Bosses import GanonDefeatRule
def set_rules(world, player):
@ -124,7 +124,7 @@ def add_rule(spot, rule, combine='and'):
spot.access_rule = lambda state: rule(state) and old_rule(state)
def add_lamp_requirement(world: World, spot, player: int, has_accessible_torch: bool = False):
def add_lamp_requirement(world: MultiWorld, spot, player: int, has_accessible_torch: bool = False):
if world.dark_room_logic[player] == "lamp":
add_rule(spot, lambda state: state.has('Lamp', player))
elif world.dark_room_logic[player] == "torches": # implicitly lamp as well
@ -1371,7 +1371,7 @@ def set_inverted_big_bomb_rules(world, player):
raise Exception('No logic found for routing from %s to the pyramid.' % bombshop_entrance.name)
def set_bunny_rules(world: World, player: int, inverted: bool):
def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
# regions for the exits of multi-entrance caves/drops that bunny cannot pass
# Note spiral cave and two brothers house are passable in superbunny state for glitch logic with extra requirements.
@ -1453,7 +1453,7 @@ def set_bunny_rules(world: World, player: int, inverted: bool):
if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions():
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has_Mirror(player) and state.has_sword(player))
elif (region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions()
or location is not None and location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations()):
or location is not None and location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations()):
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has_Mirror(player) and state.has_Boots(player))
elif location is not None and location.name in OverworldGlitchRules.get_superbunny_accessible_locations():
if new_region.name == 'Superbunny Cave (Bottom)' or region.name == 'Kakariko Well (top)':

109
worlds/alttp/__init__.py Normal file
View File

@ -0,0 +1,109 @@
from BaseClasses import World
class ALTTPWorld(World):
def __init__(self, options, slot: int):
self._region_cache = {}
self.slot = slot
self.shuffle = shuffle
self.logic = logic
self.mode = mode
self.swords = swords
self.difficulty = difficulty
self.difficulty_adjustments = difficulty_adjustments
self.timer = timer
self.progressive = progressive
self.goal = goal
self.dungeons = []
self.regions = []
self.shops = []
self.itempool = []
self.seed = None
self.precollected_items = []
self.state = CollectionState(self)
self._cached_entrances = None
self._cached_locations = None
self._entrance_cache = {}
self._location_cache = {}
self.required_locations = []
self.light_world_light_cone = False
self.dark_world_light_cone = False
self.rupoor_cost = 10
self.aga_randomness = True
self.lock_aga_door_in_escape = False
self.save_and_quit_from_boss = True
self.accessibility = accessibility
self.shuffle_ganon = shuffle_ganon
self.fix_gtower_exit = self.shuffle_ganon
self.retro = retro
self.custom = custom
self.customitemarray: List[int] = customitemarray
self.hints = hints
self.dynamic_regions = []
self.dynamic_locations = []
self.remote_items = False
self.required_medallions = ['Ether', 'Quake']
self.swamp_patch_required = False
self.powder_patch_required = False
self.ganon_at_pyramid = True
self.ganonstower_vanilla = True
self.can_access_trock_eyebridge = None
self.can_access_trock_front = None
self.can_access_trock_big_chest = None
self.can_access_trock_middle = None
self.fix_fake_world = True
self.mapshuffle = False
self.compassshuffle = False
self.keyshuffle = False
self.bigkeyshuffle = False
self.difficulty_requirements = None
self.boss_shuffle = 'none'
self.enemy_shuffle = False
self.enemy_health = 'default'
self.enemy_damage = 'default'
self.killable_thieves = False
self.tile_shuffle = False
self.bush_shuffle = False
self.beemizer = 0
self.escape_assist = []
self.crystals_needed_for_ganon = 7
self.crystals_needed_for_gt = 7
self.open_pyramid = False
self.treasure_hunt_icon = 'Triforce Piece'
self.treasure_hunt_count = 0
self.clock_mode = False
self.can_take_damage = True
self.glitch_boots = True
self.progression_balancing = True
self.local_items = set()
self.triforce_pieces_available = 30
self.triforce_pieces_required = 20
self.shop_shuffle = 'off'
self.shuffle_prizes = "g"
self.sprite_pool = []
self.dark_room_logic = "lamp"
self.restrict_dungeon_item_on_boss = False
@property
def sewer_light_cone(self):
return self.mode == "standard"
@property
def fix_trock_doors(self):
return self.shuffle != 'vanilla' or self.mode == 'inverted'
@property
def fix_skullwoods_exit(self):
return self.shuffle not in {'vanilla', 'simple', 'restricted', 'dungeonssimple'}
@property
def fix_palaceofdarkness_exit(self):
return self.shuffle not in {'vanilla', 'simple', 'restricted', 'dungeonssimple'}
@property
def fix_trock_exit(self):
return self.shuffle not in {'vanilla', 'simple', 'restricted', 'dungeonssimple'}

View File