move ALTTP to its own world folder
This commit is contained in:
parent
4f8c737eec
commit
1d58f54101
|
@ -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):
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
6
Gui.py
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
10
Mystery.py
10
Mystery.py
|
@ -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():
|
||||
|
|
9
Patch.py
9
Patch.py
|
@ -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)
|
||||
|
|
4
Utils.py
4
Utils.py
|
@ -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})')
|
||||
|
||||
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import unittest
|
||||
|
||||
from BaseClasses import CollectionState
|
||||
from Items import ItemFactory
|
||||
from worlds.alttp.Items import ItemFactory
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
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'},
|
||||
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
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
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'},
|
||||
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)
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
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'},
|
||||
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)
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
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'},
|
||||
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)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from ItemPool import difficulties
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from test.TestBase import TestBase
|
||||
|
||||
base_items = 41
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
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'},
|
||||
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)
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
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'},
|
||||
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)
|
||||
|
|
|
@ -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):
|
|
@ -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
|
||||
|
||||
|
||||
|
@ -376,44 +376,3 @@ def parse_arguments(argv, no_defaults=False):
|
|||
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()
|
|
@ -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:
|
|
@ -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):
|
|
@ -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.
|
|
@ -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,7 +39,7 @@ 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,
|
||||
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)
|
||||
|
||||
|
@ -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()
|
|
@ -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
|
|
@ -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.
|
|
@ -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'}
|
Loading…
Reference in New Issue